1,179 changes: 1,148 additions & 31 deletions src/engine/client/backend_sdl.cpp

Large diffs are not rendered by default.

140 changes: 137 additions & 3 deletions src/engine/client/backend_sdl.h
Expand Up @@ -70,7 +70,7 @@ class CCommandProcessorFragment_General
void Cmd_Nop();
void Cmd_Signal(const CCommandBuffer::SCommand_Signal *pCommand);
public:
bool RunCommand(const CCommandBuffer::SCommand * pBaseCommand);
bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};

// takes care of opengl related rendering
Expand Down Expand Up @@ -114,7 +114,122 @@ class CCommandProcessorFragment_OpenGL
public:
CCommandProcessorFragment_OpenGL();

bool RunCommand(const CCommandBuffer::SCommand * pBaseCommand);
bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};

class CGLSLProgram;
class CGLSLTWProgram;
class CGLSLPrimitiveProgram;
class CGLSLQuadProgram;
class CGLSLTileProgram;
class CGLSLBorderTileProgram;
class CGLSLBorderTileLineProgram;
// takes care of opengl 3.3 related rendering
class CCommandProcessorFragment_OpenGL3_3
{
bool m_UseMultipleTextureUnits;
bool m_UsePreinitializedVertexBuffer;

struct CTexture
{
GLuint m_Tex;
GLuint m_Sampler;
int m_LastWrapMode;
int m_MemSize;
};
CTexture m_aTextures[CCommandBuffer::MAX_TEXTURES];
volatile int *m_pTextureMemoryUsage;

CGLSLPrimitiveProgram *m_pPrimitiveProgram;
CGLSLTileProgram *m_pTileProgram;
CGLSLTileProgram *m_pTileProgramTextured;
CGLSLBorderTileProgram *m_pBorderTileProgram;
CGLSLBorderTileProgram *m_pBorderTileProgramTextured;
CGLSLBorderTileLineProgram *m_pBorderTileLineProgram;
CGLSLBorderTileLineProgram *m_pBorderTileLineProgramTextured;

GLuint m_PrimitiveDrawVertexID;
GLuint m_PrimitiveDrawBufferID;
GLuint m_LastIndexBufferBound;

GLuint m_QuadDrawIndexBufferID;
unsigned int m_CurrentIndicesInBuffer;

GLint m_MaxTextureUnits;
GLint m_MaxTexSize;

int m_LastBlendMode; //avoid all possible opengl state changes
bool m_LastClipEnable;

struct STextureBound{
int m_TextureSlot;
};
std::vector<STextureBound> m_TextureSlotBoundToUnit; //the texture index generated by loadtextureraw is stored in an index calculated by max texture units

bool IsAndUpdateTextureSlotBound(int IDX, int Slot);
void DestroyTexture(int Slot);
void DestroyVisualObjects(int Index);

void AppendIndices(unsigned int NewIndicesCount);

struct SVisualObject{
SVisualObject() : m_VertArrayID(0), m_VertBufferID(0), m_LastIndexBufferBound(0), m_NumElements(0), m_IsTextured(false) {}
GLuint m_VertArrayID;
GLuint m_VertBufferID;
GLuint m_LastIndexBufferBound;
int m_NumElements; //vertices and texture coordinates
bool m_IsTextured;
};
std::vector<SVisualObject> m_VisualObjects;

CCommandBuffer::SColorf m_ClearColor;
public:
enum
{
CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_OPENGL3_3,
CMD_SHUTDOWN,
};

struct SCommand_Init : public CCommandBuffer::SCommand
{
SCommand_Init() : SCommand(CMD_INIT) {}
class IStorage *m_pStorage;
volatile int *m_pTextureMemoryUsage;
};

struct SCommand_Shutdown : public CCommandBuffer::SCommand
{
SCommand_Shutdown() : SCommand(CMD_SHUTDOWN) {}
};

private:
static int TexFormatToOpenGLFormat(int TexFormat);
static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp);
static void *Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData);

void SetState(const CCommandBuffer::SState &State, CGLSLTWProgram *pProgram);

void Cmd_Init(const SCommand_Init *pCommand);
void Cmd_Shutdown(const SCommand_Shutdown *pCommand);
void Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand);
void Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand);
void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand);
void Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand);
void Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand);
void Cmd_Screenshot(const CCommandBuffer::SCommand_Screenshot *pCommand);

void Cmd_CreateVertBuffer(const CCommandBuffer::SCommand_CreateVertexBufferObject *pCommand);
void Cmd_AppendVertBuffer(const CCommandBuffer::SCommand_AppendVertexBufferObject *pCommand);
void Cmd_CreateVertArray(const CCommandBuffer::SCommand_CreateVertexArrayObject *pCommand);
void Cmd_RenderVertexArray(const CCommandBuffer::SCommand_RenderVertexArray *pCommand);
void Cmd_DestroyVertexArray(const CCommandBuffer::SCommand_DestroyVisual *pCommand);

void Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand);
void Cmd_RenderBorderTileLine(const CCommandBuffer::SCommand_RenderBorderTileLine *pCommand);
public:
CCommandProcessorFragment_OpenGL3_3();

bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};

// takes care of sdl related commands
Expand All @@ -127,6 +242,7 @@ class CCommandProcessorFragment_SDL
enum
{
CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_SDL,
CMD_UPDATE_VIEWPORT,
CMD_SHUTDOWN,
};

Expand All @@ -137,13 +253,23 @@ class CCommandProcessorFragment_SDL
SDL_GLContext m_GLContext;
};

struct SCommand_Update_Viewport : public CCommandBuffer::SCommand
{
SCommand_Update_Viewport() : SCommand(CMD_UPDATE_VIEWPORT) {}
int m_X;
int m_Y;
int m_Width;
int m_Height;
};

struct SCommand_Shutdown : public CCommandBuffer::SCommand
{
SCommand_Shutdown() : SCommand(CMD_SHUTDOWN) {}
};

private:
void Cmd_Init(const SCommand_Init *pCommand);
void Cmd_Update_Viewport(const SCommand_Update_Viewport *pCommand);
void Cmd_Shutdown(const SCommand_Shutdown *pCommand);
void Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand);
void Cmd_VSync(const CCommandBuffer::SCommand_VSync *pCommand);
Expand All @@ -159,9 +285,13 @@ class CCommandProcessorFragment_SDL
class CCommandProcessor_SDL_OpenGL : public CGraphicsBackend_Threaded::ICommandProcessor
{
CCommandProcessorFragment_OpenGL m_OpenGL;
CCommandProcessorFragment_OpenGL3_3 m_OpenGL3_3;
CCommandProcessorFragment_SDL m_SDL;
CCommandProcessorFragment_General m_General;

bool m_UseOpenGL3_3;
public:
void UseOpenGL3_3(bool Use) { m_UseOpenGL3_3 = Use; }
virtual void RunBuffer(CCommandBuffer *pBuffer);
};

Expand All @@ -173,8 +303,10 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
ICommandProcessor *m_pProcessor;
volatile int m_TextureMemoryUsage;
int m_NumScreens;

bool m_UseOpenGL3_3;
public:
virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight);
virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, class IStorage *pStorage);
virtual int Shutdown();

virtual int MemoryUsage() const;
Expand All @@ -191,4 +323,6 @@ class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
virtual int WindowOpen();
virtual void SetWindowGrab(bool Grab);
virtual void NotifyWindow();

virtual bool IsOpenGL3_3() { return m_UseOpenGL3_3; }
};
83 changes: 39 additions & 44 deletions src/engine/client/client.cpp
Expand Up @@ -41,6 +41,7 @@
#include <engine/shared/datafile.h>
#include <engine/shared/demo.h>
#include <engine/shared/filecollection.h>
#include <engine/shared/ghost.h>
#include <engine/shared/network.h>
#include <engine/shared/packer.h>
#include <engine/shared/protocol.h>
Expand Down Expand Up @@ -478,7 +479,7 @@ void CClient::SendInput()
if(m_PredTick[g_Config.m_ClDummy] <= 0)
return;

if(m_LastDummy != g_Config.m_ClDummy)
if(m_LastDummy != (bool)g_Config.m_ClDummy)
{
m_LastDummy = g_Config.m_ClDummy;
GameClient()->OnDummySwap();
Expand Down Expand Up @@ -1505,7 +1506,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
char aEscaped[128];
str_format(aFilename, sizeof(aFilename), "%s_%08x.map", pMap, MapCrc);

Fetcher()->Escape(aEscaped, sizeof(aEscaped), aFilename);
EscapeUrl(aEscaped, sizeof(aEscaped), aFilename);
str_format(aUrl, sizeof(aUrl), "%s/", g_Config.m_ClDDNetMapDownloadUrl);

// We only trust our own custom-selected CAs for our own servers.
Expand All @@ -1517,8 +1518,8 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)

str_append(aUrl, aEscaped, sizeof(aUrl));

m_pMapdownloadTask = new CFetchTask(true, UseDDNetCA);
Fetcher()->QueueAdd(m_pMapdownloadTask, aUrl, m_aMapdownloadFilename, IStorage::TYPE_SAVE);
m_pMapdownloadTask = std::make_shared<CFetchTask>(Storage(), aUrl, m_aMapdownloadFilename, IStorage::TYPE_SAVE, UseDDNetCA, true);
Engine()->AddJob(m_pMapdownloadTask);
}
else
SendMapRequest();
Expand Down Expand Up @@ -1731,7 +1732,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)

if(CompleteSize)
{
int IntSize = CVariableInt::Decompress(m_aSnapshotIncomingData, CompleteSize, aTmpBuffer2);
int IntSize = CVariableInt::Decompress(m_aSnapshotIncomingData, CompleteSize, aTmpBuffer2, sizeof(aTmpBuffer2));

if(IntSize < 0) // failure during decompression, bail
return;
Expand Down Expand Up @@ -1991,7 +1992,7 @@ void CClient::ProcessServerPacketDummy(CNetChunk *pPacket)

if(CompleteSize)
{
int IntSize = CVariableInt::Decompress(m_aSnapshotIncomingData, CompleteSize, aTmpBuffer2);
int IntSize = CVariableInt::Decompress(m_aSnapshotIncomingData, CompleteSize, aTmpBuffer2, sizeof(aTmpBuffer2));

if(IntSize < 0) // failure during decompression, bail
return;
Expand Down Expand Up @@ -2089,7 +2090,6 @@ void CClient::ResetMapDownload()
if(m_pMapdownloadTask)
{
m_pMapdownloadTask->Abort();
delete m_pMapdownloadTask;
m_pMapdownloadTask = NULL;
}
m_MapdownloadFile = 0;
Expand Down Expand Up @@ -2135,7 +2135,6 @@ void CClient::ResetDDNetInfo()
if(m_pDDNetInfoTask)
{
m_pDDNetInfoTask->Abort();
delete m_pDDNetInfoTask;
m_pDDNetInfoTask = NULL;
}
}
Expand Down Expand Up @@ -2486,7 +2485,6 @@ void CClient::Update()
}
else if(m_pMapdownloadTask->State() == CFetchTask::STATE_ABORTED)
{
delete m_pMapdownloadTask;
m_pMapdownloadTask = NULL;
}
}
Expand All @@ -2502,7 +2500,6 @@ void CClient::Update()
}
else if(m_pDDNetInfoTask->State() == CFetchTask::STATE_ABORTED)
{
delete m_pDDNetInfoTask;
m_pDDNetInfoTask = NULL;
}
}
Expand All @@ -2529,8 +2526,9 @@ void CClient::RegisterInterfaces()
{
Kernel()->RegisterInterface(static_cast<IDemoRecorder*>(&m_DemoRecorder[RECORDER_MANUAL]), false);
Kernel()->RegisterInterface(static_cast<IDemoPlayer*>(&m_DemoPlayer), false);
Kernel()->RegisterInterface(static_cast<IGhostRecorder*>(&m_GhostRecorder), false);
Kernel()->RegisterInterface(static_cast<IGhostLoader*>(&m_GhostLoader), false);
Kernel()->RegisterInterface(static_cast<IServerBrowser*>(&m_ServerBrowser), false);
Kernel()->RegisterInterface(static_cast<IFetcher*>(&m_Fetcher), false);
#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__)
Kernel()->RegisterInterface(static_cast<IUpdater*>(&m_Updater), false);
#endif
Expand All @@ -2549,7 +2547,6 @@ void CClient::InitInterfaces()
m_pInput = Kernel()->RequestInterface<IEngineInput>();
m_pMap = Kernel()->RequestInterface<IEngineMap>();
m_pMasterServer = Kernel()->RequestInterface<IEngineMasterServer>();
m_pFetcher = Kernel()->RequestInterface<IFetcher>();
#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__)
m_pUpdater = Kernel()->RequestInterface<IUpdater>();
#endif
Expand All @@ -2559,14 +2556,17 @@ void CClient::InitInterfaces()

m_ServerBrowser.SetBaseInfo(&m_NetClient[2], m_pGameClient->NetVersion());

m_Fetcher.Init();
FetcherInit();

#if !defined(CONF_PLATFORM_MACOSX) && !defined(__ANDROID__)
m_Updater.Init();
#endif

m_Friends.Init();
m_Foes.Init(true);

m_GhostRecorder.Init();
m_GhostLoader.Init();
}

void CClient::Run()
Expand Down Expand Up @@ -2884,6 +2884,7 @@ void CClient::Run()
GameClient()->OnShutdown();
Disconnect();

delete m_pEditor;
m_pGraphics->Shutdown();

// shutdown SDL
Expand Down Expand Up @@ -3431,6 +3432,7 @@ int main(int argc, const char **argv) // ignore_convention
{
#endif
bool Silent = false;
bool RandInitFailed = false;

for(int i = 1; i < argc; i++) // ignore_convention
{
Expand All @@ -3444,14 +3446,9 @@ int main(int argc, const char **argv) // ignore_convention
}
}

#if !defined(CONF_PLATFORM_MACOSX)
dbg_enable_threaded();
#endif

if(secure_random_init() != 0)
{
dbg_msg("secure", "could not initialize secure RNG");
return -1;
RandInitFailed = true;
}

CClient *pClient = CreateClient();
Expand All @@ -3470,6 +3467,12 @@ int main(int argc, const char **argv) // ignore_convention
IEngineMap *pEngineMap = CreateEngineMap();
IEngineMasterServer *pEngineMasterServer = CreateEngineMasterServer();

if(RandInitFailed)
{
dbg_msg("secure", "could not initialize secure RNG");
return -1;
}

{
bool RegisterFail = false;

Expand All @@ -3492,7 +3495,7 @@ int main(int argc, const char **argv) // ignore_convention
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IEngineMasterServer*>(pEngineMasterServer)); // register as both
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMasterServer*>(pEngineMasterServer), false);

RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateEditor());
RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateEditor(), false);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateGameClient(), false);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage);

Expand Down Expand Up @@ -3583,53 +3586,45 @@ int main(int argc, const char **argv) // ignore_convention

// DDRace

const char* CClient::GetCurrentMap()
const char *CClient::GetCurrentMap()
{
return m_aCurrentMap;
}

int CClient::GetCurrentMapCrc()
const char *CClient::GetCurrentMapPath()
{
return m_pMap->Crc();
return m_aCurrentMapPath;
}

const char* CClient::GetCurrentMapPath()
unsigned CClient::GetMapCrc()
{
return m_aCurrentMapPath;
return m_pMap->Crc();
}

const char* CClient::RaceRecordStart(const char *pFilename)
void CClient::RaceRecord_Start(const char *pFilename)
{
char aFilename[128];
str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", m_aCurrentMap, pFilename);

if(State() != STATE_ONLINE)
dbg_msg("demorec/record", "client is not online");
if(State() != IClient::STATE_ONLINE)
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online");
else
m_DemoRecorder[RECORDER_RACE].Start(Storage(), m_pConsole, aFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File());

return m_aCurrentMap;
m_DemoRecorder[RECORDER_RACE].Start(Storage(), m_pConsole, pFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File());
}

void CClient::RaceRecordStop()
void CClient::RaceRecord_Stop()
{
if(m_DemoRecorder[RECORDER_RACE].IsRecording())
m_DemoRecorder[RECORDER_RACE].Stop();
}

bool CClient::RaceRecordIsRecording()
bool CClient::RaceRecord_IsRecording()
{
return m_DemoRecorder[RECORDER_RACE].IsRecording();
}


void CClient::RequestDDNetInfo()
{
char aUrl[256];
#if defined(CONF_FAMILY_WINDOWS)
static bool s_IsWinXP = os_compare_version(5U, 1U) <= 0;
#else
static bool s_IsWinXP = false;
#endif
static bool s_IsWinXP = os_is_winxp_or_lower();
if(s_IsWinXP)
str_copy(aUrl, "http://info.ddnet.tw/info", sizeof(aUrl));
else
Expand All @@ -3638,13 +3633,13 @@ void CClient::RequestDDNetInfo()
if(g_Config.m_BrIndicateFinished)
{
char aEscaped[128];
Fetcher()->Escape(aEscaped, sizeof(aEscaped), g_Config.m_PlayerName);
EscapeUrl(aEscaped, sizeof(aEscaped), g_Config.m_PlayerName);
str_append(aUrl, "?name=", sizeof(aUrl));
str_append(aUrl, aEscaped, sizeof(aUrl));
}

m_pDDNetInfoTask = new CFetchTask(true, /*UseDDNetCA*/ true);
Fetcher()->QueueAdd(m_pDDNetInfoTask, aUrl, "ddnet-info.json.tmp", IStorage::TYPE_SAVE);
m_pDDNetInfoTask = std::make_shared<CFetchTask>(Storage(), aUrl, "ddnet-info.json.tmp", IStorage::TYPE_SAVE, true, true);
Engine()->AddJob(m_pDDNetInfoTask);
}

int CClient::GetPredictionTime()
Expand Down
27 changes: 15 additions & 12 deletions src/engine/client/client.h
Expand Up @@ -3,6 +3,10 @@
#ifndef ENGINE_CLIENT_CLIENT_H
#define ENGINE_CLIENT_CLIENT_H

#include <memory>

#include "fetcher.h"

class CGraph
{
public:
Expand Down Expand Up @@ -61,7 +65,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
IEngineMap *m_pMap;
IConsole *m_pConsole;
IStorage *m_pStorage;
IFetcher *m_pFetcher;
IUpdater *m_pUpdater;
IEngineMasterServer *m_pMasterServer;

Expand All @@ -75,8 +78,9 @@ class CClient : public IClient, public CDemoPlayer::IListener
class CDemoPlayer m_DemoPlayer;
class CDemoRecorder m_DemoRecorder[RECORDER_MAX];
class CDemoEditor m_DemoEditor;
class CGhostRecorder m_GhostRecorder;
class CGhostLoader m_GhostLoader;
class CServerBrowser m_ServerBrowser;
class CFetcher m_Fetcher;
class CUpdater m_Updater;
class CFriends m_Friends;
class CFriends m_Foes;
Expand Down Expand Up @@ -126,7 +130,7 @@ class CClient : public IClient, public CDemoPlayer::IListener
char m_aCmdConnect[256];

// map download
CFetchTask *m_pMapdownloadTask;
std::shared_ptr<CFetchTask> m_pMapdownloadTask;
char m_aMapdownloadFilename[256];
char m_aMapdownloadName[256];
IOHANDLE m_MapdownloadFile;
Expand All @@ -135,7 +139,7 @@ class CClient : public IClient, public CDemoPlayer::IListener
int m_MapdownloadAmount;
int m_MapdownloadTotalsize;

CFetchTask *m_pDDNetInfoTask;
std::shared_ptr<CFetchTask> m_pDDNetInfoTask;

// time
CSmoothTime m_GameTime[2];
Expand Down Expand Up @@ -207,7 +211,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
IGameClient *GameClient() { return m_pGameClient; }
IEngineMasterServer *MasterServer() { return m_pMasterServer; }
IStorage *Storage() { return m_pStorage; }
IFetcher *Fetcher() { return m_pFetcher; }
IUpdater *Updater() { return m_pUpdater; }

CClient();
Expand Down Expand Up @@ -300,7 +303,6 @@ class CClient : public IClient, public CDemoPlayer::IListener
void FinishDDNetInfo();
void LoadDDNetInfo();

virtual CFetchTask *MapDownloadTask() { return m_pMapdownloadTask; }
virtual const char *MapDownloadName() { return m_aMapdownloadName; }
virtual int MapDownloadAmount() { return !m_pMapdownloadTask ? m_MapdownloadAmount : (int)m_pMapdownloadTask->Current(); }
virtual int MapDownloadTotalsize() { return !m_pMapdownloadTask ? m_MapdownloadTotalsize : (int)m_pMapdownloadTask->Size(); }
Expand Down Expand Up @@ -381,12 +383,13 @@ class CClient : public IClient, public CDemoPlayer::IListener
void GenerateTimeoutSeed();
void GenerateTimeoutCodes();

virtual const char* GetCurrentMap();
virtual int GetCurrentMapCrc();
virtual const char* GetCurrentMapPath();
virtual const char* RaceRecordStart(const char *pFilename);
virtual void RaceRecordStop();
virtual bool RaceRecordIsRecording();
const char *GetCurrentMap();
const char *GetCurrentMapPath();
unsigned GetMapCrc();

void RaceRecord_Start(const char *pFilename);
void RaceRecord_Stop();
bool RaceRecord_IsRecording();

virtual void DemoSliceBegin();
virtual void DemoSliceEnd();
Expand Down
204 changes: 76 additions & 128 deletions src/engine/client/fetcher.cpp
@@ -1,119 +1,64 @@
#include <base/system.h>
#include <engine/engine.h>
#include <engine/storage.h>
#include <engine/shared/config.h>
#include <game/version.h>
#include "fetcher.h"

CFetchTask::CFetchTask(bool canTimeout, bool useDDNetCA)
{
m_pNext = NULL;
m_CanTimeout = canTimeout;
m_UseDDNetCA = useDDNetCA;
}
#define WIN32_LEAN_AND_MEAN
#include "curl/curl.h"
#include "curl/easy.h"

CFetcher::CFetcher()
{
m_pStorage = NULL;
m_pHandle = NULL;
m_Lock = lock_create();
sphore_init(&m_Queued);
m_pFirst = NULL;
m_pLast = NULL;
m_Running = true;
}
#include "fetcher.h"

bool CFetcher::Init()
double CFetchTask::Current() const { return m_Current; }
double CFetchTask::Size() const { return m_Size; }
int CFetchTask::Progress() const { return m_Progress; }
int CFetchTask::State() const { return m_State; }
const char *CFetchTask::Dest() const { return m_aDest; }

void CFetchTask::Abort() { m_Abort = true; };

CFetchTask::CFetchTask(IStorage *pStorage, const char *pUrl, const char *pDest, int StorageType, bool UseDDNetCA, bool CanTimeout) :
m_pStorage(pStorage),
m_StorageType(StorageType),
m_UseDDNetCA(UseDDNetCA),
m_CanTimeout(CanTimeout),
m_Size(0),
m_Progress(0),
m_State(CFetchTask::STATE_QUEUED),
m_Abort(false)
{
m_pStorage = Kernel()->RequestInterface<IStorage>();
if(!curl_global_init(CURL_GLOBAL_DEFAULT) && (m_pHandle = curl_easy_init()))
return true;
return false;
str_copy(m_aUrl, pUrl, sizeof(m_aUrl));
str_copy(m_aDest, pDest, sizeof(m_aDest));
}

CFetcher::~CFetcher()
{
if(m_pThHandle)
{
m_Running = false;
sphore_signal(&m_Queued);
thread_wait(m_pThHandle);
}
lock_destroy(m_Lock);
sphore_destroy(&m_Queued);

if(m_pHandle)
curl_easy_cleanup(m_pHandle);
curl_global_cleanup();
}

void CFetcher::QueueAdd(CFetchTask *pTask, const char *pUrl, const char *pDest, int StorageType, void *pUser, COMPFUNC pfnCompCb, PROGFUNC pfnProgCb)
bool FetcherInit()
{
str_copy(pTask->m_aUrl, pUrl, sizeof(pTask->m_aUrl));
str_copy(pTask->m_aDest, pDest, sizeof(pTask->m_aDest));
pTask->m_StorageType = StorageType;
pTask->m_pfnProgressCallback = pfnProgCb;
pTask->m_pfnCompCallback = pfnCompCb;
pTask->m_pUser = pUser;
pTask->m_Size = pTask->m_Progress = 0;
pTask->m_Abort = false;

lock_wait(m_Lock);
if(!m_pThHandle)
{
m_pThHandle = thread_init(&FetcherThread, this);
}

if(!m_pFirst)
{
m_pFirst = pTask;
m_pLast = m_pFirst;
}
else
{
m_pLast->m_pNext = pTask;
m_pLast = pTask;
}
pTask->m_State = CFetchTask::STATE_QUEUED;
lock_unlock(m_Lock);
sphore_signal(&m_Queued);
return !curl_global_init(CURL_GLOBAL_DEFAULT);
}

void CFetcher::Escape(char *pBuf, size_t size, const char *pStr)
void EscapeUrl(char *pBuf, int Size, const char *pStr)
{
char *pEsc = curl_easy_escape(0, pStr, 0);
str_copy(pBuf, pEsc, size);
str_copy(pBuf, pEsc, Size);
curl_free(pEsc);
}

void CFetcher::FetcherThread(void *pUser)
void CFetchTask::Run()
{
CFetcher *pFetcher = (CFetcher *)pUser;
dbg_msg("fetcher", "thread started...");
while(pFetcher->m_Running)
CURL *pHandle = curl_easy_init();
if(!pHandle)
{
sphore_wait(&pFetcher->m_Queued);
lock_wait(pFetcher->m_Lock);
CFetchTask *pTask = pFetcher->m_pFirst;
if(pTask)
pFetcher->m_pFirst = pTask->m_pNext;
lock_unlock(pFetcher->m_Lock);
if(pTask)
{
dbg_msg("fetcher", "task got %s -> %s", pTask->m_aUrl, pTask->m_aDest);
pFetcher->FetchFile(pTask);
if(pTask->m_pfnCompCallback)
pTask->m_pfnCompCallback(pTask, pTask->m_pUser);
}
m_State = STATE_ERROR;
return;
}
}

void CFetcher::FetchFile(CFetchTask *pTask)
{
char aPath[512];
if(pTask->m_StorageType == -2)
m_pStorage->GetBinaryPath(pTask->m_aDest, aPath, sizeof(aPath));
if(m_StorageType == -2)
m_pStorage->GetBinaryPath(m_aDest, aPath, sizeof(aPath));
else
m_pStorage->GetCompletePath(pTask->m_StorageType, pTask->m_aDest, aPath, sizeof(aPath));
m_pStorage->GetCompletePath(m_StorageType, m_aDest, aPath, sizeof(aPath));

if(fs_makedir_rec_for(aPath) < 0)
dbg_msg("fetcher", "i/o error, cannot create folder for: %s", aPath);
Expand All @@ -122,73 +67,76 @@ void CFetcher::FetchFile(CFetchTask *pTask)

if(!File)
{
dbg_msg("fetcher", "i/o error, cannot open file: %s", pTask->m_aDest);
pTask->m_State = CFetchTask::STATE_ERROR;
dbg_msg("fetcher", "i/o error, cannot open file: %s", m_aDest);
m_State = CFetchTask::STATE_ERROR;
return;
}

char aErr[CURL_ERROR_SIZE];
curl_easy_setopt(m_pHandle, CURLOPT_ERRORBUFFER, aErr);
curl_easy_setopt(pHandle, CURLOPT_ERRORBUFFER, aErr);

//curl_easy_setopt(m_pHandle, CURLOPT_VERBOSE, 1L);
if(pTask->m_CanTimeout)
//curl_easy_setopt(pHandle, CURLOPT_VERBOSE, 1L);
if(m_CanTimeout)
{
curl_easy_setopt(m_pHandle, CURLOPT_CONNECTTIMEOUT_MS, (long)g_Config.m_ClHTTPConnectTimeoutMs);
curl_easy_setopt(m_pHandle, CURLOPT_LOW_SPEED_LIMIT, (long)g_Config.m_ClHTTPLowSpeedLimit);
curl_easy_setopt(m_pHandle, CURLOPT_LOW_SPEED_TIME, (long)g_Config.m_ClHTTPLowSpeedTime);
curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT_MS, (long)g_Config.m_ClHTTPConnectTimeoutMs);
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_LIMIT, (long)g_Config.m_ClHTTPLowSpeedLimit);
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_TIME, (long)g_Config.m_ClHTTPLowSpeedTime);
}
else
{
curl_easy_setopt(m_pHandle, CURLOPT_CONNECTTIMEOUT_MS, 0);
curl_easy_setopt(m_pHandle, CURLOPT_LOW_SPEED_LIMIT, 0);
curl_easy_setopt(m_pHandle, CURLOPT_LOW_SPEED_TIME, 0);
curl_easy_setopt(pHandle, CURLOPT_CONNECTTIMEOUT_MS, 0);
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_LIMIT, 0);
curl_easy_setopt(pHandle, CURLOPT_LOW_SPEED_TIME, 0);
}
curl_easy_setopt(m_pHandle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(m_pHandle, CURLOPT_MAXREDIRS, 4L);
curl_easy_setopt(m_pHandle, CURLOPT_FAILONERROR, 1L);
if(pTask->m_UseDDNetCA)
curl_easy_setopt(pHandle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(pHandle, CURLOPT_MAXREDIRS, 4L);
curl_easy_setopt(pHandle, CURLOPT_FAILONERROR, 1L);
if(m_UseDDNetCA)
{
char aCAFile[512];
m_pStorage->GetBinaryPath("data/ca-ddnet.pem", aCAFile, sizeof aCAFile);
curl_easy_setopt(m_pHandle, CURLOPT_CAINFO, aCAFile);
curl_easy_setopt(pHandle, CURLOPT_CAINFO, aCAFile);
}
curl_easy_setopt(m_pHandle, CURLOPT_URL, pTask->m_aUrl);
curl_easy_setopt(m_pHandle, CURLOPT_WRITEDATA, File);
curl_easy_setopt(m_pHandle, CURLOPT_WRITEFUNCTION, &CFetcher::WriteToFile);
curl_easy_setopt(m_pHandle, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(m_pHandle, CURLOPT_PROGRESSDATA, pTask);
curl_easy_setopt(m_pHandle, CURLOPT_PROGRESSFUNCTION, &CFetcher::ProgressCallback);
curl_easy_setopt(m_pHandle, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(m_pHandle, CURLOPT_USERAGENT, "DDNet " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")");

dbg_msg("fetcher", "downloading %s", pTask->m_aDest);
pTask->m_State = CFetchTask::STATE_RUNNING;
int ret = curl_easy_perform(m_pHandle);
curl_easy_setopt(pHandle, CURLOPT_URL, m_aUrl);
curl_easy_setopt(pHandle, CURLOPT_WRITEDATA, File);
curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this);
curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback);
curl_easy_setopt(pHandle, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(pHandle, CURLOPT_USERAGENT, "DDNet " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")");

dbg_msg("fetcher", "downloading %s", m_aDest);
m_State = CFetchTask::STATE_RUNNING;
int Ret = curl_easy_perform(pHandle);
io_close(File);
if(ret != CURLE_OK)
if(Ret != CURLE_OK)
{
dbg_msg("fetcher", "task failed. libcurl error: %s", aErr);
pTask->m_State = (ret == CURLE_ABORTED_BY_CALLBACK) ? CFetchTask::STATE_ABORTED : CFetchTask::STATE_ERROR;
m_State = (Ret == CURLE_ABORTED_BY_CALLBACK) ? CFetchTask::STATE_ABORTED : CFetchTask::STATE_ERROR;
}
else
{
dbg_msg("fetcher", "task done %s", pTask->m_aDest);
pTask->m_State = CFetchTask::STATE_DONE;
dbg_msg("fetcher", "task done %s", m_aDest);
m_State = CFetchTask::STATE_DONE;
}

curl_easy_cleanup(pHandle);

OnCompletion();
}

size_t CFetcher::WriteToFile(char *pData, size_t size, size_t nmemb, void *pFile)
size_t CFetchTask::WriteCallback(char *pData, size_t Size, size_t Number, void *pFile)
{
return io_write((IOHANDLE)pFile, pData, size*nmemb);
return io_write((IOHANDLE)pFile, pData, Size * Number);
}

int CFetcher::ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr)
int CFetchTask::ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr)
{
CFetchTask *pTask = (CFetchTask *)pUser;
pTask->m_Current = DlCurr;
pTask->m_Size = DlTotal;
pTask->m_Progress = (100 * DlCurr) / (DlTotal ? DlTotal : 1);
if(pTask->m_pfnProgressCallback)
pTask->m_pfnProgressCallback(pTask, pTask->m_pUser);
pTask->OnProgress();
return pTask->m_Abort ? -1 : 0;
}
67 changes: 44 additions & 23 deletions src/engine/client/fetcher.h
@@ -1,35 +1,56 @@
#ifndef ENGINE_CLIENT_FETCHER_H
#define ENGINE_CLIENT_FETCHER_H

#define WIN32_LEAN_AND_MEAN
#include "curl/curl.h"
#include "curl/easy.h"
#include <engine/fetcher.h>
#include <engine/shared/jobs.h>
#include <engine/storage.h>
#include <engine/kernel.h>

class CFetcher : public IFetcher
class CFetchTask : public IJob
{
private:
CURL *m_pHandle;
IStorage *m_pStorage;

void *m_pThHandle;
char m_aUrl[256];
char m_aDest[128];
int m_StorageType;
bool m_UseDDNetCA;
bool m_CanTimeout;

double m_Size;
double m_Current;
int m_Progress;
int m_State;

std::atomic<bool> m_Abort;

virtual void OnProgress() { }
virtual void OnCompletion() { }

bool m_Running;
LOCK m_Lock;
SEMAPHORE m_Queued;
CFetchTask *m_pFirst;
CFetchTask *m_pLast;
class IStorage *m_pStorage;
public:
CFetcher();
virtual bool Init();
~CFetcher();

virtual void QueueAdd(CFetchTask *pTask, const char *pUrl, const char *pDest, int StorageType = -2, void *pUser = 0, COMPFUNC pfnCompCb = 0, PROGFUNC pfnProgCb = 0);
virtual void Escape(char *pBud, size_t size, const char *pStr);
static void FetcherThread(void *pUser);
void FetchFile(CFetchTask *pTask);
static size_t WriteToFile(char *pData, size_t size, size_t nmemb, void *pFile);
static int ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr);
static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pFile);

void Run();

public:
enum
{
STATE_ERROR = -1,
STATE_QUEUED,
STATE_RUNNING,
STATE_DONE,
STATE_ABORTED,
};

CFetchTask(IStorage *pStorage, const char *pUrl, const char *pDest, int StorageType = -2, bool UseDDNetCA = false, bool CanTimeout = true);

double Current() const;
double Size() const;
int Progress() const;
int State() const;
const char *Dest() const;
void Abort();
};

bool FetcherInit();
void EscapeUrl(char *pBud, int Size, const char *pStr);
#endif
704 changes: 569 additions & 135 deletions src/engine/client/graphics_threaded.cpp

Large diffs are not rendered by default.

139 changes: 131 additions & 8 deletions src/engine/client/graphics_threaded.h
Expand Up @@ -2,6 +2,8 @@

#include <engine/graphics.h>

#include <vector>

class CCommandBuffer
{
class CBuffer
Expand Down Expand Up @@ -51,6 +53,7 @@ class CCommandBuffer
enum
{
MAX_TEXTURES=1024*4,
MAX_VERTICES=32*1024,
};

enum
Expand All @@ -59,6 +62,7 @@ class CCommandBuffer
CMDGROUP_CORE = 0, // commands that everyone has to implement
CMDGROUP_PLATFORM_OPENGL = 10000, // commands specific to a platform
CMDGROUP_PLATFORM_SDL = 20000,
CMDGROUP_PLATFORM_OPENGL3_3 = 30000,

//
CMD_NOP = CMDGROUP_CORE,
Expand All @@ -77,6 +81,15 @@ class CCommandBuffer
// rendering
CMD_CLEAR,
CMD_RENDER,

//opengl 3.3 commands
CMD_CREATE_VERTEX_ARRAY_OBJECT,//create vao
CMD_CREATE_VERTEX_BUFFER_OBJECT,//create vbo
CMD_APPEND_VERTEX_BUFFER_OBJECT,//append data to the vbo
CMD_RENDER_IBO_VERTEX_ARRAY,//render a specific amount of the index buffer from a vao
CMD_RENDER_BORDER_TILE,//render one tile multiple times
CMD_RENDER_BORDER_TILE_LINE,//render an amount of tiles multiple times
CMD_DESTROY_VISUAL,//destroy all objects used to display anything

// swap
CMD_SWAP,
Expand Down Expand Up @@ -122,15 +135,27 @@ class CCommandBuffer
WRAP_REPEAT = 0,
WRAP_CLAMP,
};

struct SPoint { float x, y, z; };
struct SPoint { float x, y; };
struct STexCoord { float u, v; };
struct SColor { float r, g, b, a; };
struct SColorf { float r, g, b, a; };

//use normalized color values
struct SColor { unsigned char r, g, b, a; };

struct SVertex
struct SVertexBase
{
SPoint m_Pos;
STexCoord m_Tex;
};

struct SVertexOld : public SVertexBase
{
SColorf m_Color;
};

struct SVertex : public SVertexBase
{
SColor m_Color;
};

Expand Down Expand Up @@ -161,7 +186,7 @@ class CCommandBuffer
struct SCommand_Clear : public SCommand
{
SCommand_Clear() : SCommand(CMD_CLEAR) {}
SColor m_Color;
SColorf m_Color;
};

struct SCommand_Signal : public SCommand
Expand All @@ -182,7 +207,84 @@ class CCommandBuffer
SState m_State;
unsigned m_PrimType;
unsigned m_PrimCount;
SVertex *m_pVertices; // you should use the command buffer data to allocate vertices for this command
SVertexBase *m_pVertices; // you should use the command buffer data to allocate vertices for this command
};

struct SCommand_CreateVertexBufferObject : public SCommand
{
SCommand_CreateVertexBufferObject() : SCommand(CMD_CREATE_VERTEX_BUFFER_OBJECT) {}
void *m_Elements; //vertices and optinally textureCoords

bool m_IsTextured;

int m_NumVertices;
int m_VisualObjectIDX;
};

struct SCommand_AppendVertexBufferObject : public SCommand
{
SCommand_AppendVertexBufferObject() : SCommand(CMD_APPEND_VERTEX_BUFFER_OBJECT) {}
void *m_Elements; //vertices and optinally textureCoords
int m_NumVertices;
int m_VisualObjectIDX;
};

struct SCommand_CreateVertexArrayObject : public SCommand
{
SCommand_CreateVertexArrayObject() : SCommand(CMD_CREATE_VERTEX_ARRAY_OBJECT) {}
int m_VisualObjectIDX;
unsigned int m_RequiredIndicesCount;
};

struct SCommand_RenderVertexArray : public SCommand
{
SCommand_RenderVertexArray() : SCommand(CMD_RENDER_IBO_VERTEX_ARRAY) {}
//even if clipping should be enabled, we will render all
SState m_State;
SColorf m_Color; //the color of the whole tilelayer -- already envelopped

//the char offset of all indices that should be rendered, and the amount of renders
char** m_pIndicesOffsets;
unsigned int *m_pDrawCount;

int m_IndicesDrawNum;
int m_VisualObjectIDX;
int m_LOD;
};

struct SCommand_RenderBorderTile : public SCommand
{
SCommand_RenderBorderTile() : SCommand(CMD_RENDER_BORDER_TILE) {}
SState m_State;
SColorf m_Color; //the color of the whole tilelayer -- already envelopped
char *m_pIndicesOffset; // you should use the command buffer data to allocate vertices for this command
unsigned int m_DrawNum;
int m_VisualObjectIDX;
int m_LOD;

float m_Offset[2];
float m_Dir[2];
int m_JumpIndex;
};

struct SCommand_RenderBorderTileLine : public SCommand
{
SCommand_RenderBorderTileLine() : SCommand(CMD_RENDER_BORDER_TILE_LINE) {}
SState m_State;
SColorf m_Color; //the color of the whole tilelayer -- already envelopped
char *m_pIndicesOffset; // you should use the command buffer data to allocate vertices for this command
unsigned int m_IndexDrawNum;
unsigned int m_DrawNum;
int m_VisualObjectIDX;
int m_LOD;

float m_Dir[2];
};

struct SCommand_DestroyVisual : public SCommand
{
SCommand_DestroyVisual() : SCommand(CMD_DESTROY_VISUAL) {}
int m_VisualObjectIDX;
};

struct SCommand_Screenshot : public SCommand
Expand Down Expand Up @@ -322,7 +424,7 @@ class IGraphicsBackend

virtual ~IGraphicsBackend() {}

virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight) = 0;
virtual int Init(const char *pName, int *Screen, int *pWidth, int *pHeight, int FsaaSamples, int Flags, int *pDesktopWidth, int *pDesktopHeight, int *pCurrentWidth, int *pCurrentHeight, class IStorage *pStorage) = 0;
virtual int Shutdown() = 0;

virtual int MemoryUsage() const = 0;
Expand All @@ -343,6 +445,8 @@ class IGraphicsBackend
virtual void RunBuffer(CCommandBuffer *pBuffer) = 0;
virtual bool IsIdle() const = 0;
virtual void WaitForIdle() = 0;

virtual bool IsOpenGL3_3() { return false; }
};

class CGraphics_Threaded : public IEngineGraphics
Expand All @@ -360,6 +464,7 @@ class CGraphics_Threaded : public IEngineGraphics

CCommandBuffer::SState m_State;
IGraphicsBackend *m_pBackend;
bool m_UseOpenGL3_3;

CCommandBuffer *m_apCommandBuffers[NUM_CMDBUFFERS];
CCommandBuffer *m_pCommandBuffer;
Expand All @@ -370,8 +475,11 @@ class CGraphics_Threaded : public IEngineGraphics
class IConsole *m_pConsole;

CCommandBuffer::SVertex m_aVertices[MAX_VERTICES];
CCommandBuffer::SVertexOld m_aVerticesOld[MAX_VERTICES];
CCommandBuffer::SVertexBase *m_pVertices;
int m_NumVertices;

CCommandBuffer::SColorf m_aColorOld[4];
CCommandBuffer::SColor m_aColor[4];
CCommandBuffer::STexCoord m_aTexture[4];

Expand All @@ -387,10 +495,13 @@ class CGraphics_Threaded : public IEngineGraphics
int m_aTextureIndices[MAX_TEXTURES];
int m_FirstFreeTexture;
int m_TextureMemoryUsage;

std::vector<int> m_VertexArrayIndices;
int m_FirstFreeVertexArrayIndex;

void FlushVertices();
void AddVertices(int Count);
void Rotate(const CCommandBuffer::SPoint &rCenter, CCommandBuffer::SVertex *pPoints, int NumPoints);
void Rotate(const CCommandBuffer::SPoint &rCenter, CCommandBuffer::SVertexBase *pPoints, int NumPoints);

void KickCommandBuffer();

Expand Down Expand Up @@ -438,6 +549,9 @@ class CGraphics_Threaded : public IEngineGraphics

virtual void SetColorVertex(const CColorVertex *pArray, int Num);
virtual void SetColor(float r, float g, float b, float a);

void SetColor(CCommandBuffer::SVertexBase *pVertex, int ColorIndex);
CCommandBuffer::SVertexBase *GetVertex(int Index);

virtual void QuadsSetSubset(float TlU, float TlV, float BrU, float BrV);
virtual void QuadsSetSubsetFree(
Expand All @@ -449,6 +563,13 @@ class CGraphics_Threaded : public IEngineGraphics
virtual void QuadsDrawFreeform(const CFreeformItem *pArray, int Num);
virtual void QuadsText(float x, float y, float Size, const char *pText);

virtual void DrawVisualObject(int VisualObjectIDX, float *pColor, char** pOffsets, unsigned int *IndicedVertexDrawNum, size_t NumIndicesOffet);
virtual void DrawBorderTile(int VisualObjectIDX, float *pColor, char *pOffset, float *Offset, float *Dir, int JumpIndex, unsigned int DrawNum);
virtual void DrawBorderTileLine(int VisualObjectIDX, float *pColor, char *pOffset, float *Dir, unsigned int IndexDrawNum, unsigned int RedrawNum);
virtual void DestroyVisual(int VisualObjectIDX);
virtual int CreateVisualObjects(float *pVertices, unsigned char *pTexCoords, int NumTiles, unsigned int NumIndicesRequired);
virtual void AppendAllVertices(float *pVertices, unsigned char *pTexCoords, int NumTiles, int VisualObjectIDX);

virtual int GetNumScreens() const;
virtual void Minimize();
virtual void Maximize();
Expand Down Expand Up @@ -481,6 +602,8 @@ class CGraphics_Threaded : public IEngineGraphics
virtual void InsertSignal(semaphore *pSemaphore);
virtual bool IsIdle();
virtual void WaitForIdle();

virtual bool IsBufferingEnabled() { return m_UseOpenGL3_3; }
};

extern IGraphicsBackend *CreateGraphicsBackend();
93 changes: 93 additions & 0 deletions src/engine/client/opengl_sl.cpp
@@ -0,0 +1,93 @@
#include "opengl_sl.h"
#include <engine/shared/linereader.h>
#include <engine/storage.h>
#include <vector>
#include <stdio.h>
#include <string>

bool CGLSL::LoadShader(IStorage *pStorage, const char *pFile, int Type)
{
if (m_IsLoaded)
return true;
IOHANDLE f = pStorage->OpenFile(pFile, IOFLAG_READ, IStorage::TYPE_ALL);

std::vector<std::string> Lines;
if (f)
{
CLineReader LineReader;
LineReader.Init(f);
char* ReadLine = NULL;
while ((ReadLine = LineReader.Get()))
{
Lines.push_back(ReadLine);
Lines.back().append("\r\n");
}
io_close(f);

const char** ShaderCode = new const char*[Lines.size()];

for (size_t i = 0; i < Lines.size(); ++i)
{
ShaderCode[i] = Lines[i].c_str();
}

GLuint shader = glCreateShader(Type);

glShaderSource(shader, Lines.size(), ShaderCode, NULL);
glCompileShader(shader);

delete[] ShaderCode;

int CompilationStatus;
glGetShaderiv(shader, GL_COMPILE_STATUS, &CompilationStatus);

if (CompilationStatus == GL_FALSE)
{
char buff[3000];

GLint maxLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);

glGetShaderInfoLog(shader, maxLength, &maxLength, buff);

dbg_msg("GLSL", "%s", buff);
glDeleteShader(shader);
return false;
}
m_Type = Type;
m_IsLoaded = true;

m_ShaderID = shader;

return true;
}
else return false;

}

void CGLSL::DeleteShader()
{
if (!IsLoaded()) return;
m_IsLoaded = false;
glDeleteShader(m_ShaderID);
}

bool CGLSL::IsLoaded()
{
return m_IsLoaded;
}

GLuint CGLSL::GetShaderID()
{
return m_ShaderID;
}

CGLSL::CGLSL()
{
m_IsLoaded = false;
}

CGLSL::~CGLSL()
{
DeleteShader();
}
19 changes: 19 additions & 0 deletions src/engine/client/opengl_sl.h
@@ -0,0 +1,19 @@
#pragma once

#include "engine/external/glew/GL/glew.h"

class CGLSL {
public:
bool LoadShader(class IStorage *pStorage, const char *pFile, int Type);
void DeleteShader();

bool IsLoaded();
GLuint GetShaderID();

CGLSL();
virtual ~CGLSL();
private:
GLuint m_ShaderID;
int m_Type;
bool m_IsLoaded;
};
133 changes: 133 additions & 0 deletions src/engine/client/opengl_sl_program.cpp
@@ -0,0 +1,133 @@
#include "opengl_sl_program.h"
#include "opengl_sl.h"
#include <base/system.h>

void CGLSLProgram::CreateProgram()
{
m_ProgramID = glCreateProgram();
}

void CGLSLProgram::DeleteProgram()
{
if (!m_IsLinked) return;
m_IsLinked = false;
glDeleteProgram(m_ProgramID);
}

bool CGLSLProgram::AddShader(CGLSL* pShader)
{
if (pShader->IsLoaded())
{
glAttachShader(m_ProgramID, pShader->GetShaderID());
return true;
}
return false;
}

void CGLSLProgram::DetachShader(CGLSL* pShader)
{
if (pShader->IsLoaded())
{
DetachShaderByID(pShader->GetShaderID());
}
}

void CGLSLProgram::DetachShaderByID(GLuint ShaderID)
{
glDetachShader(m_ProgramID, ShaderID);
}

void CGLSLProgram::LinkProgram()
{
glLinkProgram(m_ProgramID);
int LinkStatus;
glGetProgramiv(m_ProgramID, GL_LINK_STATUS, &LinkStatus);
m_IsLinked = LinkStatus == GL_TRUE;
if (!m_IsLinked)
{
char sInfoLog[1024];
char sFinalMessage[1536];
int iLogLength;
glGetProgramInfoLog(m_ProgramID, 1024, &iLogLength, sInfoLog);
str_format(sFinalMessage, 1536, "Error! Shader program wasn't linked! The linker returned:\n\n%s", sInfoLog);
dbg_msg("GLSL Program", "%s", sFinalMessage);
}

//detach all shaders attached to this program
DetachAllShaders();
}

void CGLSLProgram::DetachAllShaders()
{
GLuint aShaders[100];
GLsizei ReturnedCount = 0;
while(1)
{
glGetAttachedShaders(m_ProgramID, 100, &ReturnedCount, aShaders);

if(ReturnedCount > 0)
{
for(GLsizei i = 0; i < ReturnedCount; ++i)
{
DetachShaderByID(aShaders[i]);
}
}

if(ReturnedCount < 100) break;
}
}

void CGLSLProgram::SetUniformVec4(int Loc, int Count, const float* Value)
{
glUniform4fv(Loc, Count, Value);
}

void CGLSLProgram::SetUniformVec2(int Loc, int Count, const float* Value)
{
glUniform2fv(Loc, Count, Value);
}

void CGLSLProgram::SetUniform(int Loc, const int Value)
{
glUniform1i(Loc, Value);
}

void CGLSLProgram::SetUniform(int Loc, const unsigned int Value)
{
glUniform1ui(Loc, Value);
}

void CGLSLProgram::SetUniform(int Loc, const float Value)
{
glUniform1f(Loc, Value);
}

void CGLSLProgram::SetUniform(int Loc, const bool Value)
{
glUniform1i(Loc, (int)Value);
}

int CGLSLProgram::GetUniformLoc(const char* Name)
{
return glGetUniformLocation(m_ProgramID, Name);
}

void CGLSLProgram::UseProgram()
{
if(m_IsLinked) glUseProgram(m_ProgramID);
}

GLuint CGLSLProgram::GetProgramID()
{
return m_ProgramID;
}

CGLSLProgram::CGLSLProgram()
{
m_IsLinked = false;
}

CGLSLProgram::~CGLSLProgram()
{
DeleteProgram();
}
76 changes: 76 additions & 0 deletions src/engine/client/opengl_sl_program.h
@@ -0,0 +1,76 @@
#pragma once

#include "engine/external/glew/GL/glew.h"

class CGLSL;

class CGLSLProgram {
public:
void CreateProgram();
void DeleteProgram();

bool AddShader(CGLSL* pShader);

void LinkProgram();
void UseProgram();
GLuint GetProgramID();

void DetachShader(CGLSL* pShader);
void DetachShaderByID(GLuint ShaderID);
void DetachAllShaders();

//Support various types
void SetUniformVec2(int Loc, int Count, const float* Value);
void SetUniformVec4(int Loc, int Count, const float* Value);
void SetUniform(int Loc, const int Value);
void SetUniform(int Loc, const unsigned int Value);
void SetUniform(int Loc, const bool Value);
void SetUniform(int Loc, const float Value);

//for performance reason we do not use SetUniform with using strings... save the Locations of the variables instead
int GetUniformLoc(const char* Name);

CGLSLProgram();
virtual ~CGLSLProgram();

protected:
GLuint m_ProgramID;
bool m_IsLinked;
};

class CGLSLTWProgram : public CGLSLProgram {
public:
int m_LocPos;
int m_LocIsTextured;
int m_LocTextureSampler;
};

class CGLSLQuadProgram : public CGLSLTWProgram {
public:

};

class CGLSLPrimitiveProgram : public CGLSLTWProgram {
public:

};

class CGLSLTileProgram : public CGLSLTWProgram {
public:
int m_LocColor;
int m_LocLOD;
};

class CGLSLBorderTileProgram : public CGLSLTileProgram {
public:
int m_LocOffset;
int m_LocDir;
int m_LocNum;
int m_LocJumpIndex;
};

class CGLSLBorderTileLineProgram : public CGLSLTileProgram {
public:
int m_LocDir;
int m_LocNum;
};
94 changes: 64 additions & 30 deletions src/engine/client/serverbrowser.cpp
Expand Up @@ -5,6 +5,7 @@
#include <base/math.h>
#include <base/system.h>

#include <engine/external/md5/md5.h>
#include <engine/shared/config.h>
#include <engine/shared/memheap.h>
#include <engine/shared/network.h>
Expand Down Expand Up @@ -56,12 +57,10 @@ CServerBrowser::CServerBrowser()
m_aFilterString[0] = 0;
m_aFilterGametypeString[0] = 0;

// the token is to keep server refresh separated from each other
m_CurrentToken = 1;

m_ServerlistType = 0;
m_BroadcastTime = 0;
m_BroadcastExtraToken = -1;
secure_random_fill(m_aTokenSeed, sizeof(m_aTokenSeed));
m_RequestNumber = 0;

m_pDDNetInfo = 0;
}
Expand Down Expand Up @@ -91,6 +90,28 @@ const CServerInfo *CServerBrowser::SortedGet(int Index) const
return &m_ppServerlist[m_pSortedServerlist[Index]]->m_Info;
}

int CServerBrowser::GenerateToken(const NETADDR &Addr) const
{
md5_state_t Md5;
md5_byte_t aDigest[16];

md5_init(&Md5);
md5_append(&Md5, m_aTokenSeed, sizeof(m_aTokenSeed));
md5_append(&Md5, (unsigned char *)&Addr, sizeof(Addr));
md5_finish(&Md5, aDigest);

return (aDigest[0] << 16) | (aDigest[1] << 8) | aDigest[2];
}

int CServerBrowser::GetBasicToken(int Token)
{
return Token & 0xff;
}

int CServerBrowser::GetExtraToken(int Token)
{
return Token >> 8;
}

bool CServerBrowser::SortCompareName(int Index1, int Index2) const
{
Expand Down Expand Up @@ -424,7 +445,6 @@ CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)

// set the info
pEntry->m_Addr = Addr;
pEntry->m_ExtraToken = secure_rand() & 0xffff;
pEntry->m_Info.m_NetAddr = Addr;

pEntry->m_Info.m_Latency = 999;
Expand Down Expand Up @@ -499,19 +519,26 @@ void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServer
}
else if(Type == IServerBrowser::SET_TOKEN)
{
int CheckToken = Token;
int BasicToken = Token;
int ExtraToken = 0;
if(pInfo->m_Type == SERVERINFO_EXTENDED)
{
CheckToken = Token & 0xff;
BasicToken = Token & 0xff;
ExtraToken = Token >> 8;
}

if(CheckToken != m_CurrentToken)
return;

pEntry = Find(Addr);
if(pEntry && pInfo->m_Type == SERVERINFO_EXTENDED)
if(m_ServerlistType != IServerBrowser::TYPE_LAN)
{
if(((Token & 0xffff00) >> 8) != pEntry->m_ExtraToken)
if(!pEntry)
{
return;
}
int Token = GenerateToken(Addr);
bool Drop = false;
Drop = Drop || BasicToken != GetBasicToken(Token);
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(Token));
if(Drop)
{
return;
}
Expand All @@ -520,9 +547,16 @@ void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServer
pEntry = Add(Addr);
if(pEntry)
{
if(m_ServerlistType == IServerBrowser::TYPE_LAN && pInfo->m_Type == SERVERINFO_EXTENDED)
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
{
if(((Token & 0xffff00) >> 8) != m_BroadcastExtraToken)
NETADDR Broadcast;
mem_zero(&Broadcast, sizeof(Broadcast));
Broadcast.type = m_pNetClient->NetType()|NETTYPE_LINK_BROADCAST;
int Token = GenerateToken(Broadcast);
bool Drop = false;
Drop = Drop || BasicToken != GetBasicToken(Token);
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(Token));
if(Drop)
{
return;
}
Expand Down Expand Up @@ -553,8 +587,7 @@ void CServerBrowser::Refresh(int Type)
m_pLastReqServer = 0;
m_NumRequests = 0;
m_CurrentMaxRequests = g_Config.m_BrMaxRequests;
// next token
m_CurrentToken = (m_CurrentToken+1)&0xff;
m_RequestNumber++;

m_ServerlistType = Type;

Expand All @@ -564,9 +597,6 @@ void CServerBrowser::Refresh(int Type)
CNetChunk Packet;
int i;

mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken;

/* do the broadcast version */
Packet.m_ClientID = -1;
mem_zero(&Packet, sizeof(Packet));
Expand All @@ -575,9 +605,14 @@ void CServerBrowser::Refresh(int Type)
Packet.m_DataSize = sizeof(Buffer);
Packet.m_pData = Buffer;
mem_zero(&Packet.m_aExtraData, sizeof(Packet.m_aExtraData));
m_BroadcastExtraToken = rand() & 0xffff;
Packet.m_aExtraData[0] = m_BroadcastExtraToken >> 8;
Packet.m_aExtraData[1] = m_BroadcastExtraToken & 0xff;

int Token = GenerateToken(Packet.m_Address);
mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
Buffer[sizeof(SERVERBROWSE_GETINFO)] = GetBasicToken(Token);

Packet.m_aExtraData[0] = GetExtraToken(Token) >> 8;
Packet.m_aExtraData[1] = GetExtraToken(Token) & 0xff;

m_BroadcastTime = time_get();

for(i = 8303; i <= 8310; i++)
Expand Down Expand Up @@ -633,20 +668,19 @@ void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) cons
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
}

int Token = GenerateToken(Addr);

mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken;
Buffer[sizeof(SERVERBROWSE_GETINFO)] = GetBasicToken(Token);

Packet.m_ClientID = -1;
Packet.m_Address = Addr;
Packet.m_Flags = NETSENDFLAG_CONNLESS|NETSENDFLAG_EXTENDED;
Packet.m_DataSize = sizeof(Buffer);
Packet.m_pData = Buffer;
mem_zero(&Packet.m_aExtraData, sizeof(Packet.m_aExtraData));
if(pEntry)
{
Packet.m_aExtraData[0] = pEntry->m_ExtraToken >> 8;
Packet.m_aExtraData[1] = pEntry->m_ExtraToken & 0xff;
}
Packet.m_aExtraData[0] = GetExtraToken(Token) >> 8;
Packet.m_aExtraData[1] = GetExtraToken(Token) & 0xff;

m_pNetClient->Send(&Packet);

Expand All @@ -669,7 +703,7 @@ void CServerBrowser::RequestImpl64(const NETADDR &Addr, CServerEntry *pEntry) co
}

mem_copy(Buffer, SERVERBROWSE_GETINFO_64_LEGACY, sizeof(SERVERBROWSE_GETINFO_64_LEGACY));
Buffer[sizeof(SERVERBROWSE_GETINFO_64_LEGACY)] = m_CurrentToken;
Buffer[sizeof(SERVERBROWSE_GETINFO_64_LEGACY)] = GetBasicToken(GenerateToken(Addr));

Packet.m_ClientID = -1;
Packet.m_Address = Addr;
Expand Down Expand Up @@ -1072,7 +1106,7 @@ void CServerBrowser::LoadDDNetInfoJson()

mem_free(pBuf);

if(m_pDDNetInfo->type != json_object)
if(m_pDDNetInfo && m_pDDNetInfo->type != json_object)
{
json_value_free(m_pDDNetInfo);
m_pDDNetInfo = 0;
Expand Down
13 changes: 7 additions & 6 deletions src/engine/client/serverbrowser.h
Expand Up @@ -17,7 +17,6 @@ class CServerBrowser : public IServerBrowser
int64 m_RequestTime;
int m_GotInfo;
bool m_Request64Legacy;
int m_ExtraToken;
CServerInfo m_Info;

CServerEntry *m_pNextIp; // ip hashed list
Expand Down Expand Up @@ -151,14 +150,16 @@ class CServerBrowser : public IServerBrowser
char m_aFilterString[64];
char m_aFilterGametypeString[128];

// the token is to keep server refresh separated from each other
int m_CurrentToken;

int m_ServerlistType;
int64 m_BroadcastTime;
int m_BroadcastExtraToken;
int m_RequestNumber;
unsigned char m_aTokenSeed[16];

int GenerateToken(const NETADDR &Addr) const;
static int GetBasicToken(int Token);
static int GetExtraToken(int Token);

// sorting criterions
// sorting criteria
bool SortCompareName(int Index1, int Index2) const;
bool SortCompareMap(int Index1, int Index2) const;
bool SortComparePing(int Index1, int Index2) const;
Expand Down
123 changes: 119 additions & 4 deletions src/engine/client/text.cpp
Expand Up @@ -573,7 +573,7 @@ class CTextRender : public IEngineTextRender
int ActualX, ActualY;

int ActualSize;
int i;
int i = 0;
int GotNewLine = 0;
float DrawX = 0.0f, DrawY = 0.0f;
int LineCount = 0;
Expand Down Expand Up @@ -613,9 +613,8 @@ class CTextRender : public IEngineTextRender
Length = str_length(pText);

// if we don't want to render, we can just skip the first outline pass
i = 1;
if(pCursor->m_Flags&TEXTFLAG_RENDER)
i = 0;
if(!(pCursor->m_Flags&TEXTFLAG_RENDER))
i = 1;

for(;i < 2; i++)
{
Expand Down Expand Up @@ -745,6 +744,122 @@ class CTextRender : public IEngineTextRender
pCursor->m_Y = DrawY;
}

virtual void UploadText(int TextureID, const char *pText, int Length, float x, float y, int Size, int MaxWidth, int MaxSize = -1, int MinSize = -1)
{
CFont *pFont = m_pDefaultFont;
FT_Bitmap *pBitmap;

if(!pFont)
return;

// set length
if(Length < 0)
Length = str_length(pText);

const char *pCurrent = (char *)pText;
const char *pEnd = pCurrent+Length;

int WidthLastChars = 0;

int FontSize = Size;

//adjust font size by the full space
if(Size == -1)
{
bool FoundMaxFontSize = false;
if(MinSize == -1)
MinSize = 8;
FontSize = MinSize;

while(!FoundMaxFontSize)
{
int WidthOfText = 0;

while(pCurrent < pEnd)
{
const char *pTmp = pCurrent;
int NextCharacter = str_utf8_decode(&pTmp);

if(NextCharacter)
{
FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, FontSize);
if(FT_Load_Char(pFont->m_FtFace, NextCharacter, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP))
{
dbg_msg("pFont", "error loading glyph %d", NextCharacter);
pCurrent = pTmp;
continue;
}
pBitmap = &pFont->m_FtFace->glyph->bitmap;

WidthOfText += pBitmap->width + 1;
}
pCurrent = pTmp;
}
if(WidthOfText > MaxWidth || (MaxSize != -1 && FontSize > MaxSize))
FoundMaxFontSize = true;

pCurrent = (char *)pText;
pEnd = pCurrent+Length;
++FontSize;
}
}


while(pCurrent < pEnd)
{
const char *pTmp = pCurrent;
int NextCharacter = str_utf8_decode(&pTmp);

if(NextCharacter)
{
unsigned int px, py;

FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, FontSize-1);

if(FT_Load_Char(pFont->m_FtFace, NextCharacter, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP))
{
dbg_msg("pFont", "error loading glyph %d", NextCharacter);
pCurrent = pTmp;
continue;
}

pBitmap = &pFont->m_FtFace->glyph->bitmap; // ignore_convention

int MaxSize = (MaxWidth - WidthLastChars);
if (MaxSize > 0)
{
int SlotW = ((((unsigned int)MaxSize) < (unsigned int)pBitmap->width) ? MaxSize : pBitmap->width);
int SlotH = pBitmap->rows;
int SlotSize = SlotW*SlotH;

// prepare glyph data
mem_zero(ms_aGlyphData, SlotSize);

if(pBitmap->pixel_mode == FT_PIXEL_MODE_GRAY) // ignore_convention
{
for (py = 0; py < (unsigned)SlotH; py++) // ignore_convention
for (px = 0; px < (unsigned)SlotW; px++)
{
ms_aGlyphData[(py)*SlotW + px] = pBitmap->buffer[py*pBitmap->width + px]; // ignore_convention
}
}
/*else if(pBitmap->pixel_mode == FT_PIXEL_MODE_MONO) // ignore_convention
{
for(py = 0; py < (unsigned)pBitmap->rows; py++) // ignore_convention
for(px = 0; px < (unsigned)pBitmap->width; px++) // ignore_convention
{
if(pBitmap->buffer[py*pBitmap->pitch+px/8]&(1<<(7-(px%8)))) // ignore_convention
ms_aGlyphData[(py)*SlotW+px] = 255;
}
}*/

Graphics()->LoadTextureRawSub(TextureID, x + WidthLastChars, y, SlotW, SlotH, CImageInfo::FORMAT_ALPHA, ms_aGlyphData);
WidthLastChars += (SlotW + 1);
}
}
pCurrent = pTmp;
}
}
};

IEngineTextRender *CreateEngineTextRender() { return new CTextRender; }
152 changes: 106 additions & 46 deletions src/engine/client/updater.cpp
@@ -1,6 +1,6 @@
#include "updater.h"
#include <base/system.h>
#include <engine/fetcher.h>
#include <engine/engine.h>
#include <engine/storage.h>
#include <engine/client.h>
#include <engine/external/json-parser/json.h>
Expand All @@ -11,68 +11,128 @@
using std::string;
using std::map;

CUpdater::CUpdater()
class CUpdaterFetchTask : public CFetchTask
{
m_pClient = NULL;
m_pStorage = NULL;
m_pFetcher = NULL;
m_State = CLEAN;
m_Percent = 0;
char m_aBuf[256];
char m_aBuf2[256];
CUpdater *m_pUpdater;

void OnCompletion();
void OnProgress();

public:
CUpdaterFetchTask(CUpdater *pUpdater, const char *pFile, const char *pDestPath);
};

static const char *GetUpdaterUrl(char *pBuf, int BufSize, const char *pFile)
{
str_format(pBuf, BufSize, "https://update4.ddnet.tw/%s", pFile);
return pBuf;
}

void CUpdater::Init()
static const char *GetUpdaterDestPath(char *pBuf, int BufSize, const char *pFile, const char *pDestPath)
{
if(!pDestPath)
{
pDestPath = pFile;
}
str_format(pBuf, BufSize, "update/%s", pDestPath);
return pBuf;
}

CUpdaterFetchTask::CUpdaterFetchTask(CUpdater *pUpdater, const char *pFile, const char *pDestPath) :
CFetchTask(pUpdater->m_pStorage, GetUpdaterUrl(m_aBuf, sizeof(m_aBuf), pFile), GetUpdaterDestPath(m_aBuf2, sizeof(m_aBuf), pFile, pDestPath), -2, true, false),
m_pUpdater(pUpdater)
{
m_pClient = Kernel()->RequestInterface<IClient>();
m_pStorage = Kernel()->RequestInterface<IStorage>();
m_pFetcher = Kernel()->RequestInterface<IFetcher>();
#if defined(CONF_FAMILY_WINDOWS)
m_IsWinXP = os_compare_version(5U, 1U) <= 0;
#else
m_IsWinXP = false;
#endif
}

void CUpdater::ProgressCallback(CFetchTask *pTask, void *pUser)
void CUpdaterFetchTask::OnProgress()
{
CUpdater *pUpdate = (CUpdater *)pUser;
str_copy(pUpdate->m_Status, pTask->Dest(), sizeof(pUpdate->m_Status));
pUpdate->m_Percent = pTask->Progress();
lock_wait(m_pUpdater->m_Lock);
str_copy(m_pUpdater->m_aStatus, Dest(), sizeof(m_pUpdater->m_aStatus));
m_pUpdater->m_Percent = Progress();
lock_unlock(m_pUpdater->m_Lock);
}

void CUpdater::CompletionCallback(CFetchTask *pTask, void *pUser)
void CUpdaterFetchTask::OnCompletion()
{
CUpdater *pUpdate = (CUpdater *)pUser;
const char *b = 0;
for(const char *a = pTask->Dest(); *a; a++)
for(const char *a = Dest(); *a; a++)
if(*a == '/')
b = a + 1;
b = b ? b : pTask->Dest();
b = b ? b : Dest();
if(!str_comp(b, "update.json"))
{
if(pTask->State() == CFetchTask::STATE_DONE)
pUpdate->m_State = GOT_MANIFEST;
else if(pTask->State() == CFetchTask::STATE_ERROR)
pUpdate->m_State = FAIL;
if(State() == CFetchTask::STATE_DONE)
m_pUpdater->SetCurrentState(IUpdater::GOT_MANIFEST);
else if(State() == CFetchTask::STATE_ERROR)
m_pUpdater->SetCurrentState(IUpdater::FAIL);
}
else if(!str_comp(b, pUpdate->m_aLastFile))
else if(!str_comp(b, m_pUpdater->m_aLastFile))
{
if(pTask->State() == CFetchTask::STATE_DONE)
pUpdate->m_State = MOVE_FILES;
else if(pTask->State() == CFetchTask::STATE_ERROR)
pUpdate->m_State = FAIL;
if(State() == CFetchTask::STATE_DONE)
m_pUpdater->SetCurrentState(IUpdater::MOVE_FILES);
else if(State() == CFetchTask::STATE_ERROR)
m_pUpdater->SetCurrentState(IUpdater::FAIL);
}
delete pTask;
}

CUpdater::CUpdater()
{
m_pClient = NULL;
m_pStorage = NULL;
m_pEngine = NULL;
m_State = CLEAN;
m_Percent = 0;
m_Lock = lock_create();
}

void CUpdater::Init()
{
m_pClient = Kernel()->RequestInterface<IClient>();
m_pStorage = Kernel()->RequestInterface<IStorage>();
m_pEngine = Kernel()->RequestInterface<IEngine>();
m_IsWinXP = os_is_winxp_or_lower();
}

CUpdater::~CUpdater()
{
lock_destroy(m_Lock);
}

void CUpdater::SetCurrentState(int NewState)
{
lock_wait(m_Lock);
m_State = NewState;
lock_unlock(m_Lock);
}

int CUpdater::GetCurrentState()
{
lock_wait(m_Lock);
int Result = m_State;
lock_unlock(m_Lock);
return Result;
}

void CUpdater::GetCurrentFile(char *pBuf, int BufSize)
{
lock_wait(m_Lock);
str_copy(pBuf, m_aStatus, BufSize);
lock_unlock(m_Lock);
}

int CUpdater::GetCurrentPercent()
{
lock_wait(m_Lock);
int Result = m_Percent;
lock_unlock(m_Lock);
return Result;
}

void CUpdater::FetchFile(const char *pFile, const char *pDestPath)
{
char aBuf[256], aPath[256];
str_format(aBuf, sizeof(aBuf), "https://update4.ddnet.tw/%s", pFile);
if(!pDestPath)
pDestPath = pFile;
str_format(aPath, sizeof(aPath), "update/%s", pDestPath);
CFetchTask *Task = new CFetchTask(false, true);
m_pFetcher->QueueAdd(Task, aBuf, aPath, -2, this, &CUpdater::CompletionCallback, &CUpdater::ProgressCallback);
m_pEngine->AddJob(std::make_shared<CUpdaterFetchTask>(this, pFile, pDestPath));
}

void CUpdater::MoveFile(const char *pFile)
Expand All @@ -98,27 +158,27 @@ void CUpdater::Update()
{
switch(m_State)
{
case GOT_MANIFEST:
case IUpdater::GOT_MANIFEST:
PerformUpdate();
break;
case MOVE_FILES:
case IUpdater::MOVE_FILES:
CommitUpdate();
break;
default:
return;
}
}

void CUpdater::AddFileJob(const char *pFile, bool job)
void CUpdater::AddFileJob(const char *pFile, bool Job)
{
m_FileJobs[string(pFile)] = job;
m_FileJobs[string(pFile)] = Job;
}

void CUpdater::ReplaceClient()
{
dbg_msg("updater", "replacing " PLAT_CLIENT_EXEC);

//Replace running executable by renaming twice...
// Replace running executable by renaming twice...
if(!m_IsWinXP)
{
m_pStorage->RemoveBinaryFile("DDNet.old");
Expand Down
23 changes: 14 additions & 9 deletions src/engine/client/updater.h
Expand Up @@ -2,7 +2,7 @@
#define ENGINE_CLIENT_UPDATER_H

#include <engine/updater.h>
#include <engine/fetcher.h>
#include "fetcher.h"
#include <map>
#include <string>

Expand Down Expand Up @@ -34,14 +34,18 @@

class CUpdater : public IUpdater
{
friend class CUpdaterFetchTask;

class IClient *m_pClient;
class IStorage *m_pStorage;
class IFetcher *m_pFetcher;
class IEngine *m_pEngine;

bool m_IsWinXP;

LOCK m_Lock;

int m_State;
char m_Status[256];
char m_aStatus[256];
int m_Percent;
char m_aLastFile[256];

Expand All @@ -50,7 +54,7 @@ class CUpdater : public IUpdater

std::map<std::string, bool> m_FileJobs;

void AddFileJob(const char *pFile, bool job);
void AddFileJob(const char *pFile, bool Job);
void FetchFile(const char *pFile, const char *pDestPath = 0);
void MoveFile(const char *pFile);

Expand All @@ -61,14 +65,15 @@ class CUpdater : public IUpdater
void ReplaceClient();
void ReplaceServer();

void SetCurrentState(int NewState);

public:
CUpdater();
static void ProgressCallback(CFetchTask *pTask, void *pUser);
static void CompletionCallback(CFetchTask *pTask, void *pUser);
~CUpdater();

int GetCurrentState() { return m_State; };
char *GetCurrentFile() { return m_Status; };
int GetCurrentPercent() { return m_Percent; };
int GetCurrentState();
void GetCurrentFile(char *pBuf, int BufSize);
int GetCurrentPercent();

virtual void InitiateUpdate();
void Init();
Expand Down
2 changes: 2 additions & 0 deletions src/engine/console.h
Expand Up @@ -70,6 +70,7 @@ class IConsole : public IInterface
int GetAccessLevel() const { return m_AccessLevel; }
};

typedef void (*FTeeHistorianCommandCallback)(int ClientID, int FlagMask, const char *pCmd, IResult *pResult, void *pUser);
typedef void (*FPrintCallback)(const char *pStr, void *pUser, bool Highlighted);
typedef void (*FPossibleCallback)(const char *pCmd, void *pUser);
typedef void (*FCommandCallback)(IResult *pResult, void *pUserData);
Expand All @@ -96,6 +97,7 @@ class IConsole : public IInterface
virtual int RegisterPrintCallback(int OutputLevel, FPrintCallback pfnPrintCallback, void *pUserData) = 0;
virtual void SetPrintOutputLevel(int Index, int OutputLevel) = 0;
virtual void Print(int Level, const char *pFrom, const char *pStr, bool Highlighted = false) = 0;
virtual void SetTeeHistorianCommandCallback(FTeeHistorianCommandCallback pfnCallback, void *pUser) = 0;

virtual void SetAccessLevel(int AccessLevel) = 0;

Expand Down
13 changes: 9 additions & 4 deletions src/engine/engine.h
Expand Up @@ -6,10 +6,16 @@
#include "kernel.h"
#include <engine/shared/jobs.h>

class CHostLookup
class CHostLookup : public IJob
{
private:
virtual void Run();

public:
CJob m_Job;
CHostLookup();
CHostLookup(const char *pHostname, int Nettype);

int m_Result;
char m_aHostname[128];
int m_Nettype;
NETADDR m_Addr;
Expand All @@ -25,8 +31,7 @@ class IEngine : public IInterface
public:
virtual void Init() = 0;
virtual void InitLogfile() = 0;
virtual void HostLookup(CHostLookup *pLookup, const char *pHostname, int Nettype) = 0;
virtual void AddJob(CJob *pJob, JOBFUNC pfnFunc, void *pData) = 0;
virtual void AddJob(std::shared_ptr<IJob> pJob) = 0;
};

extern IEngine *CreateEngine(const char *pAppname, bool Silent);
Expand Down
2,618 changes: 2,618 additions & 0 deletions src/engine/external/glew/GL/eglew.h

Large diffs are not rendered by default.

23,686 changes: 23,686 additions & 0 deletions src/engine/external/glew/GL/glew.h

Large diffs are not rendered by default.

1,775 changes: 1,775 additions & 0 deletions src/engine/external/glew/GL/glxew.h

Large diffs are not rendered by default.

1,447 changes: 1,447 additions & 0 deletions src/engine/external/glew/GL/wglew.h

Large diffs are not rendered by default.

73 changes: 73 additions & 0 deletions src/engine/external/glew/LICENSE.txt
@@ -0,0 +1,73 @@
The OpenGL Extension Wrangler Library
Copyright (C) 2002-2007, Milan Ikits <milan ikits[]ieee org>
Copyright (C) 2002-2007, Marcelo E. Magallon <mmagallo[]debian org>
Copyright (C) 2002, Lev Povalahev
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.


Mesa 3-D graphics library
Version: 7.0

Copyright (C) 1999-2007 Brian Paul All Rights Reserved.

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
BRIAN PAUL 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.


Copyright (c) 2007 The Khronos Group Inc.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and/or associated documentation files (the
"Materials"), to deal in the Materials without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Materials, and to
permit persons to whom the Materials are 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 Materials.

THE MATERIALS ARE 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
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
190 changes: 190 additions & 0 deletions src/engine/external/glew/README.md
@@ -0,0 +1,190 @@
# GLEW - The OpenGL Extension Wrangler Library

![](http://glew.sourceforge.net/glew.png)

http://glew.sourceforge.net/

https://github.com/nigels-com/glew

[![Build Status](https://travis-ci.org/nigels-com/glew.svg?branch=master)](https://travis-ci.org/nigels-com/glew)
[![Gitter](https://badges.gitter.im/nigels-com/glew.svg)](https://gitter.im/nigels-com/glew?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Download](https://img.shields.io/sourceforge/dm/glew.svg)](https://sourceforge.net/projects/glew/files/latest/download)

## Downloads

Current release is [2.1.0](https://sourceforge.net/projects/glew/files/glew/2.1.0/).
[(Change Log)](http://glew.sourceforge.net/log.html)

Sources available as
[ZIP](https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0.zip/download) or
[TGZ](https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0.tgz/download).

Windows binaries for [32-bit and 64-bit](https://sourceforge.net/projects/glew/files/glew/2.1.0/glew-2.1.0-win32.zip/download).

### Recent snapshots

Snapshots may contain new features, bug-fixes or new OpenGL extensions ahead of tested, official releases.

## Build

It is highly recommended to build from a tgz or zip release snapshot.
The code generation workflow is a complex brew of gnu make, perl and python, that works best on Linux or Mac.
For most end-users of GLEW the official releases are the best choice, with first class support.

### Linux and Mac

#### Using GNU Make

##### Install build tools

Debian/Ubuntu/Mint: `$ sudo apt-get install build-essential libxmu-dev libxi-dev libgl-dev libosmesa-dev`

RedHat/CentOS/Fedora: `$ sudo yum install libXmu-devel libXi-devel libGL-devel`

##### Build

$ make
$ sudo make install
$ make clean

Targets: `all, glew.lib (sub-targets: glew.lib.shared, glew.lib.static), glew.bin, clean, install, uninstall`

Variables: `SYSTEM=linux-clang, GLEW_DEST=/usr/local, STRIP=`

_Note: may need to make **auto** folder_

#### Using cmake

*CMake 2.8.12 or higher is required.*

##### Install build tools

Debian/Ubuntu/Mint: `$ sudo apt-get install build-essential libXmu-dev libXi-dev libgl-dev cmake`

RedHat/CentOS/Fedora: `$ sudo yum install libXmu-devel libXi-devel libGL-devel cmake`

##### Build

$ cd build
$ cmake ./cmake
$ make -j4

| Target | Description |
| ---------- | ----------- |
| glew | Build the glew shared library. |
| glew_s | Build the glew static library. |
| glewinfo | Build the `glewinfo` executable (requires `BUILD_UTILS` to be `ON`). |
| visualinfo | Build the `visualinfo` executable (requires `BUILD_UTILS` to be `ON`). |
| install | Install all enabled targets into `CMAKE_INSTALL_PREFIX`. |
| clean | Clean up build artifacts. |
| all | Build all enabled targets (default target). |

| Variables | Description |
| --------------- | ----------- |
| BUILD_UTILS | Build the `glewinfo` and `visualinfo` executables. |
| GLEW_REGAL | Build in Regal mode. |
| GLEW_OSMESA | Build in off-screen Mesa mode. |
| BUILD_FRAMEWORK | Build as MacOSX Framework. Setting `CMAKE_INSTALL_PREFIX` to `/Library/Frameworks` is recommended. |

### Windows

#### Visual Studio

Use the provided Visual Studio project file in build/vc12/

Projects for vc6 and vc10 are also provided

#### MSYS/Mingw

Available from [Mingw](http://www.mingw.org/)

Requirements: bash, make, gcc

$ mingw32-make
$ mingw32-make install
$ mingw32-make install.all

Alternative toolchain: `SYSTEM=mingw-win32`

#### MSYS2/Mingw-w64

Available from [Msys2](http://msys2.github.io/) and/or [Mingw-w64](http://mingw-w64.org/)

Requirements: bash, make, gcc

$ pacman -S gcc make mingw-w64-i686-gcc mingw-w64-x86_64-gcc
$ make
$ make install
$ make install.all

Alternative toolchain: `SYSTEM=msys, SYSTEM=msys-win32, SYSTEM=msys-win64`

## glewinfo

`glewinfo` is a command-line tool useful for inspecting the capabilities of an
OpenGL implementation and GLEW support for that. Please include `glewinfo.txt`
with bug reports, as appropriate.

---------------------------
GLEW Extension Info
---------------------------

GLEW version 2.0.0
Reporting capabilities of pixelformat 3
Running on a Intel(R) HD Graphics 3000 from Intel
OpenGL version 3.1.0 - Build 9.17.10.4229 is supported

GL_VERSION_1_1: OK
---------------

GL_VERSION_1_2: OK
---------------
glCopyTexSubImage3D: OK
glDrawRangeElements: OK
glTexImage3D: OK
glTexSubImage3D: OK

...

## Code Generation

A Unix or Mac environment is needed for building GLEW from scratch to
include new extensions, or customize the code generation. The extension
data is regenerated from the top level source directory with:

make extensions

An alternative to generating the GLEW sources from scratch is to
download a pre-generated (unsupported) snapshot:

https://sourceforge.net/projects/glew/files/glew/snapshots/

Travis-built snapshots are also available:

https://glew.s3.amazonaws.com/index.html

## Authors

GLEW is currently maintained by [Nigel Stewart](https://github.com/nigels-com)
with bug fixes, new OpenGL extension support and new releases.

GLEW was developed by [Milan Ikits](http://www.cs.utah.edu/~ikits/)
and [Marcelo Magallon](http://wwwvis.informatik.uni-stuttgart.de/~magallon/).
Aaron Lefohn, Joe Kniss, and Chris Wyman were the first users and also
assisted with the design and debugging process.

The acronym GLEW originates from Aaron Lefohn.
Pasi K&auml;rkk&auml;inen identified and fixed several problems with
GLX and SDL. Nate Robins created the `wglinfo` utility, to
which modifications were made by Michael Wimmer.

## Copyright and Licensing

GLEW is originally derived from the EXTGL project by Lev Povalahev.
The source code is licensed under the
[Modified BSD License](http://glew.sourceforge.net/glew.txt), the
[Mesa 3-D License](http://glew.sourceforge.net/mesa.txt) (MIT) and the
[Khronos License](http://glew.sourceforge.net/khronos.txt) (MIT).

The automatic code generation scripts are released under the
[GNU GPL](http://glew.sourceforge.net/gpl.txt).
1 change: 1 addition & 0 deletions src/engine/external/glew/VERSION
@@ -0,0 +1 @@
2.1.0
28,581 changes: 28,581 additions & 0 deletions src/engine/external/glew/glew.c

Large diffs are not rendered by default.

62 changes: 0 additions & 62 deletions src/engine/fetcher.h

This file was deleted.

60 changes: 60 additions & 0 deletions src/engine/ghost.h
@@ -0,0 +1,60 @@
#ifndef ENGINE_GHOST_H
#define ENGINE_GHOST_H

#include <engine/shared/protocol.h>

#include "kernel.h"

class CGhostHeader
{
public:
unsigned char m_aMarker[8];
unsigned char m_Version;
char m_aOwner[MAX_NAME_LENGTH];
char m_aMap[64];
unsigned char m_aCrc[4];
unsigned char m_aNumTicks[4];
unsigned char m_aTime[4];

int GetTime() const
{
return (m_aTime[0] << 24) | (m_aTime[1] << 16) | (m_aTime[2] << 8) | (m_aTime[3]);
}

int GetTicks() const
{
return (m_aNumTicks[0] << 24) | (m_aNumTicks[1] << 16) | (m_aNumTicks[2] << 8) | (m_aNumTicks[3]);
}
};

class IGhostRecorder : public IInterface
{
MACRO_INTERFACE("ghostrecorder", 0)
public:
virtual ~IGhostRecorder() {}

virtual int Start(const char *pFilename, const char *pMap, unsigned MapCrc, const char *pName) = 0;
virtual int Stop(int Ticks, int Time) = 0;

virtual void WriteData(int Type, const void *pData, int Size) = 0;
virtual bool IsRecording() const = 0;
};

class IGhostLoader : public IInterface
{
MACRO_INTERFACE("ghostloader", 0)
public:
virtual ~IGhostLoader() {}

virtual int Load(const char *pFilename, const char *pMap, unsigned Crc) = 0;
virtual void Close() = 0;

virtual const CGhostHeader *GetHeader() const = 0;

virtual bool ReadNextType(int *pType) = 0;
virtual bool ReadData(int Type, void *pData, int Size) = 0;

virtual bool GetGhostInfo(const char *pFilename, CGhostHeader *pGhostHeader, const char *pMap, unsigned Crc) = 0;
};

#endif
9 changes: 9 additions & 0 deletions src/engine/graphics.h
Expand Up @@ -95,6 +95,15 @@ class IGraphics : public IInterface
virtual int LoadTexture(const char *pFilename, int StorageType, int StoreFormat, int Flags) = 0;
virtual int LoadTextureRawSub(int TextureID, int x, int y, int Width, int Height, int Format, const void *pData) = 0;
virtual void TextureSet(int TextureID) = 0;

//upload/draw of VBO and IBO
virtual void DrawVisualObject(int VisualObjectIDX, float* pColor, char** pOffsets, unsigned int* IndicedVertexDrawNum, size_t NumIndicesOffet) = 0;
virtual void DrawBorderTile(int VisualObjectIDX, float* pColor, char* pOffset, float* Offset, float* Dir, int JumpIndex, unsigned int DrawNum) = 0;
virtual void DrawBorderTileLine(int VisualObjectIDX, float* pColor, char* pOffset, float* Dir, unsigned int IndexDrawNum, unsigned int RedrawNum) = 0;
virtual void DestroyVisual(int VisualObjectIDX) = 0;
virtual int CreateVisualObjects(float* pVertices, unsigned char* pTexCoords, int NumTiles, unsigned int NumIndicesRequired) = 0;
virtual void AppendAllVertices(float* pVertices, unsigned char* pTexCoords, int NumTiles, int VisualObjectIDX) = 0;
virtual bool IsBufferingEnabled() = 0;

struct CLineItem
{
Expand Down
8 changes: 8 additions & 0 deletions src/engine/server.h
Expand Up @@ -132,6 +132,8 @@ class IServer : public IInterface
return true;
}

virtual void GetMapInfo(char *pMapName, int MapNameSize, int *pMapSize, int *pMapCrc) = 0;

virtual void SetClientName(int ClientID, char const *pName) = 0;
virtual void SetClientClan(int ClientID, char const *pClan) = 0;
virtual void SetClientCountry(int ClientID, int Country) = 0;
Expand All @@ -155,6 +157,7 @@ class IServer : public IInterface
};
virtual void SetRconCID(int ClientID) = 0;
virtual int GetAuthedState(int ClientID) = 0;
virtual const char *GetAuthName(int ClientID) = 0;
virtual void Kick(int ClientID, const char *pReason) = 0;

virtual void DemoRecorder_HandleAutoStart() = 0;
Expand All @@ -178,6 +181,8 @@ class IServer : public IInterface
virtual void ResetNetErrorString(int ClientID) = 0;
virtual bool SetTimedOut(int ClientID, int OrigID) = 0;
virtual void SetTimeoutProtected(int ClientID) = 0;

virtual void SetErrorShutdown(const char *pReason) = 0;
};

class IGameServer : public IInterface
Expand Down Expand Up @@ -218,6 +223,9 @@ class IGameServer : public IInterface
virtual int GetClientVersion(int ClientID) = 0;
virtual void SetClientVersion(int ClientID, int Version) = 0;
virtual bool PlayerExists(int ClientID) = 0;

virtual void OnClientEngineJoin(int ClientID) = 0;
virtual void OnClientEngineDrop(int ClientID, const char *pReason) = 0;
};

extern IGameServer *CreateGameServer();
Expand Down
180 changes: 145 additions & 35 deletions src/engine/server/server.cpp
Expand Up @@ -323,6 +323,10 @@ CServer::CServer()
m_ServerInfoNumRequests = 0;
m_ServerInfoHighLoad = false;

#ifdef CONF_FAMILY_UNIX
m_ConnLoggingSocketCreated = false;
#endif

#if defined (CONF_SQL)
for (int i = 0; i < MAX_SQLSERVERS; i++)
{
Expand All @@ -334,6 +338,8 @@ CServer::CServer()
CSqlConnector::SetWriteServers(m_apSqlWriteServers);
#endif

m_aErrorShutdownReason[0] = 0;

Init();
}

Expand Down Expand Up @@ -490,6 +496,16 @@ int CServer::GetAuthedState(int ClientID)
return m_aClients[ClientID].m_Authed;
}

const char *CServer::GetAuthName(int ClientID)
{
int Key = m_aClients[ClientID].m_AuthKey;
if(Key == -1)
{
return 0;
}
return m_AuthManager.KeyIdent(Key);
}

int CServer::GetClientInfo(int ClientID, CClientInfo *pInfo)
{
dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid");
Expand Down Expand Up @@ -709,10 +725,10 @@ void CServer::DoSnapshot()
const int MaxSize = MAX_SNAPSHOT_PACKSIZE;
int NumPackets;

SnapshotSize = CVariableInt::Compress(aDeltaData, DeltaSize, aCompData);
SnapshotSize = CVariableInt::Compress(aDeltaData, DeltaSize, aCompData, sizeof(aCompData));
NumPackets = (SnapshotSize+MaxSize-1)/MaxSize;

for(int n = 0, Left = SnapshotSize; Left; n++)
for(int n = 0, Left = SnapshotSize; Left > 0; n++)
{
int Chunk = Left < MaxSize ? Left : MaxSize;
Left -= Chunk;
Expand Down Expand Up @@ -789,7 +805,9 @@ int CServer::NewClientNoAuthCallback(int ClientID, bool Reset, void *pUser)
}

pThis->SendMap(ClientID);

#if defined(CONF_FAMILY_UNIX)
pThis->SendConnLoggingCommand(OPEN_SESSION, pThis->m_NetServer.ClientAddr(ClientID));
#endif
return 0;
}

Expand All @@ -809,6 +827,11 @@ int CServer::NewClientCallback(int ClientID, void *pUser)
pThis->m_aClients[ClientID].m_TrafficSince = 0;
memset(&pThis->m_aClients[ClientID].m_Addr, 0, sizeof(NETADDR));
pThis->m_aClients[ClientID].Reset();
pThis->GameServer()->OnClientEngineJoin(ClientID);

#if defined(CONF_FAMILY_UNIX)
pThis->SendConnLoggingCommand(OPEN_SESSION, pThis->m_NetServer.ClientAddr(ClientID));
#endif
return 0;
}

Expand All @@ -817,22 +840,43 @@ void CServer::InitDnsbl(int ClientID)
NETADDR Addr = *m_NetServer.ClientAddr(ClientID);

//TODO: support ipv6
if (Addr.type != NETTYPE_IPV4)
if(Addr.type != NETTYPE_IPV4)
return;

// build dnsbl host lookup
char aBuf[256];
if (g_Config.m_SvDnsblKey[0] == '\0')
if(g_Config.m_SvDnsblKey[0] == '\0')
{
// without key
str_format(aBuf, sizeof(aBuf), "%d.%d.%d.%d.%s", Addr.ip[3], Addr.ip[2], Addr.ip[1], Addr.ip[0], g_Config.m_SvDnsblHost);
}
else
{
// with key
str_format(aBuf, sizeof(aBuf), "%s.%d.%d.%d.%d.%s", g_Config.m_SvDnsblKey, Addr.ip[3], Addr.ip[2], Addr.ip[1], Addr.ip[0], g_Config.m_SvDnsblHost);
}

IEngine *pEngine = Kernel()->RequestInterface<IEngine>();
pEngine->HostLookup(&m_aClients[ClientID].m_DnsblLookup, aBuf, NETTYPE_IPV4);
pEngine->AddJob(m_aClients[ClientID].m_pDnsblLookup = std::make_shared<CHostLookup>(aBuf, NETTYPE_IPV4));
}

#ifdef CONF_FAMILY_UNIX
void CServer::SendConnLoggingCommand(CONN_LOGGING_CMD Cmd, const NETADDR* pAddr)
{
if(!g_Config.m_SvConnLoggingServer[0] || !m_ConnLoggingSocketCreated)
return;

// pack the data and send it
unsigned char aData[23] = {0};
aData[0] = Cmd;
mem_copy(&aData[1], &pAddr->type, 4);
mem_copy(&aData[5], pAddr->ip, 16);
mem_copy(&aData[21], &pAddr->port, 2);

net_unix_send(m_ConnLoggingSocket, &m_ConnLoggingDestAddr, aData, sizeof(aData));
}
#endif

int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
{
CServer *pThis = (CServer *)pUser;
Expand Down Expand Up @@ -860,6 +904,11 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
pThis->m_aClients[ClientID].m_TrafficSince = 0;
pThis->m_aPrevStates[ClientID] = CClient::STATE_EMPTY;
pThis->m_aClients[ClientID].m_Snapshots.PurgeAll();

pThis->GameServer()->OnClientEngineDrop(ClientID, pReason);
#if defined(CONF_FAMILY_UNIX)
pThis->SendConnLoggingCommand(CLOSE_SESSION, pThis->m_NetServer.ClientAddr(ClientID));
#endif
return 0;
}

Expand All @@ -870,6 +919,13 @@ void CServer::SendRconType(int ClientID, bool UsernameReq)
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
}

void CServer::GetMapInfo(char *pMapName, int MapNameSize, int *pMapSize, int *pMapCrc)
{
str_copy(pMapName, GetMapName(), MapNameSize);
*pMapSize = m_CurrentMapSize;
*pMapCrc = m_CurrentMapCrc;
}

void CServer::SendMap(int ClientID)
{
CMsgPacker Msg(NETMSG_MAP_CHANGE);
Expand Down Expand Up @@ -1051,7 +1107,7 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
}

// reserved slot
if(ClientID >= (g_Config.m_SvMaxClients - g_Config.m_SvReservedSlots) && g_Config.m_SvReservedSlotsPass[0] != 0 && strcmp(g_Config.m_SvReservedSlotsPass, pPassword) != 0)
if(ClientID >= (g_Config.m_SvMaxClients - g_Config.m_SvReservedSlots) && g_Config.m_SvReservedSlotsPass[0] != 0 && str_comp(g_Config.m_SvReservedSlotsPass, pPassword) != 0)
{
m_NetServer.Drop(ClientID, "This server is full");
return;
Expand Down Expand Up @@ -1603,7 +1659,9 @@ void CServer::PumpNetwork()
}
}
else
{
ProcessClientPacket(&Packet);
}
}

m_ServerBan.Update();
Expand Down Expand Up @@ -1733,6 +1791,10 @@ int CServer::Run()
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);

GameServer()->OnInit();
if(ErrorShutdown())
{
return 1;
}
str_format(aBuf, sizeof(aBuf), "version %s", GameServer()->NetVersion());
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);

Expand Down Expand Up @@ -1795,6 +1857,10 @@ int CServer::Run()
m_ServerInfoFirstRequest = 0;
Kernel()->ReregisterInterface(GameServer());
GameServer()->OnInit();
if(ErrorShutdown())
{
break;
}
UpdateServerInfo();
}
else
Expand All @@ -1820,10 +1886,10 @@ int CServer::Run()
InitDnsbl(ClientID);
}
else if (m_aClients[ClientID].m_DnsblState == CClient::DNSBL_STATE_PENDING &&
m_aClients[ClientID].m_DnsblLookup.m_Job.Status() == CJob::STATE_DONE)
m_aClients[ClientID].m_pDnsblLookup->Status() == IJob::STATE_DONE)
{

if (m_aClients[ClientID].m_DnsblLookup.m_Job.Result() != 0)
if (m_aClients[ClientID].m_pDnsblLookup->m_Result != 0)
{
// entry not found -> whitelisted
m_aClients[ClientID].m_DnsblState = CClient::DNSBL_STATE_WHITELISTED;
Expand Down Expand Up @@ -1872,6 +1938,10 @@ int CServer::Run()
}

GameServer()->OnTick();
if(ErrorShutdown())
{
break;
}
}

// snap game
Expand Down Expand Up @@ -1929,11 +1999,16 @@ int CServer::Run()
}
}
}
const char *pDisconnectReason = "Server shutdown";
if(ErrorShutdown())
{
pDisconnectReason = m_aErrorShutdownReason;
}
// disconnect all clients on shutdown
for(int i = 0; i < MAX_CLIENTS; ++i)
{
if(m_aClients[i].m_State != CClient::STATE_EMPTY)
m_NetServer.Drop(i, "Server shutdown");
m_NetServer.Drop(i, pDisconnectReason);
}

m_Econ.Shutdown();
Expand All @@ -1949,17 +2024,17 @@ int CServer::Run()
mem_free(m_pCurrentMapData);

#if defined (CONF_SQL)
for (int i = 0; i < MAX_SQLSERVERS; i++)
{
if (m_apSqlReadServers[i])
delete m_apSqlReadServers[i];
for (int i = 0; i < MAX_SQLSERVERS; i++)
{
if (m_apSqlReadServers[i])
delete m_apSqlReadServers[i];

if (m_apSqlWriteServers[i])
delete m_apSqlWriteServers[i];
}
if (m_apSqlWriteServers[i])
delete m_apSqlWriteServers[i];
}
#endif

return 0;
return ErrorShutdown();
}

void CServer::ConTestingCommands(CConsole::IResult *pResult, void *pUser)
Expand Down Expand Up @@ -2523,6 +2598,8 @@ void CServer::LogoutClient(int ClientID, const char *pReason)
m_aClients[ClientID].m_Authed = AUTHED_NO;
m_aClients[ClientID].m_AuthKey = -1;

GameServer()->OnSetAuthed(ClientID, AUTHED_NO);

Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}

Expand Down Expand Up @@ -2576,6 +2653,34 @@ void CServer::ConchainRconHelperPasswordChange(IConsole::IResult *pResult, void
((CServer *)pUserData)->ConchainRconPasswordChangeGeneric(AUTHED_HELPER, pResult);
}

#if defined(CONF_FAMILY_UNIX)
void CServer::ConchainConnLoggingServerChange(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
if(pResult->NumArguments() == 1)
{
CServer *pServer = (CServer *)pUserData;

// open socket to send new connections
if(!pServer->m_ConnLoggingSocketCreated)
{
pServer->m_ConnLoggingSocket = net_unix_create_unnamed();
if(pServer->m_ConnLoggingSocket == -1)
{
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "Failed to created socket for communication with the connection logging server.");
}
else
{
pServer->m_ConnLoggingSocketCreated = true;
}
}

// set the destination address for the connection logging
net_unix_set_addr(&pServer->m_ConnLoggingDestAddr, pResult->GetString(0));
}
}
#endif

void CServer::RegisterCommands()
{
m_pConsole = Kernel()->RequestInterface<IConsole>();
Expand All @@ -2594,20 +2699,18 @@ void CServer::RegisterCommands()

Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "Reload the map");

#if defined (CONF_SQL)

Console()->Register("add_sqlserver", "s['r'|'w'] s[Database] s[Prefix] s[User] s[Password] s[IP] i[Port] ?i[SetUpDatabase ?]", CFGFLAG_SERVER, ConAddSqlServer, this, "add a sqlserver");
#if defined(CONF_SQL)
Console()->Register("add_sqlserver", "s['r'|'w'] s[Database] s[Prefix] s[User] s[Password] s[IP] i[Port] ?i[SetUpDatabase ?]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAddSqlServer, this, "add a sqlserver");
Console()->Register("dump_sqlservers", "s['r'|'w']", CFGFLAG_SERVER, ConDumpSqlServers, this, "dumps all sqlservers readservers = r, writeservers = w");

#endif

Console()->Register("dnsbl_status", "", CFGFLAG_SERVER, ConDnsblStatus, this, "List blacklisted players");

Console()->Register("auth_add", "s[ident] s[level] s[pw]", CFGFLAG_SERVER, ConAuthAdd, this, "Add a rcon key");
Console()->Register("auth_add_p", "s[ident] s[level] s[hash] s[salt]", CFGFLAG_SERVER, ConAuthAddHashed, this, "Add a prehashed rcon key");
Console()->Register("auth_change", "s[ident] s[level] s[pw]", CFGFLAG_SERVER, ConAuthUpdate, this, "Update a rcon key");
Console()->Register("auth_change_p", "s[ident] s[level] s[hash] s[salt]", CFGFLAG_SERVER, ConAuthUpdateHashed, this, "Update a rcon key with prehashed data");
Console()->Register("auth_remove", "s[ident]", CFGFLAG_SERVER, ConAuthRemove, this, "Remove a rcon key");
Console()->Register("auth_add", "s[ident] s[level] s[pw]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAuthAdd, this, "Add a rcon key");
Console()->Register("auth_add_p", "s[ident] s[level] s[hash] s[salt]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAuthAddHashed, this, "Add a prehashed rcon key");
Console()->Register("auth_change", "s[ident] s[level] s[pw]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAuthUpdate, this, "Update a rcon key");
Console()->Register("auth_change_p", "s[ident] s[level] s[hash] s[salt]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAuthUpdateHashed, this, "Update a rcon key with prehashed data");
Console()->Register("auth_remove", "s[ident]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAuthRemove, this, "Remove a rcon key");
Console()->Register("auth_list", "", CFGFLAG_SERVER, ConAuthList, this, "List all rcon keys");

Console()->Chain("sv_name", ConchainSpecialInfoupdate, this);
Expand All @@ -2621,6 +2724,10 @@ void CServer::RegisterCommands()
Console()->Chain("sv_rcon_mod_password", ConchainRconModPasswordChange, this);
Console()->Chain("sv_rcon_helper_password", ConchainRconHelperPasswordChange, this);

#if defined(CONF_FAMILY_UNIX)
Console()->Chain("sv_conn_logging_server", ConchainConnLoggingServerChange, this);
#endif

// register console commands in sub parts
m_ServerBan.InitServerBan(Console(), Storage(), this);
m_pGameServer->OnConsoleInit();
Expand Down Expand Up @@ -2671,10 +2778,6 @@ int main(int argc, const char **argv) // ignore_convention
}
}

#if !defined(CONF_PLATFORM_MACOSX) && !defined(FUZZING)
dbg_enable_threaded();
#endif

if(secure_random_init() != 0)
{
dbg_msg("secure", "could not initialize secure RNG");
Expand Down Expand Up @@ -2751,6 +2854,7 @@ int main(int argc, const char **argv) // ignore_convention

// free
delete pKernel;

return 0;
}

Expand Down Expand Up @@ -2784,7 +2888,7 @@ const char *CServer::GetAnnouncementLine(char const *pFileName)
}
else if(!g_Config.m_SvAnnouncementRandom)
{
if(m_AnnouncementLastLine >= v.size())
if(++m_AnnouncementLastLine >= v.size())
m_AnnouncementLastLine %= v.size();
}
else
Expand All @@ -2802,12 +2906,13 @@ const char *CServer::GetAnnouncementLine(char const *pFileName)
return v[m_AnnouncementLastLine];
}

int* CServer::GetIdMap(int ClientID)
int *CServer::GetIdMap(int ClientID)
{
return (int*)(IdMap + VANILLA_MAX_CLIENTS * ClientID);
return (int *)(IdMap + VANILLA_MAX_CLIENTS * ClientID);
}

bool CServer::SetTimedOut(int ClientID, int OrigID) {
bool CServer::SetTimedOut(int ClientID, int OrigID)
{
if (!m_NetServer.SetTimedOut(ClientID, OrigID))
{
return false;
Expand All @@ -2816,3 +2921,8 @@ bool CServer::SetTimedOut(int ClientID, int OrigID) {
m_aClients[ClientID].m_Authed = IServer::AUTHED_NO;
return true;
}

void CServer::SetErrorShutdown(const char *pReason)
{
str_copy(m_aErrorShutdownReason, pReason, sizeof(m_aErrorShutdownReason));
}