diff --git a/src/engine/autoupdate.h b/src/engine/autoupdate.h
new file mode 100644
index 000000000..355f1af7b
--- /dev/null
+++ b/src/engine/autoupdate.h
@@ -0,0 +1,27 @@
+#ifndef ENGINE_AUTOUPDATE_H
+#define ENGINE_AUTOUPDATE_H
+
+#include "kernel.h"
+
+class IAutoUpdate : public IInterface
+{
+ MACRO_INTERFACE("autoupdate", 0)
+public:
+ enum
+ {
+ IGNORED = -1,
+ CLEAN,
+ GETTING_MANIFEST,
+ GOT_MANIFEST,
+ PARSING_UPDATE,
+ DOWNLOADING,
+ NEED_RESTART,
+ };
+
+ virtual void Update() = 0;
+ virtual void InitiateUpdate() = 0;
+ virtual void IgnoreUpdate() = 0;
+
+};
+
+#endif
diff --git a/src/engine/client/autoupdate.cpp b/src/engine/client/autoupdate.cpp
new file mode 100644
index 000000000..654e72d44
--- /dev/null
+++ b/src/engine/client/autoupdate.cpp
@@ -0,0 +1,169 @@
+#include "autoupdate.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+using std::string;
+using std::vector;
+
+#if defined(CONF_FAMILY_WINDOWS)
+ #define PLAT_EXEC_NAME "DDNet.exe"
+#elif defined(CONF_FAMILY_UNIX)
+ #define PLAT_EXEC_NAME "DDNet"
+#endif
+
+CAutoUpdate::CAutoUpdate()
+{
+ m_pClient = NULL;
+ m_pStorage = NULL;
+ m_pFetcher = NULL;
+ m_State = CLEAN;
+ m_Percent = 0;
+}
+
+void CAutoUpdate::Init()
+{
+ m_pClient = Kernel()->RequestInterface();
+ m_pStorage = Kernel()->RequestInterface();
+ m_pFetcher = Kernel()->RequestInterface();
+}
+
+void CAutoUpdate::ProgressCallback(const char *pDest, void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr)
+{
+ CAutoUpdate *pUpdate = (CAutoUpdate *)pUser;
+ str_copy(pUpdate->m_Status, pDest, sizeof(m_Status));
+ pUpdate->m_Percent = (100*DlCurr)/(DlTotal ? DlTotal : 1);
+}
+
+void CAutoUpdate::CompletionCallback(const char *pDest, void *pUser)
+{
+ CAutoUpdate *pUpdate = (CAutoUpdate *)pUser;
+ if(!str_comp(pDest, "update.json")){
+ pUpdate->m_State = GOT_MANIFEST;
+ }
+ else if(!str_comp(pDest, pUpdate->m_aLastFile)){
+ if(pUpdate->m_ClientUpdate)
+ pUpdate->ReplaceExecutable();
+ pUpdate->m_State = NEED_RESTART;
+ }
+}
+
+void CAutoUpdate::FetchFile(const char *pFile, const char *pDestPath)
+{
+ char aBuf[256];
+ str_format(aBuf, sizeof(aBuf), "https://learath2.info/%s", pFile);
+ if(!pDestPath)
+ pDestPath = pFile;
+ m_pFetcher->QueueAdd(aBuf, pDestPath, &CAutoUpdate::CompletionCallback, &CAutoUpdate::ProgressCallback, this);
+}
+
+void CAutoUpdate::Update()
+{
+ switch(m_State){
+ case GOT_MANIFEST:
+ PerformUpdate();
+ default:
+ return;
+ }
+}
+
+void CAutoUpdate::AddNewFile(const char *pFile)
+{
+ for(vector::iterator it = m_AddedFiles.begin(); it < m_AddedFiles.end(); ++it)
+ if(!str_comp(it->c_str(), pFile))
+ return;
+ m_AddedFiles.push_back(string(pFile));
+}
+
+void CAutoUpdate::AddRemovedFile(const char *pFile)
+{
+ //First remove from to be downloaded list
+ for(vector::iterator it = m_AddedFiles.begin(); it < m_AddedFiles.end(); ++it){
+ if(!str_comp(it->c_str(), pFile)){
+ m_AddedFiles.erase(it);
+ break;
+ }
+ }
+ m_RemovedFiles.push_back(string(pFile));
+}
+
+void CAutoUpdate::ReplaceExecutable()
+{
+ dbg_msg("autoupdate", "Replacing" PLAT_EXEC_NAME);
+ m_pStorage->RenameFile(PLAT_EXEC_NAME, "DDNet.old", 2);
+ m_pStorage->RenameFile("DDNet.tmp", PLAT_EXEC_NAME, 2);
+}
+
+void CAutoUpdate::ParseUpdate()
+{
+ IOHANDLE File = m_pStorage->OpenFile("update.json", IOFLAG_READ, IStorage::TYPE_ALL);
+ 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++)
+ AddNewFile(json_string_get(json_array_get(pTemp, j)));
+ }
+ if((pTemp = json_object_get(pCurrent, "remove"))->type == json_array){
+ for(int j = 0; j < json_array_length(pTemp); j++)
+ AddRemovedFile(json_string_get(json_array_get(pTemp, j)));
+ }
+ }
+ else
+ break;
+ }
+ }
+ }
+}
+
+void CAutoUpdate::InitiateUpdate()
+{
+ m_State = GETTING_MANIFEST;
+ FetchFile("update.json");
+}
+
+void CAutoUpdate::IgnoreUpdate()
+{
+ m_State = IGNORED;
+}
+
+void CAutoUpdate::PerformUpdate()
+{
+ m_State = PARSING_UPDATE;
+ dbg_msg("autoupdate", "Parsing update.json");
+ ParseUpdate();
+ m_State = DOWNLOADING;
+
+ if(m_ClientUpdate)
+ str_copy(m_aLastFile, "DDNet.tmp", sizeof(m_aLastFile));
+ else
+ str_copy(m_aLastFile, m_AddedFiles.front().c_str(), sizeof(m_aLastFile));
+
+ while(!m_AddedFiles.empty()){
+ FetchFile(m_AddedFiles.back().c_str());
+ m_AddedFiles.pop_back();
+ }
+ while(!m_RemovedFiles.empty()){
+ m_pStorage->RemoveFile(m_RemovedFiles.back().c_str(), IStorage::TYPE_SAVE);
+ m_RemovedFiles.pop_back();
+ }
+ if(m_ClientUpdate)
+ FetchFile(PLAT_EXEC_NAME, "DDNet.tmp");
+}
\ No newline at end of file
diff --git a/src/engine/client/autoupdate.h b/src/engine/client/autoupdate.h
new file mode 100644
index 000000000..064657be6
--- /dev/null
+++ b/src/engine/client/autoupdate.h
@@ -0,0 +1,46 @@
+#ifndef ENGINE_CLIENT_AUTOUPDATE_H
+#define ENGINE_CLIENT_AUTOUPDATE_H
+
+#include
+#include
+#include
+
+class CAutoUpdate : public IAutoUpdate
+{
+ class IClient *m_pClient;
+ class IStorage *m_pStorage;
+ class IFetcher *m_pFetcher;
+
+ int m_State;
+ char m_Status[256];
+ int m_Percent;
+ char m_aLastFile[256];
+
+ bool m_ClientUpdate;
+ bool m_ServerUpdate;
+
+ std::vector m_AddedFiles;
+ std::vector m_RemovedFiles;
+
+ void AddNewFile(const char *pFile);
+ void AddRemovedFile(const char *pFile);
+ void FetchFile(const char *pFile, const char *pDestPath = 0);
+
+ void ParseUpdate();
+ void PerformUpdate();
+ void ReplaceExecutable();
+
+public:
+ CAutoUpdate();
+ static void ProgressCallback(const char *pDest, void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr);
+ static void CompletionCallback(const char *pDest, void *pUser);
+
+
+ virtual void InitiateUpdate();
+ void IgnoreUpdate();
+ void Init();
+ virtual void Update();
+ void Restart();
+};
+
+#endif
\ No newline at end of file
diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp
index b2667898f..4d015eefe 100644
--- a/src/engine/client/client.cpp
+++ b/src/engine/client/client.cpp
@@ -59,6 +59,7 @@
#include "friends.h"
#include "serverbrowser.h"
#include "fetcher.h"
+#include "autoupdate.h"
#include "client.h"
#include
@@ -1165,9 +1166,7 @@ void CClient::ProcessConnlessPacket(CNetChunk *pPacket)
// assume version is out of date when version-data doesn't match
if(!VersionMatch)
- {
str_copy(m_aVersionStr, aVersion, sizeof(m_aVersionStr));
- }
// request the news
CNetChunk Packet;
@@ -2474,6 +2473,7 @@ void CClient::RegisterInterfaces()
Kernel()->RegisterInterface(static_cast(&m_DemoPlayer));
Kernel()->RegisterInterface(static_cast(&m_ServerBrowser));
Kernel()->RegisterInterface(static_cast(&m_Fetcher));
+ Kernel()->RegisterInterface(static_cast(&m_AutoUpdate));
Kernel()->RegisterInterface(static_cast(&m_Friends));
}
@@ -2489,6 +2489,7 @@ void CClient::InitInterfaces()
m_pMap = Kernel()->RequestInterface();
m_pMasterServer = Kernel()->RequestInterface();
m_pFetcher = Kernel()->RequestInterface();
+ m_pAutoUpdate = Kernel()->RequestInterface();
m_pStorage = Kernel()->RequestInterface();
m_DemoEditor.Init(m_pGameClient->NetVersion(), &m_SnapshotDelta, m_pConsole, m_pStorage);
@@ -2496,6 +2497,7 @@ void CClient::InitInterfaces()
m_ServerBrowser.SetBaseInfo(&m_NetClient[2], m_pGameClient->NetVersion());
m_Fetcher.Init();
+ m_AutoUpdate.Init();
m_Friends.Init();
@@ -2632,6 +2634,8 @@ void CClient::Run()
if(Input()->Update())
break; // SDL_QUIT
+ AutoUpdate()->Update();
+
// update sound
Sound()->Update();
diff --git a/src/engine/client/client.h b/src/engine/client/client.h
index 5228b4ee7..a99930d45 100644
--- a/src/engine/client/client.h
+++ b/src/engine/client/client.h
@@ -64,6 +64,7 @@ class CClient : public IClient, public CDemoPlayer::IListner
IConsole *m_pConsole;
IStorage *m_pStorage;
IFetcher *m_pFetcher;
+ IAutoUpdate *m_pAutoUpdate;
IEngineMasterServer *m_pMasterServer;
enum
@@ -78,6 +79,7 @@ class CClient : public IClient, public CDemoPlayer::IListner
class CDemoEditor m_DemoEditor;
class CServerBrowser m_ServerBrowser;
class CFetcher m_Fetcher;
+ class CAutoUpdate m_AutoUpdate;
class CFriends m_Friends;
class CMapChecker m_MapChecker;
@@ -202,6 +204,7 @@ public:
IEngineMasterServer *MasterServer() { return m_pMasterServer; }
IStorage *Storage() { return m_pStorage; }
IFetcher *Fetcher() { return m_pFetcher; }
+ IAutoUpdate *AutoUpdate() { return m_pAutoUpdate; }
CClient();
diff --git a/src/game/client/component.h b/src/game/client/component.h
index 211ecc6c1..6a4b1ec7c 100644
--- a/src/game/client/component.h
+++ b/src/game/client/component.h
@@ -28,6 +28,7 @@ protected:
class IServerBrowser *ServerBrowser() const { return m_pClient->ServerBrowser(); }
class CLayers *Layers() const { return m_pClient->Layers(); }
class CCollision *Collision() const { return m_pClient->Collision(); }
+ class IAutoUpdate *AutoUpdate() const { return m_pClient->AutoUpdate(); }
public:
virtual ~CComponent() {}
class CGameClient *GameClient() const { return m_pClient; }
diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp
index 934f1c4f1..921e8d670 100644
--- a/src/game/client/gameclient.cpp
+++ b/src/game/client/gameclient.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
@@ -123,6 +124,7 @@ void CGameClient::OnConsoleInit()
m_pServerBrowser = Kernel()->RequestInterface();
m_pEditor = Kernel()->RequestInterface();
m_pFriends = Kernel()->RequestInterface();
+ m_pAutoUpdate = Kernel()->RequestInterface();
// setup pointers
m_pBinds = &::gs_Binds;
diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h
index 1990503e6..ad790aad1 100644
--- a/src/game/client/gameclient.h
+++ b/src/game/client/gameclient.h
@@ -95,6 +95,7 @@ class CGameClient : public IGameClient
class IServerBrowser *m_pServerBrowser;
class IEditor *m_pEditor;
class IFriends *m_pFriends;
+ class IAutoUpdate *m_pAutoUpdate;
CLayers m_Layers;
class CCollision m_Collision;
@@ -135,6 +136,7 @@ public:
class CCollision *Collision() { return &m_Collision; };
class IEditor *Editor() { return m_pEditor; }
class IFriends *Friends() { return m_pFriends; }
+ class IAutoUpdate *AutoUpdate() { return m_pAutoUpdate; }
int NetobjNumCorrections() { return m_NetObjHandler.NumObjCorrections(); }
const char *NetobjCorrectedOn() { return m_NetObjHandler.CorrectedObjOn(); }