473 changes: 235 additions & 238 deletions src/engine/client/video.cpp

Large diffs are not rendered by default.

63 changes: 32 additions & 31 deletions src/engine/client/video.h
Expand Up @@ -37,18 +37,18 @@ extern "C"

// a wrapper around a single output AVStream
typedef struct OutputStream {
AVStream *st;
AVCodecContext *enc;
AVStream *pSt;
AVCodecContext *pEnc;

/* pts of the next frame that will be generated */
int64_t next_pts;
int samples_count;
int64_t NextPts;
int SamplesCount;

AVFrame *frame;
AVFrame *tmp_frame;
AVFrame *pFrame;
AVFrame *pTmpFrame;

struct SwsContext *sws_ctx;
struct SwrContext *swr_ctx;
struct SwsContext *pSwsCtx;
struct SwrContext *pSwrCtx;
} OutputStream;

class CVideo : public IVideo
Expand All @@ -57,38 +57,39 @@ class CVideo : public IVideo
CVideo(class CGraphics_Threaded* pGraphics, class IStorage* pStorage, class IConsole *pConsole, int width, int height, const char *name);
~CVideo();

virtual void start();
virtual void stop();
virtual void pause(bool p);
virtual void Start();
virtual void Stop();
virtual void Pause(bool Pause);
virtual bool IsRecording() { return m_Recording; }

virtual void nextVideoFrame();
virtual void nextVideoFrame_thread();
virtual bool frameRendered() { return !m_NextFrame; }
virtual void NextVideoFrame();
virtual void NextVideoFrameThread();
virtual bool FrameRendered() { return !m_NextFrame; }

virtual void nextAudioFrame(void (*Mix)(short *pFinalOut, unsigned Frames));
virtual void nextAudioFrame_timeline();
virtual bool aframeRendered() { return !m_NextaFrame; }
virtual void NextAudioFrame(void (*Mix)(short *pFinalOut, unsigned Frames));
virtual void NextAudioFrameTimeline();
virtual bool AudioFrameRendered() { return !m_NextAudioFrame; }

static IVideo* Current() { return IVideo::ms_pCurrentVideo; }

static void Init() { av_log_set_level(AV_LOG_DEBUG); }

private:
void fill_video_frame();
void read_rgb_from_gl();
void FillVideoFrame();
void ReadRGBFromGL();

void fill_audio_frame();
void FillAudioFrame();

void open_video();
void open_audio();
AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height);
AVFrame* alloc_audio_frame(enum AVSampleFormat sample_fmt, uint64_t channel_layout, int sample_rate, int nb_samples);
void OpenVideo();
void OpenAudio();
AVFrame *AllocPicture(enum AVPixelFormat PixFmt, int Width, int Height);
AVFrame* AllocAudioFrame(enum AVSampleFormat SampleFmt, uint64_t ChannelLayout, int SampleRate, int NbSamples);

void write_frame(OutputStream* pStream);
void finish_frames(OutputStream* pStream);
void close_stream(OutputStream *ost);
void WriteFrame(OutputStream* pStream);
void FinishFrames(OutputStream* pStream);
void CloseStream(OutputStream *pStream);

void add_stream(OutputStream *ost, AVFormatContext *oc, AVCodec **codec, enum AVCodecID codec_id);
void AddStream(OutputStream *pStream, AVFormatContext *pOC, AVCodec **ppCodec, enum AVCodecID CodecId);

class CGraphics_Threaded* m_pGraphics;
class IStorage* m_pStorage;
Expand All @@ -98,9 +99,9 @@ class CVideo : public IVideo
int m_Height;
char m_Name[256];
//FILE *m_dbgfile;
int m_vseq;
int m_Vseq;
short m_aBuffer[ALEN*2];
int m_vframe;
int m_Vframe;

int m_FPS;

Expand All @@ -111,7 +112,7 @@ class CVideo : public IVideo
bool m_ProcessingAudioFrame;

bool m_NextFrame;
bool m_NextaFrame;
bool m_NextAudioFrame;

bool m_HasAudio;

Expand Down
3 changes: 2 additions & 1 deletion src/engine/demo.h
Expand Up @@ -4,6 +4,7 @@
#define ENGINE_DEMO_H

#include <base/hash.h>
#include <engine/map.h>
#include <engine/shared/uuid_manager.h>
#include "kernel.h"

Expand Down Expand Up @@ -45,7 +46,7 @@ struct CTimelineMarkers

struct CMapInfo
{
char m_aName[128];
char m_aName[MAX_MAP_LENGTH];
SHA256_DIGEST m_Sha256;
int m_Crc;
int m_Size;
Expand Down
1 change: 1 addition & 0 deletions src/engine/ghost.h
@@ -1,6 +1,7 @@
#ifndef ENGINE_GHOST_H
#define ENGINE_GHOST_H

#include <engine/map.h>
#include <engine/shared/protocol.h>

#include "kernel.h"
Expand Down
5 changes: 5 additions & 0 deletions src/engine/map.h
Expand Up @@ -6,6 +6,11 @@
#include <base/hash.h>
#include "kernel.h"

enum
{
MAX_MAP_LENGTH = 128
};

class IMap : public IInterface
{
MACRO_INTERFACE("map", 0)
Expand Down
14 changes: 4 additions & 10 deletions src/engine/message.h
Expand Up @@ -9,18 +9,12 @@
class CMsgPacker : public CPacker
{
public:
CMsgPacker(int Type, bool System=false)
int m_MsgID;
bool m_System;
bool m_NoTranslate;
CMsgPacker(int Type, bool System = false, bool NoTranslate = false) : m_MsgID(Type), m_System(System), m_NoTranslate(NoTranslate)
{
Reset();
if(Type < OFFSET_UUID)
{
AddInt((Type<<1)|(System?1:0));
}
else
{
AddInt((0<<1)|(System?1:0)); // NETMSG_EX, NETMSGTYPE_EX
g_UuidManager.PackUuid(Type, this);
}
}
};

Expand Down
57 changes: 48 additions & 9 deletions src/engine/server.h
Expand Up @@ -3,12 +3,16 @@
#ifndef ENGINE_SERVER_H
#define ENGINE_SERVER_H

#include <type_traits>

#include <base/hash.h>
#include <base/math.h>

#include "kernel.h"
#include "message.h"
#include <game/generated/protocol.h>
#include <game/generated/protocol7.h>
#include <game/generated/protocolglue.h>
#include <engine/shared/protocol.h>

struct CAntibotRoundData;
Expand Down Expand Up @@ -52,24 +56,40 @@ class IServer : public IInterface

virtual int SendMsg(CMsgPacker *pMsg, int Flags, int ClientID) = 0;

template<class T>
int SendPackMsg(T *pMsg, int Flags, int ClientID)
template<class T, typename std::enable_if<!protocol7::is_sixup<T>::value, int>::type = 0>
inline int SendPackMsg(T *pMsg, int Flags, int ClientID)
{
int result = 0;
int Result = 0;
T tmp;
if (ClientID == -1)
{
for(int i = 0; i < MAX_CLIENTS; i++)
if(ClientIngame(i))
{
mem_copy(&tmp, pMsg, sizeof(T));
result = SendPackMsgTranslate(&tmp, Flags, i);
Result = SendPackMsgTranslate(&tmp, Flags, i);
}
} else {
mem_copy(&tmp, pMsg, sizeof(T));
result = SendPackMsgTranslate(&tmp, Flags, ClientID);
Result = SendPackMsgTranslate(&tmp, Flags, ClientID);
}
return result;
return Result;
}

template<class T, typename std::enable_if<protocol7::is_sixup<T>::value, int>::type = 1>
inline int SendPackMsg(T *pMsg, int Flags, int ClientID)
{
int Result = 0;
if(ClientID == -1)
{
for(int i = 0; i < MAX_CLIENTS; i++)
if(ClientIngame(i) && IsSixup(i))
Result = SendPackMsgOne(pMsg, Flags, i);
}
else if(IsSixup(ClientID))
Result = SendPackMsgOne(pMsg, Flags, ClientID);

return Result;
}

template<class T>
Expand All @@ -87,12 +107,23 @@ class IServer : public IInterface

int SendPackMsgTranslate(CNetMsg_Sv_Chat *pMsg, int Flags, int ClientID)
{
if (pMsg->m_ClientID >= 0 && !Translate(pMsg->m_ClientID, ClientID))
if(pMsg->m_ClientID >= 0 && !Translate(pMsg->m_ClientID, ClientID))
{
str_format(msgbuf, sizeof(msgbuf), "%s: %s", ClientName(pMsg->m_ClientID), pMsg->m_pMessage);
pMsg->m_pMessage = msgbuf;
pMsg->m_ClientID = VANILLA_MAX_CLIENTS - 1;
}

if(IsSixup(ClientID))
{
protocol7::CNetMsg_Sv_Chat Msg7;
Msg7.m_ClientID = pMsg->m_ClientID;
Msg7.m_pMessage = pMsg->m_pMessage;
Msg7.m_Mode = pMsg->m_Team > 0 ? protocol7::CHAT_TEAM : protocol7::CHAT_ALL;
Msg7.m_TargetID = -1;
return SendPackMsgOne(&Msg7, Flags, ClientID);
}

return SendPackMsgOne(pMsg, Flags, ClientID);
}

Expand All @@ -106,14 +137,18 @@ class IServer : public IInterface
template<class T>
int SendPackMsgOne(T *pMsg, int Flags, int ClientID)
{
CMsgPacker Packer(pMsg->MsgID(), false);
dbg_assert(ClientID != -1, "SendPackMsgOne called with -1");
CMsgPacker Packer(pMsg->MsgID(), false, protocol7::is_sixup<T>::value);

if(pMsg->Pack(&Packer))
return -1;
return SendMsg(&Packer, Flags, ClientID);
}

bool Translate(int& Target, int Client)
{
if(IsSixup(Client))
return true;
CClientInfo Info;
GetClientInfo(Client, &Info);
if (Info.m_DDNetVersion >= VERSION_DDNET_OLD)
Expand All @@ -134,6 +169,8 @@ class IServer : public IInterface

bool ReverseTranslate(int& Target, int Client)
{
if(IsSixup(Client))
return true;
CClientInfo Info;
GetClientInfo(Client, &Info);
if (Info.m_DDNetVersion >= VERSION_DDNET_OLD)
Expand Down Expand Up @@ -199,6 +236,8 @@ class IServer : public IInterface
virtual void SendMsgRaw(int ClientID, const void *pData, int Size, int Flags) = 0;

virtual char *GetMapName() = 0;

virtual bool IsSixup(int ClientID) const = 0;
};

class IGameServer : public IInterface
Expand Down Expand Up @@ -240,7 +279,7 @@ class IGameServer : public IInterface
virtual void OnSetAuthed(int ClientID, int Level) = 0;
virtual bool PlayerExists(int ClientID) = 0;

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

virtual void FillAntibot(CAntibotRoundData *pData) = 0;
Expand Down
87 changes: 65 additions & 22 deletions src/engine/server/register.cpp
Expand Up @@ -10,8 +10,12 @@

#include "register.h"

CRegister::CRegister()
CRegister::CRegister(bool Sixup)
{
m_Sixup = Sixup;
m_pName = Sixup ? "regsixup" : "register";
m_LastTokenRequest = 0;

m_pNetServer = 0;
m_pMasterServer = 0;
m_pConsole = 0;
Expand All @@ -25,24 +29,42 @@ CRegister::CRegister()
m_RegisterRegisteredServer = -1;
}

void CRegister::FeedToken(NETADDR Addr, SECURITY_TOKEN ResponseToken)
{
Addr.port = 0;
for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
{
NETADDR Addr2 = m_aMasterserverInfo[i].m_Addr;
Addr2.port = 0;
if(net_addr_comp(&Addr, &Addr2) == 0)
{
m_aMasterserverInfo[i].m_Token = ResponseToken;
break;
}
}
}

void CRegister::RegisterNewState(int State)
{
m_RegisterState = State;
m_RegisterStateStart = time_get();
}

void CRegister::RegisterSendFwcheckresponse(NETADDR *pAddr)
void CRegister::RegisterSendFwcheckresponse(NETADDR *pAddr, SECURITY_TOKEN ResponseToken)
{
CNetChunk Packet;
Packet.m_ClientID = -1;
Packet.m_Address = *pAddr;
Packet.m_Flags = NETSENDFLAG_CONNLESS;
Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE);
Packet.m_pData = SERVERBROWSE_FWRESPONSE;
m_pNetServer->Send(&Packet);
if(m_Sixup)
m_pNetServer->SendConnlessSixup(&Packet, ResponseToken);
else
m_pNetServer->Send(&Packet);
}

void CRegister::RegisterSendHeartbeat(NETADDR Addr)
void CRegister::RegisterSendHeartbeat(NETADDR Addr, SECURITY_TOKEN ResponseToken)
{
static unsigned char aData[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
unsigned short Port = g_Config.m_SvPort;
Expand All @@ -61,18 +83,24 @@ void CRegister::RegisterSendHeartbeat(NETADDR Addr)
Port = g_Config.m_SvExternalPort;
aData[sizeof(SERVERBROWSE_HEARTBEAT)] = Port >> 8;
aData[sizeof(SERVERBROWSE_HEARTBEAT)+1] = Port&0xff;
m_pNetServer->Send(&Packet);
if(m_Sixup)
m_pNetServer->SendConnlessSixup(&Packet, ResponseToken);
else
m_pNetServer->Send(&Packet);
}

void CRegister::RegisterSendCountRequest(NETADDR Addr)
void CRegister::RegisterSendCountRequest(NETADDR Addr, SECURITY_TOKEN ResponseToken)
{
CNetChunk Packet;
Packet.m_ClientID = -1;
Packet.m_Address = Addr;
Packet.m_Flags = NETSENDFLAG_CONNLESS;
Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT);
Packet.m_pData = SERVERBROWSE_GETCOUNT;
m_pNetServer->Send(&Packet);
if(m_Sixup)
m_pNetServer->SendConnlessSixup(&Packet, ResponseToken);
else
m_pNetServer->Send(&Packet);
}

void CRegister::RegisterGotCount(CNetChunk *pChunk)
Expand Down Expand Up @@ -107,13 +135,19 @@ void CRegister::RegisterUpdate(int Nettype)

m_pMasterServer->Update();

if(m_Sixup && (m_RegisterState == REGISTERSTATE_HEARTBEAT || m_RegisterState == REGISTERSTATE_REGISTERED) && Now > m_LastTokenRequest+Freq*5)
{
m_pNetServer->SendTokenSixup(m_aMasterserverInfo[m_RegisterRegisteredServer].m_Addr, NET_SECURITY_TOKEN_UNKNOWN);
m_LastTokenRequest = Now;
}

if(m_RegisterState == REGISTERSTATE_START)
{
m_RegisterCount = 0;
m_RegisterFirst = 1;
RegisterNewState(REGISTERSTATE_UPDATE_ADDRS);
m_pMasterServer->RefreshAddresses(Nettype);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", "refreshing ip addresses");
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, "refreshing ip addresses");
}
else if(m_RegisterState == REGISTERSTATE_UPDATE_ADDRS)
{
Expand All @@ -133,12 +167,16 @@ void CRegister::RegisterUpdate(int Nettype)

NETADDR Addr = m_pMasterServer->GetAddr(i);
m_aMasterserverInfo[i].m_Addr = Addr;
if(m_Sixup)
m_aMasterserverInfo[i].m_Addr.port = 8283;
m_aMasterserverInfo[i].m_Valid = 1;
m_aMasterserverInfo[i].m_Count = -1;
m_aMasterserverInfo[i].m_LastSend = 0;
m_aMasterserverInfo[i].m_Token = NET_SECURITY_TOKEN_UNKNOWN;
}

m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", "fetching server counts");
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, "fetching server counts");
m_LastTokenRequest = Now;
RegisterNewState(REGISTERSTATE_QUERY_COUNT);
}
}
Expand All @@ -156,13 +194,16 @@ void CRegister::RegisterUpdate(int Nettype)
if(m_aMasterserverInfo[i].m_LastSend+Freq < Now)
{
m_aMasterserverInfo[i].m_LastSend = Now;
RegisterSendCountRequest(m_aMasterserverInfo[i].m_Addr);
if(m_Sixup && m_aMasterserverInfo[i].m_Token == NET_SECURITY_TOKEN_UNKNOWN)
m_pNetServer->SendTokenSixup(m_aMasterserverInfo[i].m_Addr, NET_SECURITY_TOKEN_UNKNOWN);
else
RegisterSendCountRequest(m_aMasterserverInfo[i].m_Addr, m_aMasterserverInfo[i].m_Token);
}
}
}

// check if we are done or timed out
if(Left == 0 || Now > m_RegisterStateStart+Freq*3)
if(Left == 0 || Now > m_RegisterStateStart+Freq*5)
{
// choose server
int Best = -1;
Expand All @@ -180,14 +221,14 @@ void CRegister::RegisterUpdate(int Nettype)
m_RegisterRegisteredServer = Best;
if(m_RegisterRegisteredServer == -1)
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", "WARNING: No master servers. Retrying in 60 seconds");
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, "WARNING: No master servers. Retrying in 60 seconds");
RegisterNewState(REGISTERSTATE_ERROR);
}
else
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "chose '%s' as master, sending heartbeats", m_pMasterServer->GetName(m_RegisterRegisteredServer));
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", aBuf);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, aBuf);
m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend = 0;
RegisterNewState(REGISTERSTATE_HEARTBEAT);
}
Expand All @@ -199,19 +240,19 @@ void CRegister::RegisterUpdate(int Nettype)
if(Now > m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend+Freq*15)
{
m_aMasterserverInfo[m_RegisterRegisteredServer].m_LastSend = Now;
RegisterSendHeartbeat(m_aMasterserverInfo[m_RegisterRegisteredServer].m_Addr);
RegisterSendHeartbeat(m_aMasterserverInfo[m_RegisterRegisteredServer].m_Addr, m_aMasterserverInfo[m_RegisterRegisteredServer].m_Token);
}

if(Now > m_RegisterStateStart+Freq*60)
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", "WARNING: Master server is not responding, switching master");
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, "WARNING: Master server is not responding, switching master");
RegisterNewState(REGISTERSTATE_START);
}
}
else if(m_RegisterState == REGISTERSTATE_REGISTERED)
{
if(m_RegisterFirst)
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", "server registered");
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, "server registered");

m_RegisterFirst = 0;

Expand All @@ -235,7 +276,7 @@ void CRegister::RegisterUpdate(int Nettype)
}
}

int CRegister::RegisterProcessPacket(CNetChunk *pPacket)
int CRegister::RegisterProcessPacket(CNetChunk *pPacket, SECURITY_TOKEN ResponseToken)
{
// check for masterserver address
bool Valid = false;
Expand All @@ -253,24 +294,26 @@ int CRegister::RegisterProcessPacket(CNetChunk *pPacket)
if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) &&
mem_comp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
{
RegisterSendFwcheckresponse(&pPacket->m_Address);
RegisterSendFwcheckresponse(&pPacket->m_Address, ResponseToken);
return 1;
}
else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) &&
mem_comp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
{
if(m_RegisterFirst)
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", "no firewall/nat problems detected");
RegisterNewState(REGISTERSTATE_REGISTERED);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, "no firewall/nat problems detected");

if(m_RegisterState == REGISTERSTATE_HEARTBEAT || m_RegisterState == REGISTERSTATE_REGISTERED)
RegisterNewState(REGISTERSTATE_REGISTERED);
return 1;
}
else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) &&
mem_comp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
{
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", "ERROR: the master server reports that clients can not connect to this server.");
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, "ERROR: the master server reports that clients can not connect to this server.");
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ERROR: configure your firewall/nat to let through udp on port %d.", g_Config.m_SvPort);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "register", aBuf);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, m_pName, aBuf);
//RegisterNewState(REGISTERSTATE_ERROR);
return 1;
}
Expand Down
16 changes: 11 additions & 5 deletions src/engine/server/register.h
Expand Up @@ -23,12 +23,17 @@ class CRegister
int m_Count;
int m_Valid;
int64 m_LastSend;
SECURITY_TOKEN m_Token;
};

class CNetServer *m_pNetServer;
class IEngineMasterServer *m_pMasterServer;
class IConsole *m_pConsole;

bool m_Sixup;
const char *m_pName;
int64 m_LastTokenRequest;

int m_RegisterState;
int64 m_RegisterStateStart;
int m_RegisterFirst;
Expand All @@ -38,16 +43,17 @@ class CRegister
int m_RegisterRegisteredServer;

void RegisterNewState(int State);
void RegisterSendFwcheckresponse(NETADDR *pAddr);
void RegisterSendHeartbeat(NETADDR Addr);
void RegisterSendCountRequest(NETADDR Addr);
void RegisterSendFwcheckresponse(NETADDR *pAddr, SECURITY_TOKEN ResponseToken);
void RegisterSendHeartbeat(NETADDR Addr, SECURITY_TOKEN ResponseToken);
void RegisterSendCountRequest(NETADDR Addr, SECURITY_TOKEN ResponseToken);
void RegisterGotCount(struct CNetChunk *pChunk);

public:
CRegister();
CRegister(bool Sixup);
void Init(class CNetServer *pNetServer, class IEngineMasterServer *pMasterServer, class IConsole *pConsole);
void RegisterUpdate(int Nettype);
int RegisterProcessPacket(struct CNetChunk *pPacket);
int RegisterProcessPacket(struct CNetChunk *pPacket, SECURITY_TOKEN ResponseToken = 0);
void FeedToken(NETADDR Addr, SECURITY_TOKEN ResponseToken);
};

#endif
469 changes: 386 additions & 83 deletions src/engine/server/server.cpp

Large diffs are not rendered by default.

27 changes: 20 additions & 7 deletions src/engine/server/server.h
Expand Up @@ -200,6 +200,8 @@ class CServer : public IServer
// DNSBL
int m_DnsblState;
std::shared_ptr<CHostLookup> m_pDnsblLookup;

bool m_Sixup;
};

CClient m_aClients[MAX_CLIENTS];
Expand Down Expand Up @@ -229,14 +231,21 @@ class CServer : public IServer
int64 m_Lastheartbeat;
//static NETADDR4 master_server;

enum
{
SIX=0,
SIXUP,
};

char m_aCurrentMap[MAX_PATH_LENGTH];
SHA256_DIGEST m_CurrentMapSha256;
unsigned m_CurrentMapCrc;
unsigned char *m_pCurrentMapData;
unsigned int m_CurrentMapSize;
SHA256_DIGEST m_aCurrentMapSha256[2];
unsigned m_aCurrentMapCrc[2];
unsigned char *m_apCurrentMapData[2];
unsigned int m_aCurrentMapSize[2];

CDemoRecorder m_aDemoRecorder[MAX_CLIENTS+1];
CRegister m_Register;
CRegister m_RegSixup;
CAuthManager m_AuthManager;

int m_RconRestrict;
Expand Down Expand Up @@ -290,7 +299,7 @@ class CServer : public IServer

void DoSnapshot();

static int NewClientCallback(int ClientID, void *pUser);
static int NewClientCallback(int ClientID, void *pUser, bool Sixup);
static int NewClientNoAuthCallback(int ClientID, void *pUser);
static int DelClientCallback(int ClientID, const char *pReason, void *pUser);

Expand Down Expand Up @@ -330,11 +339,15 @@ class CServer : public IServer
void Clear();
};
CCache m_ServerInfoCache[3 * 2];
CCache m_SixupServerInfoCache[2];
bool m_ServerInfoNeedsUpdate;

void ExpireServerInfo();
void CacheServerInfo(CCache *pCache, int Type, bool SendClients);
void CacheServerInfoSixup(CCache *pCache, bool SendClients);
void SendServerInfo(const NETADDR *pAddr, int Token, int Type, bool SendClients);
void GetServerInfoSixup(CPacker *pPacker, int Token, bool SendClients);
bool RateLimitServerInfoConnless();
void SendServerInfoConnless(const NETADDR *pAddr, int Token, int Type);
void UpdateServerInfo(bool Resend = false);

Expand Down Expand Up @@ -377,8 +390,6 @@ class CServer : public IServer
// console commands for sqlmasters
static void ConAddSqlServer(IConsole::IResult *pResult, void *pUserData);
static void ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData);

static void CreateTablesThread(void *pData);
#endif

static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
Expand Down Expand Up @@ -435,6 +446,8 @@ class CServer : public IServer
bool ErrorShutdown() const { return m_aErrorShutdownReason[0] != 0; }
void SetErrorShutdown(const char *pReason);

bool IsSixup(int ClientID) const { return m_aClients[ClientID].m_Sixup; }

#ifdef CONF_FAMILY_UNIX
enum CONN_LOGGING_CMD
{
Expand Down
9 changes: 6 additions & 3 deletions src/engine/server/sql_server.cpp
Expand Up @@ -167,11 +167,12 @@ void CSqlServer::Disconnect()
m_SqlLock.release();
}

void CSqlServer::CreateTables()
bool CSqlServer::CreateTables()
{
if (!Connect())
return;
return false;

bool Success = false;
try
{
char aBuf[1024];
Expand All @@ -186,20 +187,22 @@ void CSqlServer::CreateTables()
str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_maps (Map VARCHAR(128) BINARY NOT NULL, Server VARCHAR(32) BINARY NOT NULL, Mapper VARCHAR(128) BINARY NOT NULL, Points INT DEFAULT 0, Stars INT DEFAULT 0, Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY Map (Map)) CHARACTER SET utf8mb4;", m_aPrefix);
executeSql(aBuf);

str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_saves (Savegame TEXT CHARACTER SET utf8mb4 BINARY NOT NULL, Map VARCHAR(128) BINARY NOT NULL, Code VARCHAR(128) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, Server CHAR(4), DDNet7 BOOL DEFAULT FALSE, UNIQUE KEY (Map, Code)) CHARACTER SET utf8mb4;", m_aPrefix);
str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_saves (Savegame TEXT CHARACTER SET utf8mb4 BINARY NOT NULL, Map VARCHAR(128) BINARY NOT NULL, Code VARCHAR(128) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, Server CHAR(4), DDNet7 BOOL DEFAULT FALSE, SaveID VARCHAR(36) DEFAULT NULL, UNIQUE KEY (Map, Code)) CHARACTER SET utf8mb4;", m_aPrefix);
executeSql(aBuf);

str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_points (Name VARCHAR(%d) BINARY NOT NULL, Points INT DEFAULT 0, UNIQUE KEY Name (Name)) CHARACTER SET utf8mb4;", m_aPrefix, MAX_NAME_LENGTH);
executeSql(aBuf);

dbg_msg("sql", "Tables were created successfully");
Success = true;
}
catch (sql::SQLException &e)
{
dbg_msg("sql", "MySQL Error: %s", e.what());
}

Disconnect();
return Success;
}

void CSqlServer::executeSql(const char *pCommand)
Expand Down
3 changes: 2 additions & 1 deletion src/engine/server/sql_server.h
Expand Up @@ -17,7 +17,7 @@ class CSqlServer

bool Connect();
void Disconnect();
void CreateTables();
bool CreateTables();

void executeSql(const char *pCommand);
void executeSqlQuery(const char *pQuery);
Expand All @@ -30,6 +30,7 @@ class CSqlServer
const char* GetPass() { return m_aPass; }
const char* GetIP() { return m_aIp; }
int GetPort() { return m_Port; }
sql::Connection *Connection() const { return m_pConnection; }

static int ms_NumReadServer;
static int ms_NumWriteServer;
Expand Down
9 changes: 8 additions & 1 deletion src/engine/server/sql_string_helpers.h
@@ -1,6 +1,8 @@
#ifndef ENGINE_SERVER_SQL_STRING_HELPERS_H
#define ENGINE_SERVER_SQL_STRING_HELPERS_H

#include <base/system.h>

namespace sqlstr
{

Expand Down Expand Up @@ -30,14 +32,19 @@ class CSqlString
const char* Str() const { return m_aString; }
const char* ClrStr() const { return m_aClearString; }

CSqlString& operator = (const char *pStr)
CSqlString& operator=(const char *pStr)
{
str_copy(m_aString, pStr, size);
str_copy(m_aClearString, pStr, size);
ClearString(m_aClearString, sizeof(m_aClearString));
return *this;
}

bool operator<(const CSqlString& other) const
{
return str_comp(m_aString, other.m_aString) < 0;
}

private:
char m_aString[size];
char m_aClearString[size * 2 - 1];
Expand Down
3 changes: 2 additions & 1 deletion src/engine/serverbrowser.h
Expand Up @@ -3,6 +3,7 @@
#ifndef ENGINE_SERVERBROWSER_H
#define ENGINE_SERVERBROWSER_H

#include <engine/map.h>
#include <engine/shared/protocol.h>

#include "kernel.h"
Expand Down Expand Up @@ -53,7 +54,7 @@ class CServerInfo
int m_HasRank;
char m_aGameType[16];
char m_aName[64];
char m_aMap[32];
char m_aMap[MAX_MAP_LENGTH];
int m_MapCrc;
int m_MapSize;
char m_aVersion[32];
Expand Down
17 changes: 8 additions & 9 deletions src/engine/shared/config_variables.h
Expand Up @@ -119,6 +119,7 @@ MACRO_CONFIG_STR(Bindaddr, bindaddr, 128, "", CFGFLAG_CLIENT|CFGFLAG_SERVER|CFGF
MACRO_CONFIG_INT(SvIpv4Only, sv_ipv4only, 0, 0, 1, CFGFLAG_SERVER, "Whether to bind only to ipv4, otherwise bind to all available interfaces")
MACRO_CONFIG_INT(SvPort, sv_port, 8303, 0, 0, CFGFLAG_SERVER, "Port to use for the server (Only ports 8303-8310 work in LAN server browser)")
MACRO_CONFIG_INT(SvExternalPort, sv_external_port, 0, 0, 0, CFGFLAG_SERVER, "External port to report to the master servers")
MACRO_CONFIG_STR(SvHostname, sv_hostname, 128, "", CFGFLAG_SAVE|CFGFLAG_SERVER, "Server hostname (0.7 only)")
MACRO_CONFIG_STR(SvMap, sv_map, 128, "Kobra 4", CFGFLAG_SERVER, "Map to use on the server")
MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, MAX_CLIENTS, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server")
MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 4, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server")
Expand All @@ -144,6 +145,8 @@ MACRO_CONFIG_INT(SvPlayerDemoRecord, sv_player_demo_record, 0, 0, 1, CFGFLAG_SER
MACRO_CONFIG_INT(SvDemoChat, sv_demo_chat, 0, 0, 1, CFGFLAG_SERVER, "Record chat for demos")
MACRO_CONFIG_INT(SvServerInfoPerSecond, sv_server_info_per_second, 50, 0, 10000, CFGFLAG_SERVER, "Maximum number of complete server info responses that are sent out per second (0 for no limit)")
MACRO_CONFIG_INT(SvVanConnPerSecond, sv_van_conn_per_second, 10, 0, 10000, CFGFLAG_SERVER, "Antispoof specific ratelimit (0 for no limit)")
MACRO_CONFIG_INT(SvSixup, sv_sixup, 1, 0, 1, CFGFLAG_SERVER, "Enable sixup connections")
MACRO_CONFIG_INT(SvSkillLevel, sv_skill_level, 1, SERVERINFO_LEVEL_MIN, SERVERINFO_LEVEL_MAX, CFGFLAG_SERVER, "Difficulty level for Teeworlds 0.7 (0: Casual, 1: Normal, 2: Competitive)")

MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_ECON, "Address to bind the external console to. Anything but 'localhost' is dangerous")
MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_ECON, "Port to use for the external console")
Expand Down Expand Up @@ -181,6 +184,7 @@ MACRO_CONFIG_INT(SvPauseMessages, sv_pause_messages, 0, 0, 1, CFGFLAG_SERVER, "W
MACRO_CONFIG_INT(SvSpecFrequency, sv_pause_frequency, 1, 0, 9999, CFGFLAG_SERVER, "The minimum allowed delay between /spec")
MACRO_CONFIG_INT(SvInvite, sv_invite, 1, 0, 1, CFGFLAG_SERVER, "Whether players can invite other players to teams")
MACRO_CONFIG_INT(SvInviteFrequency, sv_invite_frequency, 1, 0, 9999, CFGFLAG_SERVER, "The minimum allowed delay between invites")
MACRO_CONFIG_INT(SvTeleOthersAuthLevel, sv_tele_others_auth_level, 1, 1, 3, CFGFLAG_SERVER, "The auth level you need to tele others")

MACRO_CONFIG_INT(SvEmotionalTees, sv_emotional_tees, 1, -1, 1, CFGFLAG_SERVER, "Whether eye change of tees is enabled with emoticons = 1, not = 0, -1 not at all")
MACRO_CONFIG_INT(SvEmoticonDelay, sv_emoticon_delay, 3, 0, 9999, CFGFLAG_SERVER, "The time in seconds between over-head emoticons")
Expand All @@ -198,7 +202,6 @@ MACRO_CONFIG_INT(SvVoteYesPercentage, sv_vote_yes_percentage, 50, 1, 100, CFGFLA
MACRO_CONFIG_INT(SvVoteMajority, sv_vote_majority, 0, 0, 1, CFGFLAG_SERVER, "Whether No. of Yes is compared to No. of No votes or to number of total Players ( Default is 0 Y compare N)")
MACRO_CONFIG_INT(SvVoteMaxTotal, sv_vote_max_total, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "How many people can participate in a vote at max (0 = no limit by default)")
MACRO_CONFIG_INT(SvVoteVetoTime, sv_vote_veto_time, 20, 0, 1000, CFGFLAG_SERVER, "Minutes of time on a server until a player can veto map change votes (0 = disabled)")
MACRO_CONFIG_INT(SvSpectatorVotes, sv_spectator_votes, 1, 0, 1, CFGFLAG_SERVER, "Choose if spectators are allowed to start votes")
MACRO_CONFIG_INT(SvKillDelay, sv_kill_delay, 1, 0, 9999, CFGFLAG_SERVER, "The minimum time in seconds between kills")
MACRO_CONFIG_INT(SvSuicidePenalty, sv_suicide_penalty, 0, 0, 9999, CFGFLAG_SERVER, "The minimum time in seconds between kill or /kills and respawn")

Expand All @@ -210,13 +213,12 @@ MACRO_CONFIG_INT(SvShotgunBulletSound, sv_shotgun_bullet_sound, 0, 0, 1, CFGFLAG
MACRO_CONFIG_INT(SvCheckpointSave, sv_checkpoint_save, 1, 0, 1, CFGFLAG_SERVER, "Whether to save checkpoint times to the score file")
MACRO_CONFIG_STR(SvScoreFolder, sv_score_folder, 32, "records", CFGFLAG_SERVER, "Folder to save score files to")

#if defined(CONF_SQL)
MACRO_CONFIG_INT(SvUseSQL, sv_use_sql, 0, 0, 1, CFGFLAG_SERVER, "Enables SQL DB instead of record file")
MACRO_CONFIG_STR(SvSqlServerName, sv_sql_servername, 5, "UNK", CFGFLAG_SERVER, "SQL Server name that is inserted into record table")
MACRO_CONFIG_STR(SvSqlValidServerNames, sv_sql_valid_servernames, 64, "UNK", CFGFLAG_SERVER, "Comma separated list of valid server names for saving a game to ([A-Z][A-Z][A-Z].?")
MACRO_CONFIG_INT(SvSaveGames, sv_savegames, 1, 0, 1, CFGFLAG_SERVER, "Enables savegames (/save and /load)")
MACRO_CONFIG_INT(SvSaveGamesDelay, sv_savegames_delay, 60, 0, 10000, CFGFLAG_SERVER, "Delay in seconds for loading a savegame")

#if defined(CONF_SQL)
MACRO_CONFIG_INT(SvUseSQL, sv_use_sql, 0, 0, 1, CFGFLAG_SERVER, "Enables SQL DB instead of record file")
MACRO_CONFIG_STR(SvSqlFailureFile, sv_sql_failure_file, 64, "failed_sql.sql", CFGFLAG_SERVER, "File to store failed Sql-Inserts (ranks)")
MACRO_CONFIG_INT(SvSqlQueriesDelay, sv_sql_queries_delay, 1, 0, 20, CFGFLAG_SERVER, "Delay in seconds between SQL queries of a single player")
#endif
Expand Down Expand Up @@ -340,9 +342,8 @@ MACRO_CONFIG_INT(ClUnpredictedShadow, cl_unpredicted_shadow, 0, -1, 1, CFGFLAG_C
MACRO_CONFIG_INT(ClPredictDDRace, cl_predict_ddrace, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict DDRace tiles and tunezones")
MACRO_CONFIG_INT(ClPredictFreeze, cl_predict_freeze, 1, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Predict freeze tiles (0 = off, 1 = on, 2 = partial (allow a small amount of movement in freeze)")
MACRO_CONFIG_INT(ClShowNinja, cl_show_ninja, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show ninja skin")
MACRO_CONFIG_INT(ClShowHookCollOther, cl_show_hook_coll_other, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show other players' hook collision line")
MACRO_CONFIG_INT(ClShowHookCollOwn, cl_show_hook_coll_own, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show own players' hook collision line")
MACRO_CONFIG_INT(ClShowHookCollAlways, cl_show_hook_coll_always, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show every players' hook collision line even if they're not using it")
MACRO_CONFIG_INT(ClShowHookCollOther, cl_show_hook_coll_other, 1, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show other players' hook collision line (2 to always show)")
MACRO_CONFIG_INT(ClShowHookCollOwn, cl_show_hook_coll_own, 1, 0, 2, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show own players' hook collision line (2 to always show)")
MACRO_CONFIG_INT(ClChatTeamColors, cl_chat_teamcolors, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Show names in chat in team colors")
MACRO_CONFIG_INT(ClChatReset, cl_chat_reset, 1, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SAVE, "Reset chat when pressing escape")
MACRO_CONFIG_INT(ClShowDirection, cl_show_direction, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Show tee direction")
Expand Down Expand Up @@ -375,6 +376,4 @@ MACRO_CONFIG_INT(GfxEnableTextureUnitOptimization, gfx_enable_texture_unit_optim
#endif
MACRO_CONFIG_INT(GfxUsePreinitBuffer, gfx_use_preinitialized_buffer, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Use only one buffer for data, that is uploaded to the GPU(might help when using an iGPUs).")

#if defined(CONF_VIDEORECORDER)
MACRO_CONFIG_INT(ClVideoRecorderFPS, cl_video_recorder_fps, 60, 1, 1000, CFGFLAG_SAVE|CFGFLAG_CLIENT, "At which FPS the videorecorder should record demos.")
#endif
18 changes: 11 additions & 7 deletions src/engine/shared/console.cpp
Expand Up @@ -39,14 +39,16 @@ float CConsole::CResult::GetFloat(unsigned Index)

ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light)
{
ColorHSLA hsl = ColorHSLA(0, 0, 0).Lighten();
ColorHSLA hsl = ColorHSLA(0, 0, 0);
if(Index >= m_NumArgs)
return hsl;

const char *pStr = m_apArgs[Index];
if(str_isallnum(pStr) || ((pStr[0] == '-' || pStr[0] == '+') && str_isallnum(pStr+1))) // Teeworlds Color (Packed HSL)
{
hsl = ColorHSLA(str_toulong_base(pStr, 10), true);
if(Light)
hsl = hsl.UnclampLighting();
}
else if(*pStr == '$') // Hex RGB
{
Expand Down Expand Up @@ -92,7 +94,7 @@ ColorHSLA CConsole::CResult::GetColor(unsigned Index, bool Light)
else if(!str_comp_nocase(pStr, "black"))
hsl = ColorHSLA(0, 0, 0);

return Light ? hsl.Lighten() : hsl;
return hsl;
}

const IConsole::CCommandInfo *CConsole::CCommand::NextCommandInfo(int AccessLevel, int FlagMask) const
Expand Down Expand Up @@ -763,7 +765,8 @@ static void ColVariableCommand(IConsole::IResult *pResult, void *pUserData)

if(pResult->NumArguments())
{
int Val = pResult->GetColor(0, pData->m_Light).Pack(pData->m_Alpha);
ColorHSLA Col = pResult->GetColor(0, pData->m_Light);
int Val = Col.Pack(pData->m_Light ? 0.5f : 0.0f, pData->m_Alpha);

*(pData->m_pVariable) = Val;
if(pResult->m_ClientID != IConsole::CLIENT_ID_GAME)
Expand All @@ -776,6 +779,8 @@ static void ColVariableCommand(IConsole::IResult *pResult, void *pUserData)
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "console", aBuf);

ColorHSLA hsl(*(pData->m_pVariable), true);
if(pData->m_Light)
hsl = hsl.UnclampLighting();
str_format(aBuf, sizeof(aBuf), "H: %d°, S: %d%%, L: %d%%", round_truncate(hsl.h * 360), round_truncate(hsl.s * 100), round_truncate(hsl.l * 100));
pData->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "console", aBuf);

Expand Down Expand Up @@ -872,13 +877,12 @@ void CConsole::ConToggle(IConsole::IResult *pResult, void *pUser)
{
CColVariableData *pData = static_cast<CColVariableData *>(pUserData);
bool Light = pData->m_Light;
float Darkest = Light ? 0.5f : 0.0f;
bool Alpha = pData->m_Alpha;
unsigned Cur = *pData->m_pVariable;
ColorHSLA Val = Cur == pResult->GetColor(1, Light).Pack(Alpha) ? pResult->GetColor(2, Light) : pResult->GetColor(1, Light);
if(Light)
Val = Val.Lighten();
ColorHSLA Val = Cur == pResult->GetColor(1, Light).Pack(Darkest, Alpha) ? pResult->GetColor(2, Light) : pResult->GetColor(1, Light);

str_format(aBuf, sizeof(aBuf), "%s %u", pResult->GetString(0), Val.Pack(Alpha));
str_format(aBuf, sizeof(aBuf), "%s %u", pResult->GetString(0), Val.Pack(Darkest, Alpha));
pConsole->ExecuteLine(aBuf);
aBuf[0] = 0;
}
Expand Down
2 changes: 1 addition & 1 deletion src/engine/shared/console.h
Expand Up @@ -119,7 +119,7 @@ class CConsole : public IConsole
virtual const char *GetString(unsigned Index);
virtual int GetInteger(unsigned Index);
virtual float GetFloat(unsigned Index);
virtual ColorHSLA GetColor(unsigned Index, bool Light = false);
virtual ColorHSLA GetColor(unsigned Index, bool Light);

// DDRace

Expand Down
40 changes: 40 additions & 0 deletions src/engine/shared/csv.cpp
@@ -0,0 +1,40 @@
#include "csv.h"

void CsvWrite(IOHANDLE File, int NumColumns, const char *const *ppColumns)
{
for(int i = 0; i < NumColumns; i++)
{
if(i != 0)
{
io_write(File, ",", 1);
}
const char *pColumn = ppColumns[i];
int ColumnLength = str_length(pColumn);
if(!str_find(pColumn, "\"") && !str_find(pColumn, ","))
{
io_write(File, pColumn, ColumnLength);
continue;
}

int Start = 0;
io_write(File, "\"", 1);
for(int j = 0; j < ColumnLength; j++)
{
if(pColumn[j] == '"')
{
if(Start != j)
{
io_write(File, pColumn + Start, j - Start);
}
Start = j + 1;
io_write(File, "\"\"", 2);
}
}
if(Start != ColumnLength)
{
io_write(File, pColumn + Start, ColumnLength - Start);
}
io_write(File, "\"", 1);
}
io_write_newline(File);
}
8 changes: 8 additions & 0 deletions src/engine/shared/csv.h
@@ -0,0 +1,8 @@
#ifndef ENGINE_SHARED_CSV_H
#define ENGINE_SHARED_CSV_H

#include <base/system.h>

void CsvWrite(IOHANDLE File, int NumColumns, const char *const *pColumns);

#endif // ENGINE_SHARED_CSV_H
10 changes: 5 additions & 5 deletions src/engine/shared/demo.cpp
Expand Up @@ -681,7 +681,7 @@ void CDemoPlayer::Pause()
m_Info.m_Info.m_Paused = 1;
#if defined(CONF_VIDEORECORDER)
if(IVideo::Current() && g_Config.m_ClVideoPauseWithDemo)
IVideo::Current()->pause(true);
IVideo::Current()->Pause(true);
#endif
}

Expand All @@ -695,7 +695,7 @@ void CDemoPlayer::Unpause()
}
#if defined(CONF_VIDEORECORDER)
if(IVideo::Current() && g_Config.m_ClVideoPauseWithDemo)
IVideo::Current()->pause(false);
IVideo::Current()->Pause(false);
#endif
}

Expand Down Expand Up @@ -884,9 +884,9 @@ int64 CDemoPlayer::time()
if (!s_Recording)
{
s_Recording = true;
m_Info.m_LastUpdate = IVideo::time();
m_Info.m_LastUpdate = IVideo::Time();
}
return IVideo::time();
return IVideo::Time();
}
else
{
Expand Down Expand Up @@ -1038,7 +1038,7 @@ int CDemoPlayer::Stop()
{
#if defined(CONF_VIDEORECORDER)
if (IVideo::Current())
IVideo::Current()->stop();
IVideo::Current()->Stop();
#endif

if(!m_File)
Expand Down
2 changes: 1 addition & 1 deletion src/engine/shared/json.cpp
Expand Up @@ -9,7 +9,7 @@ const struct _json_value *json_object_get (const json_value * object, const char
return &json_value_none;

for (i = 0; i < object->u.object.length; ++ i)
if (!strcmp (object->u.object.values [i].name, index))
if (!str_comp(object->u.object.values [i].name, index))
return object->u.object.values [i].value;

return &json_value_none;
Expand Down
106 changes: 73 additions & 33 deletions src/engine/shared/network.cpp
Expand Up @@ -41,12 +41,12 @@ int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk)
// TODO: add checking here so we don't read too far
for(int i = 0; i < m_CurrentChunk; i++)
{
pData = Header.Unpack(pData);
pData = Header.Unpack(pData, (m_pConnection && m_pConnection->m_Sixup) ? 6 : 4);
pData += Header.m_Size;
}

// unpack the header
pData = Header.Unpack(pData);
pData = Header.Unpack(pData, (m_pConnection && m_pConnection->m_Sixup) ? 6 : 4);
m_CurrentChunk++;

if(pData+Header.m_Size > pEnd)
Expand Down Expand Up @@ -110,7 +110,7 @@ void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *
net_udp_send(Socket, pAddr, aBuffer, DataSize + DATA_OFFSET);
}

void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken)
void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup, bool NoCompress)
{
unsigned char aBuffer[NET_MAX_PACKETSIZE];
int CompressedSize = -1;
Expand All @@ -126,7 +126,13 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
io_flush(ms_DataLogSent);
}

if (SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
int HeaderSize = NET_PACKETHEADERSIZE;
if (Sixup)
{
HeaderSize += 4;
mem_copy(&aBuffer[3], &SecurityToken, 4);
}
else if (SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
{
// append security token
// if SecurityToken is NET_SECURITY_TOKEN_UNKNOWN we will still append it hoping to negotiate it
Expand All @@ -135,11 +141,12 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
}

// compress
CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4);
if(!NoCompress)
CompressedSize = ms_Huffman.Compress(pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[HeaderSize], NET_MAX_PACKETSIZE-HeaderSize);

// check if the compression was enabled, successful and good enough
#ifndef FUZZING
if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize)
if(!NoCompress && CompressedSize > 0 && CompressedSize < pPacket->m_DataSize)
{
FinalSize = CompressedSize;
pPacket->m_Flags |= NET_PACKETFLAG_COMPRESSION;
Expand All @@ -149,15 +156,24 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
{
// use uncompressed data
FinalSize = pPacket->m_DataSize;
mem_copy(&aBuffer[3], pPacket->m_aChunkData, pPacket->m_DataSize);
mem_copy(&aBuffer[HeaderSize], pPacket->m_aChunkData, pPacket->m_DataSize);
pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION;
}

if(Sixup)
{
unsigned Flags = 0;
if (pPacket->m_Flags&NET_PACKETFLAG_CONTROL) Flags |= 1;
if (pPacket->m_Flags&NET_PACKETFLAG_RESEND) Flags |= 2;
if (pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION) Flags |= 4;
pPacket->m_Flags = Flags;
}

// set header and send the packet if all things are good
if(FinalSize >= 0)
{
FinalSize += NET_PACKETHEADERSIZE;
aBuffer[0] = ((pPacket->m_Flags<<4)&0xf0)|((pPacket->m_Ack>>8)&0xf);
FinalSize += HeaderSize;
aBuffer[0] = ((pPacket->m_Flags<<2)&0xfc)|((pPacket->m_Ack>>8)&0x3);
aBuffer[1] = pPacket->m_Ack&0xff;
aBuffer[2] = pPacket->m_NumChunks;
net_udp_send(Socket, pAddr, aBuffer, FinalSize);
Expand All @@ -175,7 +191,7 @@ void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct
}

// TODO: rename this function
int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket)
int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, bool& Sixup, SECURITY_TOKEN *SecurityToken, SECURITY_TOKEN *ResponseToken)
{
// check the size
if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE)
Expand All @@ -195,45 +211,70 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct
}

// read the packet
pPacket->m_Flags = pBuffer[0]>>4;
pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1];
pPacket->m_NumChunks = pBuffer[2];
pPacket->m_DataSize = Size - NET_PACKETHEADERSIZE;
pPacket->m_Flags = pBuffer[0]>>2;

if(pPacket->m_Flags&NET_PACKETFLAG_CONNLESS)
{
const int DATA_OFFSET = 6;
if(Size < DATA_OFFSET)
{
//dbg_msg("", "connection less packet too small, %d", Size);
if(Size < 1)
return -1;

Sixup = (pBuffer[0]&0x3) == 1;
int Offset = Sixup ? 9 : 6;
if(Size < Offset)
return -1;

if(Sixup)
{
mem_copy(SecurityToken, &pBuffer[1], 4);
mem_copy(ResponseToken, &pBuffer[5], 4);
}

pPacket->m_Flags = NET_PACKETFLAG_CONNLESS;
pPacket->m_Ack = 0;
pPacket->m_NumChunks = 0;
pPacket->m_DataSize = Size - DATA_OFFSET;
mem_copy(pPacket->m_aChunkData, pBuffer + DATA_OFFSET, pPacket->m_DataSize);
pPacket->m_DataSize = Size - Offset;
mem_copy(pPacket->m_aChunkData, pBuffer + Offset, pPacket->m_DataSize);

if(mem_comp(pBuffer, NET_HEADER_EXTENDED, sizeof(NET_HEADER_EXTENDED)) == 0)
if(!Sixup && mem_comp(pBuffer, NET_HEADER_EXTENDED, sizeof(NET_HEADER_EXTENDED)) == 0)
{
pPacket->m_Flags |= NET_PACKETFLAG_EXTENDED;
mem_copy(pPacket->m_aExtraData, pBuffer + sizeof(NET_HEADER_EXTENDED), sizeof(pPacket->m_aExtraData));
}
}
else
{
if(pPacket->m_Flags&NET_PACKETFLAG_UNUSED)
Sixup = true;
int DataStart = Sixup ? 7 : NET_PACKETHEADERSIZE;
if(Size < DataStart)
return -1;

pPacket->m_Ack = ((pBuffer[0]&0x3)<<8) | pBuffer[1];
pPacket->m_NumChunks = pBuffer[2];
pPacket->m_DataSize = Size - DataStart;

if(Sixup)
{
unsigned Flags = 0;
if (pPacket->m_Flags&1) Flags |= NET_PACKETFLAG_CONTROL;
if (pPacket->m_Flags&2) Flags |= NET_PACKETFLAG_RESEND;
if (pPacket->m_Flags&4) Flags |= NET_PACKETFLAG_COMPRESSION;
pPacket->m_Flags = Flags;

mem_copy(SecurityToken, &pBuffer[3], 4);
}

if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION)
{
// Don't allow compressed control packets.
if(pPacket->m_Flags&NET_PACKETFLAG_CONTROL)
{
return -1;
}
pPacket->m_DataSize = ms_Huffman.Decompress(&pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData));
pPacket->m_DataSize = ms_Huffman.Decompress(&pBuffer[DataStart], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData));
}
else
mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize);
mem_copy(pPacket->m_aChunkData, &pBuffer[DataStart], pPacket->m_DataSize);
}

// check for errors
Expand All @@ -259,7 +300,7 @@ int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct
}


void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken)
void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup)
{
CNetPacketConstruct Construct;
Construct.m_Flags = NET_PACKETFLAG_CONTROL;
Expand All @@ -270,32 +311,31 @@ void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int Con
mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize);

// send the control message
CNetBase::SendPacket(Socket, pAddr, &Construct, SecurityToken);
CNetBase::SendPacket(Socket, pAddr, &Construct, SecurityToken, Sixup, true);
}



unsigned char *CNetChunkHeader::Pack(unsigned char *pData)
unsigned char *CNetChunkHeader::Pack(unsigned char *pData, int split)
{
pData[0] = ((m_Flags&3)<<6)|((m_Size>>4)&0x3f);
pData[1] = (m_Size&0xf);
pData[0] = ((m_Flags&3)<<6)|((m_Size>>split)&0x3f);
pData[1] = (m_Size&((1<<split)-1));
if(m_Flags&NET_CHUNKFLAG_VITAL)
{
pData[1] |= (m_Sequence>>2)&0xf0;
pData[1] |= (m_Sequence>>2)&(~((1<<split)-1));
pData[2] = m_Sequence&0xff;
return pData + 3;
}
return pData + 2;
}

unsigned char *CNetChunkHeader::Unpack(unsigned char *pData)
unsigned char *CNetChunkHeader::Unpack(unsigned char *pData, int split)
{
m_Flags = (pData[0]>>6)&3;
m_Size = ((pData[0]&0x3f)<<4) | (pData[1]&0xf);
m_Size = ((pData[0]&0x3f)<<split) | (pData[1]&((1<<split)-1));
m_Sequence = -1;
if(m_Flags&NET_CHUNKFLAG_VITAL)
{
m_Sequence = ((pData[1]&0xf0)<<2) | pData[2];
m_Sequence = ((pData[1]&(~((1<<split)-1)))<<2) | pData[2];
return pData + 3;
}
return pData + 2;
Expand Down
51 changes: 32 additions & 19 deletions src/engine/shared/network.h
Expand Up @@ -14,7 +14,11 @@
CURRENT:
packet header: 3 bytes
unsigned char flags_ack; // 4bit flags, 4bit ack
unsigned char flags_ack; // 6bit flags, 2bit ack
0.6: ORNCaaAA
0.6.5: ORNCTUAA
0.7: --NORCAA
unsigned char ack; // 8 bit ack
unsigned char num_chunks; // 8 bit chunks
Expand Down Expand Up @@ -64,12 +68,14 @@ enum
NET_CONNSTATE_ONLINE=3,
NET_CONNSTATE_ERROR=4,

NET_PACKETFLAG_CONTROL=1,
NET_PACKETFLAG_CONNLESS=2,
NET_PACKETFLAG_RESEND=4,
NET_PACKETFLAG_COMPRESSION=8,
NET_PACKETFLAG_UNUSED=1<<0,
NET_PACKETFLAG_TOKEN=1<<1,
NET_PACKETFLAG_CONTROL=1<<2,
NET_PACKETFLAG_CONNLESS=1<<3,
NET_PACKETFLAG_RESEND=1<<4,
NET_PACKETFLAG_COMPRESSION=1<<5,
// NOT SENT VIA THE NETWORK DIRECTLY:
NET_PACKETFLAG_EXTENDED=16,
NET_PACKETFLAG_EXTENDED=1<<6,

NET_CHUNKFLAG_VITAL=1,
NET_CHUNKFLAG_RESEND=2,
Expand Down Expand Up @@ -100,7 +106,8 @@ enum
};

typedef int (*NETFUNC_DELCLIENT)(int ClientID, const char *pReason, void *pUser);
typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser);
typedef int (*NETFUNC_NEWCLIENT_CON)(int ClientID, void *pUser);
typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser, bool Sixup);
typedef int (*NETFUNC_NEWCLIENT_NOAUTH)(int ClientID, void *pUser);
typedef int (*NETFUNC_CLIENTREJOIN)(int ClientID, void *pUser);

Expand All @@ -124,8 +131,8 @@ class CNetChunkHeader
int m_Size;
int m_Sequence;

unsigned char *Pack(unsigned char *pData);
unsigned char *Unpack(unsigned char *pData);
unsigned char *Pack(unsigned char *pData, int split = 4);
unsigned char *Unpack(unsigned char *pData, int split = 4);
};

class CNetChunkResend
Expand Down Expand Up @@ -164,7 +171,6 @@ class CNetConnection
unsigned short m_PeerAck;
unsigned m_State;

int m_Token;
SECURITY_TOKEN m_SecurityToken;
int m_RemoteClosed;
bool m_BlockCloseMsg;
Expand Down Expand Up @@ -228,12 +234,15 @@ class CNetConnection
int SecurityToken() const { return m_SecurityToken; }
TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *ResendBuffer() { return &m_Buffer; };

void SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer);
void SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer, bool Sixup);

// anti spoof
void DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken);
void DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup);
void SetUnknownSeq() { m_UnknownSeq = true; }
void SetSequence(int Sequence) { m_Sequence = Sequence; }

bool m_Sixup;
SECURITY_TOKEN m_Token;
};

class CConsoleNetConnection
Expand Down Expand Up @@ -327,13 +336,14 @@ class CNetServer
CNetRecvUnpacker m_RecvUnpacker;

void OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketConstruct &Packet);
int OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token);
void OnPreConnMsg(NETADDR &Addr, CNetPacketConstruct &Packet);
void OnConnCtrlMsg(NETADDR &Addr, int ClientID, int ControlMsg, const CNetPacketConstruct &Packet);
bool ClientExists(const NETADDR &Addr) { return GetClientSlot(Addr) != -1; };
int GetClientSlot(const NETADDR &Addr);
void SendControl(NETADDR &Addr, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);

int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth=false);
int TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth=false, bool Sixup=false, SECURITY_TOKEN Token=0);
int NumClientsWithAddr(NETADDR Addr);
bool Connlimit(NETADDR Addr);
void SendMsgs(NETADDR &Addr, const CMsgPacker *Msgs[], int num);
Expand All @@ -347,7 +357,7 @@ class CNetServer
int Close();

//
int Recv(CNetChunk *pChunk);
int Recv(CNetChunk *pChunk, SECURITY_TOKEN *ResponseToken);
int Send(CNetChunk *pChunk);
int Update();

Expand All @@ -362,6 +372,9 @@ class CNetServer
int NetType() const { return m_Socket.type; }
int MaxClients() const { return m_MaxClients; }

void SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token);
int SendConnlessSixup(CNetChunk *pChunk, SECURITY_TOKEN ResponseToken);

//
void SetMaxClientsPerIP(int Max);
bool SetTimedOut(int ClientID, int OrigID);
Expand All @@ -387,14 +400,14 @@ class CNetConsole
class CNetBan *m_pNetBan;
CSlot m_aSlots[NET_MAX_CONSOLE_CLIENTS];

NETFUNC_NEWCLIENT m_pfnNewClient;
NETFUNC_NEWCLIENT_CON m_pfnNewClient;
NETFUNC_DELCLIENT m_pfnDelClient;
void *m_UserPtr;

CNetRecvUnpacker m_RecvUnpacker;

public:
void SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);
void SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser);

//
bool Open(NETADDR BindAddr, class CNetBan *pNetBan, int Flags);
Expand Down Expand Up @@ -466,11 +479,11 @@ class CNetBase
static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize);
static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize);

static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken);
static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize, SECURITY_TOKEN SecurityToken, bool Sixup = false);
static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize, bool Extended, unsigned char aExtra[4]);
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken);
static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket, SECURITY_TOKEN SecurityToken, bool Sixup = false, bool NoCompress = false);

static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket);
static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket, bool& Sixup, SECURITY_TOKEN *SecurityToken = 0, SECURITY_TOKEN *ResponseToken = 0);

// The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not
static int IsSeqInBackroom(int Seq, int Ack);
Expand Down
3 changes: 2 additions & 1 deletion src/engine/shared/network_client.cpp
Expand Up @@ -73,7 +73,8 @@ int CNetClient::Recv(CNetChunk *pChunk)
if(Bytes <= 0)
break;

if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data) == 0)
bool Sixup = false;
if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data, Sixup) == 0)
{
if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS)
{
Expand Down
21 changes: 14 additions & 7 deletions src/engine/shared/network_conn.cpp
Expand Up @@ -31,6 +31,7 @@ void CNetConnection::Reset(bool Rejoin)
m_State = NET_CONNSTATE_OFFLINE;
m_Token = -1;
m_SecurityToken = NET_SECURITY_TOKEN_UNKNOWN;
m_Sixup = false;
}

m_LastSendTime = 0;
Expand Down Expand Up @@ -93,7 +94,7 @@ int CNetConnection::Flush()

// send of the packets
m_Construct.m_Ack = m_Ack;
CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct, m_SecurityToken);
CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct, m_SecurityToken, m_Sixup);

// update send times
m_LastSendTime = time_get();
Expand All @@ -120,7 +121,7 @@ int CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int
Header.m_Size = DataSize;
Header.m_Sequence = Sequence;
pChunkData = &m_Construct.m_aChunkData[m_Construct.m_DataSize];
pChunkData = Header.Pack(pChunkData);
pChunkData = Header.Pack(pChunkData, m_Sixup ? 6 : 4);
mem_copy(pChunkData, pData, DataSize);
pChunkData += DataSize;

Expand Down Expand Up @@ -165,7 +166,7 @@ void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSi
{
// send the control message
m_LastSendTime = time_get();
CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize, m_SecurityToken);
CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize, m_SecurityToken, m_Sixup);
}

void CNetConnection::ResendChunk(CNetChunkResend *pResend)
Expand Down Expand Up @@ -220,7 +221,7 @@ void CNetConnection::Disconnect(const char *pReason)
Reset();
}

void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken)
void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken, SECURITY_TOKEN Token, bool Sixup)
{
Reset();

Expand All @@ -235,11 +236,13 @@ void CNetConnection::DirectInit(NETADDR &Addr, SECURITY_TOKEN SecurityToken)
m_LastUpdateTime = Now;

m_SecurityToken = SecurityToken;
m_Token = Token;
m_Sixup = Sixup;
}

int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_TOKEN SecurityToken)
{
if (State() != NET_CONNSTATE_OFFLINE && m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
if (!m_Sixup && State() != NET_CONNSTATE_OFFLINE && m_SecurityToken != NET_SECURITY_TOKEN_UNKNOWN && m_SecurityToken != NET_SECURITY_TOKEN_UNSUPPORTED)
{
// supposed to have a valid token in this packet, check it
if (pPacket->m_DataSize < (int)sizeof(m_SecurityToken))
Expand All @@ -253,6 +256,9 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_
}
}

if(m_Sixup && SecurityToken != m_Token)
return 0;

// check if actual ack value is valid(own sequence..latest peer ack)
if(m_Sequence >= m_PeerAck)
{
Expand Down Expand Up @@ -329,7 +335,7 @@ int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr, SECURITY_
&& pPacket->m_DataSize >= (int)(1 + sizeof(SECURITY_TOKEN_MAGIC) + sizeof(m_SecurityToken))
&& !mem_comp(&pPacket->m_aChunkData[1], SECURITY_TOKEN_MAGIC, sizeof(SECURITY_TOKEN_MAGIC)))
{
m_SecurityToken = SecurityToken;
m_SecurityToken = NET_SECURITY_TOKEN_UNSUPPORTED;
if(g_Config.m_Debug)
dbg_msg("security", "generated token %d", m_SecurityToken);
}
Expand Down Expand Up @@ -466,7 +472,7 @@ int CNetConnection::Update()
return 0;
}

void CNetConnection::SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer)
void CNetConnection::SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SECURITY_TOKEN SecurityToken, TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> *pResendBuffer, bool Sixup)
{
int64 Now = time_get();

Expand All @@ -481,6 +487,7 @@ void CNetConnection::SetTimedOut(const NETADDR *pAddr, int Sequence, int Ack, SE
m_LastRecvTime = Now;
m_LastUpdateTime = Now;
m_SecurityToken = SecurityToken;
m_Sixup = Sixup;

// copy resend buffer
m_Buffer.Init();
Expand Down
2 changes: 1 addition & 1 deletion src/engine/shared/network_console.cpp
Expand Up @@ -31,7 +31,7 @@ bool CNetConsole::Open(NETADDR BindAddr, CNetBan *pNetBan, int Flags)
return true;
}

void CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
void CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser)
{
m_pfnNewClient = pfnNewClient;
m_pfnDelClient = pfnDelClient;
Expand Down
113 changes: 100 additions & 13 deletions src/engine/shared/network_server.cpp
Expand Up @@ -10,6 +10,7 @@
#include "network.h"
#include <engine/message.h>
#include <engine/shared/protocol.h>
#include <game/generated/protocol.h>

const int DummyMapCrc = 0x6c760ac4;
unsigned char g_aDummyMapData[] = {
Expand Down Expand Up @@ -41,7 +42,6 @@ unsigned char g_aDummyMapData[] = {
0xc2, 0x00, 0x00, 0x38, 0x00, 0x05
};


static SECURITY_TOKEN ToSecurityToken(const unsigned char *pData)
{
return (int)pData[0] | (pData[1] << 8) | (pData[2] << 16) | (pData[3] << 24);
Expand Down Expand Up @@ -147,7 +147,7 @@ SECURITY_TOKEN CNetServer::GetToken(const NETADDR &Addr)
SHA256_CTX Sha256;
sha256_init(&Sha256);
sha256_update(&Sha256, (unsigned char*)m_SecurityTokenSeed, sizeof(m_SecurityTokenSeed));
sha256_update(&Sha256, (unsigned char*)&Addr, sizeof(Addr));
sha256_update(&Sha256, (unsigned char*)&Addr, sizeof(20)); //omit port, bad idea?

SECURITY_TOKEN SecurityToken = ToSecurityToken(sha256_finish(&Sha256).data);

Expand Down Expand Up @@ -214,12 +214,19 @@ bool CNetServer::Connlimit(NETADDR Addr)
return false;
}

int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth)
int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, bool VanillaAuth, bool Sixup, SECURITY_TOKEN Token)
{
if(Sixup && !g_Config.m_SvSixup)
{
const char Msg[] = "0.7 connections are not accepted at this time";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, Msg, sizeof(Msg), SecurityToken, Sixup);
return -1; // failed to add client?
}

if (Connlimit(Addr))
{
const char Msg[] = "Too many connections in a short time";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, Msg, sizeof(Msg), SecurityToken);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, Msg, sizeof(Msg), SecurityToken, Sixup);
return -1; // failed to add client
}

Expand All @@ -228,7 +235,7 @@ int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, boo
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "Only %d players with the same IP are allowed", m_MaxClientsPerIP);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1, SecurityToken);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, aBuf, str_length(aBuf) + 1, SecurityToken, Sixup);
return -1; // failed to add client
}

Expand All @@ -245,13 +252,13 @@ int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, boo
if (Slot == -1)
{
const char FullMsg[] = "This server is full";
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg), SecurityToken);
CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg), SecurityToken, Sixup);

return -1; // failed to add client
}

// init connection slot
m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken);
m_aSlots[Slot].m_Connection.DirectInit(Addr, SecurityToken, Token, Sixup);

if (VanillaAuth)
{
Expand All @@ -273,7 +280,7 @@ int CNetServer::TryAcceptClient(NETADDR &Addr, SECURITY_TOKEN SecurityToken, boo
if (VanillaAuth)
m_pfnNewClientNoAuth(Slot, m_UserPtr);
else
m_pfnNewClient(Slot, m_UserPtr);
m_pfnNewClient(Slot, m_UserPtr, Sixup);

return Slot; // done
}
Expand Down Expand Up @@ -546,6 +553,42 @@ void CNetServer::OnTokenCtrlMsg(NETADDR &Addr, int ControlMsg, const CNetPacketC
}
}

int CNetServer::OnSixupCtrlMsg(NETADDR &Addr, CNetChunk *pChunk, int ControlMsg, const CNetPacketConstruct &Packet, SECURITY_TOKEN &ResponseToken, SECURITY_TOKEN Token)
{
if(m_RecvUnpacker.m_Data.m_DataSize < 5 || ClientExists(Addr))
return 0; // silently ignore

mem_copy(&ResponseToken, Packet.m_aChunkData+1, 4);

if(ControlMsg == 5)
{
if(m_RecvUnpacker.m_Data.m_DataSize >= 512)
{
SendTokenSixup(Addr, ResponseToken);
return 0;
}

// Is this behaviour safe to rely on?
pChunk->m_Flags = 0;
pChunk->m_ClientID = -1;
pChunk->m_Address = Addr;
pChunk->m_DataSize = 0;
return 1;
}
else if(ControlMsg == NET_CTRLMSG_CONNECT)
{
SECURITY_TOKEN MyToken = GetToken(Addr);
unsigned char aToken[4];
mem_copy(aToken, &MyToken, 4);

CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CONNECTACCEPT, aToken, sizeof(aToken), ResponseToken, true);
if(Token == MyToken)
TryAcceptClient(Addr, ResponseToken, false, true, Token);
}

return 0;
}

int CNetServer::GetClientSlot(const NETADDR &Addr)
{
int Slot = -1;
Expand Down Expand Up @@ -590,7 +633,7 @@ static bool IsDDNetControlMsg(const CNetPacketConstruct *pPacket)
/*
TODO: chopp up this function into smaller working parts
*/
int CNetServer::Recv(CNetChunk *pChunk)
int CNetServer::Recv(CNetChunk *pChunk, SECURITY_TOKEN *ResponseToken)
{
while(1)
{
Expand All @@ -617,10 +660,16 @@ int CNetServer::Recv(CNetChunk *pChunk)
continue;
}

if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data) == 0)
SECURITY_TOKEN Token;
bool Sixup = false;
*ResponseToken = NET_SECURITY_TOKEN_UNKNOWN;
if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data, Sixup, &Token, ResponseToken) == 0)
{
if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS)
{
if(Sixup && Token != GetToken(Addr))
continue;

pChunk->m_Flags = NETSENDFLAG_CONNLESS;
pChunk->m_ClientID = -1;
pChunk->m_Address = Addr;
Expand All @@ -643,6 +692,13 @@ int CNetServer::Recv(CNetChunk *pChunk)
// normal packet, find matching slot
int Slot = GetClientSlot(Addr);

if(!Sixup && Slot != -1 && m_aSlots[Slot].m_Connection.m_Sixup)
{
Sixup = true;
if(CNetBase::UnpackPacket(pData, Bytes, &m_RecvUnpacker.m_Data, Sixup, &Token))
continue;
}

if (Slot != -1)
{
// found
Expand All @@ -651,7 +707,7 @@ int CNetServer::Recv(CNetChunk *pChunk)
if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL)
OnConnCtrlMsg(Addr, Slot, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data);

if(m_aSlots[Slot].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr))
if(m_aSlots[Slot].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr, Token))
{
if(m_RecvUnpacker.m_Data.m_DataSize)
m_RecvUnpacker.Start(&Addr, &m_aSlots[Slot].m_Connection, Slot);
Expand All @@ -661,7 +717,13 @@ int CNetServer::Recv(CNetChunk *pChunk)
{
// not found, client that wants to connect

if(IsDDNetControlMsg(&m_RecvUnpacker.m_Data))
if(Sixup)
{
// got 0.7 control msg
if(OnSixupCtrlMsg(Addr, pChunk, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data, *ResponseToken, Token) == 1)
return 1;
}
else if(IsDDNetControlMsg(&m_RecvUnpacker.m_Data))
// got ddnet control msg
OnTokenCtrlMsg(Addr, m_RecvUnpacker.m_Data.m_aChunkData[0], m_RecvUnpacker.m_Data);
else
Expand Down Expand Up @@ -710,6 +772,31 @@ int CNetServer::Send(CNetChunk *pChunk)
return 0;
}

void CNetServer::SendTokenSixup(NETADDR &Addr, SECURITY_TOKEN Token)
{
SECURITY_TOKEN MyToken = GetToken(Addr);
unsigned char aBuf[512] = {};
mem_copy(aBuf, &MyToken, 4);
int Size = (Token == NET_SECURITY_TOKEN_UNKNOWN) ? 512 : 4;
CNetBase::SendControlMsg(m_Socket, &Addr, 0, 5, aBuf, Size, Token, true);
}

int CNetServer::SendConnlessSixup(CNetChunk *pChunk, SECURITY_TOKEN ResponseToken)
{
if(pChunk->m_DataSize > NET_MAX_PACKETSIZE - 9)
return -1;

unsigned char aBuffer[NET_MAX_PACKETSIZE];
aBuffer[0] = NET_PACKETFLAG_CONNLESS<<2 | 1;
SECURITY_TOKEN Token = GetToken(pChunk->m_Address);
mem_copy(aBuffer+1, &ResponseToken, 4);
mem_copy(aBuffer+5, &Token, 4);
mem_copy(aBuffer+9, pChunk->m_pData, pChunk->m_DataSize);
net_udp_send(m_Socket, &pChunk->m_Address, aBuffer, pChunk->m_DataSize + 9);

return 0;
}

void CNetServer::SetMaxClientsPerIP(int Max)
{
// clamp
Expand All @@ -726,7 +813,7 @@ bool CNetServer::SetTimedOut(int ClientID, int OrigID)
if (m_aSlots[ClientID].m_Connection.State() != NET_CONNSTATE_ERROR)
return false;

m_aSlots[ClientID].m_Connection.SetTimedOut(ClientAddr(OrigID), m_aSlots[OrigID].m_Connection.SeqSequence(), m_aSlots[OrigID].m_Connection.AckSequence(), m_aSlots[OrigID].m_Connection.SecurityToken(), m_aSlots[OrigID].m_Connection.ResendBuffer());
m_aSlots[ClientID].m_Connection.SetTimedOut(ClientAddr(OrigID), m_aSlots[OrigID].m_Connection.SeqSequence(), m_aSlots[OrigID].m_Connection.AckSequence(), m_aSlots[OrigID].m_Connection.SecurityToken(), m_aSlots[OrigID].m_Connection.ResendBuffer(), m_aSlots[OrigID].m_Connection.m_Sixup);
m_aSlots[OrigID].m_Connection.Reset();
return true;
}
Expand Down
13 changes: 13 additions & 0 deletions src/engine/shared/packer.cpp
Expand Up @@ -114,6 +114,19 @@ int CUnpacker::GetInt()
return i;
}

int CUnpacker::GetIntOrDefault(int Default)
{
if(m_Error)
{
return 0;
}
if(m_pCurrent == m_pEnd)
{
return Default;
}
return GetInt();
}

const char *CUnpacker::GetString(int SanitizeType)
{
if(m_Error)
Expand Down
1 change: 1 addition & 0 deletions src/engine/shared/packer.h
Expand Up @@ -43,6 +43,7 @@ class CUnpacker

void Reset(const void *pData, int Size);
int GetInt();
int GetIntOrDefault(int Default);
const char *GetString(int SanitizeType = SANITIZE);
const unsigned char *GetRaw(int Size);
bool Error() const { return m_Error; }
Expand Down
5 changes: 4 additions & 1 deletion src/engine/shared/protocol.h
Expand Up @@ -78,7 +78,10 @@ enum
enum
{
SERVER_TICK_SPEED=50,
SERVER_FLAG_PASSWORD = 0x1,
SERVER_FLAG_PASSWORD = 1<<0,
SERVER_FLAG_TIMESCORE = 1<<1,
SERVERINFO_LEVEL_MIN = 0,
SERVERINFO_LEVEL_MAX = 2,

MAX_CLIENTS=64,
VANILLA_MAX_CLIENTS=16,
Expand Down
17 changes: 16 additions & 1 deletion src/engine/shared/snapshot.cpp
Expand Up @@ -4,6 +4,9 @@
#include "compression.h"
#include "uuid_manager.h"

#include <game/generated/protocol.h>
#include <game/generated/protocolglue.h>

// CSnapshot

CSnapshotItem *CSnapshot::GetItem(int Index)
Expand Down Expand Up @@ -529,10 +532,11 @@ CSnapshotBuilder::CSnapshotBuilder()
m_NumExtendedItemTypes = 0;
}

void CSnapshotBuilder::Init()
void CSnapshotBuilder::Init(bool Sixup)
{
m_DataSize = 0;
m_NumItems = 0;
m_Sixup = Sixup;

for(int i = 0; i < m_NumExtendedItemTypes; i++)
{
Expand All @@ -558,6 +562,7 @@ int *CSnapshotBuilder::GetItemData(int Key)

int CSnapshotBuilder::Finish(void *SpnapData)
{
//dbg_msg("snap", "---------------------------");
// flattern and make the snapshot
CSnapshot *pSnap = (CSnapshot *)SpnapData;
int OffsetSize = sizeof(int)*m_NumItems;
Expand Down Expand Up @@ -622,6 +627,16 @@ void *CSnapshotBuilder::NewItem(int Type, int ID, int Size)

CSnapshotItem *pObj = (CSnapshotItem *)(m_aData + m_DataSize);

if(m_Sixup)
{
if(Type >= 0)
Type = Obj_SixToSeven(Type);
else
Type *= -1;

if(Type < 0) return pObj;
}

mem_zero(pObj, sizeof(CSnapshotItem) + Size);
pObj->m_TypeAndID = (Type<<16)|ID;
m_aOffsets[m_NumItems] = m_DataSize;
Expand Down
4 changes: 3 additions & 1 deletion src/engine/shared/snapshot.h
Expand Up @@ -139,10 +139,12 @@ class CSnapshotBuilder
void AddExtendedItemType(int Index);
int GetExtendedItemTypeIndex(int TypeID);

bool m_Sixup;

public:
CSnapshotBuilder();

void Init();
void Init(bool Sixup = false);

void *NewItem(int Type, int ID, int Size);

Expand Down
6 changes: 6 additions & 0 deletions src/engine/shared/teehistorian_ex_chunks.h
Expand Up @@ -6,3 +6,9 @@ UUID(TEEHISTORIAN_DDNETVER, "teehistorian-ddnetver@ddnet.tw")
UUID(TEEHISTORIAN_AUTH_INIT, "teehistorian-auth-init@ddnet.tw")
UUID(TEEHISTORIAN_AUTH_LOGIN, "teehistorian-auth-login@ddnet.tw")
UUID(TEEHISTORIAN_AUTH_LOGOUT, "teehistorian-auth-logout@ddnet.tw")
UUID(TEEHISTORIAN_JOINVER6, "teehistorian-joinver6@ddnet.tw")
UUID(TEEHISTORIAN_JOINVER7, "teehistorian-joinver7@ddnet.tw")
UUID(TEEHISTORIAN_SAVE_SUCCESS, "teehistorian-save-success@ddnet.tw")
UUID(TEEHISTORIAN_SAVE_FAILURE, "teehistorian-save-failure@ddnet.tw")
UUID(TEEHISTORIAN_LOAD_SUCCESS, "teehistorian-load-success@ddnet.tw")
UUID(TEEHISTORIAN_LOAD_FAILURE, "teehistorian-load-failure@ddnet.tw")
23 changes: 12 additions & 11 deletions src/engine/shared/video.h
Expand Up @@ -8,25 +8,26 @@ class IVideo
public:
virtual ~IVideo() {};

virtual void start() = 0;
virtual void stop() = 0;
virtual void pause(bool p) = 0;
virtual void Start() = 0;
virtual void Stop() = 0;
virtual void Pause(bool Pause) = 0;
virtual bool IsRecording() = 0;

virtual void nextVideoFrame() = 0;
virtual bool frameRendered() = 0;
virtual void nextVideoFrame_thread() = 0;
virtual void NextVideoFrame() = 0;
virtual bool FrameRendered() = 0;
virtual void NextVideoFrameThread() = 0;

virtual void nextAudioFrame(void (*Mix)(short *pFinalOut, unsigned Frames)) = 0;
virtual bool aframeRendered() = 0;
virtual void nextAudioFrame_timeline() = 0;
virtual void NextAudioFrame(void (*Mix)(short *pFinalOut, unsigned Frames)) = 0;
virtual bool AudioFrameRendered() = 0;
virtual void NextAudioFrameTimeline() = 0;


static IVideo* Current() { return ms_pCurrentVideo; }

static int64 time() { return ms_Time; }
static int64 Time() { return ms_Time; }
static float LocalTime() { return ms_LocalTime; }
static void SetLocalStartTime(int64 LocalStartTime) { ms_LocalStartTime = LocalStartTime; }
static void SetFPS(int fps) { ms_TickTime = time_freq() / fps; }
static void SetFPS(int FPS) { ms_TickTime = time_freq() / FPS; }

protected:
static IVideo* ms_pCurrentVideo;
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/component.h
Expand Up @@ -35,7 +35,7 @@ class CComponent
class IUpdater *Updater() const { return m_pClient->Updater(); }

#if defined(CONF_VIDEORECORDER)
int64 time() const { return IVideo::Current() ? IVideo::time() : time_get(); }
int64 time() const { return IVideo::Current() ? IVideo::Time() : time_get(); }
float LocalTime() const { return IVideo::Current() ? IVideo::LocalTime() : Client()->LocalTime(); }
#else
int64 time() const { return time_get(); }
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/components/background.h
Expand Up @@ -10,7 +10,7 @@ class CBackground : public CMapLayers
{
IEngineMap *m_pMap;
bool m_Loaded;
char m_aMapName[128];
char m_aMapName[MAX_MAP_LENGTH];

//to avoid spam when in menu
int64 m_LastLoad;
Expand Down
63 changes: 63 additions & 0 deletions src/game/client/components/chat.cpp
Expand Up @@ -9,6 +9,7 @@
#include <engine/textrender.h>
#include <engine/keys.h>
#include <engine/shared/config.h>
#include <engine/shared/csv.h>

#include <game/generated/protocol.h>
#include <game/generated/client_data.h>
Expand Down Expand Up @@ -543,6 +544,65 @@ bool CChat::LineShouldHighlight(const char *pLine, const char *pName)
return false;
}

#define SAVES_FILE "ddnet-saves.txt"
const char *SAVES_HEADER[] = {
"Time",
"Player",
"Map",
"Code",
};

void CChat::StoreSave(const char *pText)
{
const char *pStart = str_find(pText, "Team successfully saved by ");
const char *pMid = str_find(pText, ". Use '/load ");
const char *pOn = str_find(pText, "' on ");
const char *pEnd = str_find(pText, pOn ? " to continue" : "' to continue");

if(!pStart || !pMid || !pEnd || pMid < pStart || pEnd < pMid || (pOn && (pOn < pMid || pEnd < pOn)))
return;

char aName[16];
str_copy(aName, pStart + 27, minimum(static_cast<size_t>(pMid - pStart - 26), sizeof(aName)));

char aSaveCode[64];

str_copy(aSaveCode, pMid + 13, minimum(static_cast<size_t>((pOn ? pOn : pEnd) - pMid - 12), sizeof(aSaveCode)));

char aTimestamp[20];
str_timestamp_format(aTimestamp, sizeof(aTimestamp), FORMAT_SPACE);

// TODO: Find a simple way to get the names of team members. This doesn't
// work since team is killed first, then save message gets sent:
/*
for(int i = 0; i < MAX_CLIENTS; i++)
{
const CNetObj_PlayerInfo *pInfo = GameClient()->m_Snap.m_paInfoByDDTeam[i];
if(!pInfo)
continue;
pInfo->m_Team // All 0
}
*/

IOHANDLE File = Storage()->OpenFile(SAVES_FILE, IOFLAG_APPEND, IStorage::TYPE_SAVE);
if(!File)
return;

const char *apColumns[4] = {
aTimestamp,
aName,
Client()->GetCurrentMap(),
aSaveCode,
};

if(io_tell(File) == 0)
{
CsvWrite(File, 4, SAVES_HEADER);
}
CsvWrite(File, 4, apColumns);
io_close(File);
}

void CChat::AddLine(int ClientID, int Team, const char *pLine)
{
if(*pLine == 0 ||
Expand Down Expand Up @@ -640,6 +700,9 @@ void CChat::AddLine(int ClientID, int Team, const char *pLine)
{
str_copy(m_aLines[m_CurrentLine].m_aName, "*** ", sizeof(m_aLines[m_CurrentLine].m_aName));
str_format(m_aLines[m_CurrentLine].m_aText, sizeof(m_aLines[m_CurrentLine].m_aText), "%s", pLine);

if(Client()->State() != IClient::STATE_DEMOPLAYBACK)
StoreSave(m_aLines[m_CurrentLine].m_aText);
}
else
{
Expand Down
1 change: 1 addition & 0 deletions src/game/client/components/chat.h
Expand Up @@ -91,6 +91,7 @@ class CChat : public CComponent
static void ConEcho(IConsole::IResult *pResult, void *pUserData);

bool LineShouldHighlight(const char *pLine, const char *pName);
void StoreSave(const char *pText);

public:
CChat();
Expand Down
9 changes: 4 additions & 5 deletions src/game/client/components/controls.cpp
Expand Up @@ -504,17 +504,16 @@ void CControls::ClampMousePos()
else
{
float CameraMaxDistance = 200.0f;
float CameraMinDistance = 0.0f;
float FollowFactor = (g_Config.m_ClDyncam ? g_Config.m_ClDyncamFollowFactor : g_Config.m_ClMouseFollowfactor) / 100.0f;
float DeadZone = g_Config.m_ClDyncam ? g_Config.m_ClDyncamDeadzone : g_Config.m_ClMouseDeadzone;
float MaxDistance = g_Config.m_ClDyncam ? g_Config.m_ClDyncamMaxDistance : g_Config.m_ClMouseMaxDistance;
float MouseMax = minimum(CameraMaxDistance/FollowFactor + DeadZone, MaxDistance);
float MouseMax = minimum((FollowFactor != 0 ? CameraMaxDistance/FollowFactor + DeadZone : MaxDistance), MaxDistance);
float MinDistance = g_Config.m_ClDyncam ? g_Config.m_ClDyncamMinDistance : g_Config.m_ClMouseMinDistance;
float MouseMin = minimum(CameraMinDistance/FollowFactor + DeadZone, MinDistance);
float MouseMin = MinDistance;

if(length(m_MousePos[g_Config.m_ClDummy]) > MouseMax)
m_MousePos[g_Config.m_ClDummy] = normalize(m_MousePos[g_Config.m_ClDummy])*MouseMax;
if(length(m_MousePos[g_Config.m_ClDummy]) < MouseMin)
m_MousePos[g_Config.m_ClDummy] = normalize(m_MousePos[g_Config.m_ClDummy])*MouseMin;
if(length(m_MousePos[g_Config.m_ClDummy]) > MouseMax)
m_MousePos[g_Config.m_ClDummy] = normalize(m_MousePos[g_Config.m_ClDummy])*MouseMax;
}
}
2 changes: 1 addition & 1 deletion src/game/client/components/effects.cpp
Expand Up @@ -169,7 +169,7 @@ void CEffects::PlayerDeath(vec2 Pos, int ClientID)
if(ClientID >= 0)
{
if(m_pClient->m_aClients[ClientID].m_UseCustomColor)
BloodColor = color_cast<ColorRGBA>(ColorHSLA(m_pClient->m_aClients[ClientID].m_ColorBody).Lighten());
BloodColor = color_cast<ColorRGBA>(ColorHSLA(m_pClient->m_aClients[ClientID].m_ColorBody).UnclampLighting(0.5f));
else
{
const CSkins::CSkin *s = m_pClient->m_pSkins->Get(m_pClient->m_aClients[ClientID].m_SkinID);
Expand Down
5 changes: 0 additions & 5 deletions src/game/client/components/mapimages.cpp
Expand Up @@ -191,11 +191,6 @@ void CMapImages::UpdateEntityLayerText(IGraphics::CTextureHandle Texture, int Te
int CurrentNumberSuitableFontSize = TextRender()->AdjustFontSize(aBuf, DigitsCount, TextureSize);
int UniversalSuitableFontSize = CurrentNumberSuitableFontSize*0.9; // should be smoothed enough to fit any digits combination

if (UniversalSuitableFontSize < 1)
{
dbg_msg("pFont", "texture with id '%d' will not be loaded. Reason - font is too small", (int)Texture);
}

int ApproximateTextWidth = TextRender()->CalculateTextWidth(aBuf, DigitsCount, 0, UniversalSuitableFontSize);
int XOffSet = (64-ApproximateTextWidth)/2;
YOffset += ((TextureSize - UniversalSuitableFontSize)/2);
Expand Down
5 changes: 4 additions & 1 deletion src/game/client/components/mapsounds.cpp
@@ -1,3 +1,4 @@
#include <engine/demo.h>
#include <engine/engine.h>
#include <engine/sound.h>

Expand Down Expand Up @@ -95,6 +96,8 @@ void CMapSounds::OnRender()
if(Client()->State() != IClient::STATE_ONLINE && Client()->State() != IClient::STATE_DEMOPLAYBACK)
return;

bool DemoPlayerPaused = Client()->State() == IClient::STATE_DEMOPLAYBACK && DemoPlayer()->BaseInfo()->m_Paused;

// enqueue sounds
for(int i = 0; i < m_lSourceQueue.size(); i++)
{
Expand All @@ -108,7 +111,7 @@ void CMapSounds::OnRender()
Client()->IntraGameTick(g_Config.m_ClDummy));
}
float Offset = s_Time-pSource->m_pSource->m_TimeDelay;
if(Offset >= 0.0f && g_Config.m_SndEnable && (g_Config.m_GfxHighDetail || !pSource->m_HighDetail))
if(!DemoPlayerPaused && Offset >= 0.0f && g_Config.m_SndEnable && (g_Config.m_GfxHighDetail || !pSource->m_HighDetail))
{
if(pSource->m_Voice.IsValid())
{
Expand Down
82 changes: 48 additions & 34 deletions src/game/client/components/menus.cpp
Expand Up @@ -674,14 +674,25 @@ int CMenus::RenderMenubar(CUIRect r)
if(Client()->State() == IClient::STATE_OFFLINE)
{
// offline menus
Box.VSplitLeft(90.0f, &Button, &Box);
Box.VSplitLeft(60.0f, &Button, &Box);
static int s_NewsButton=0;
if(DoButton_MenuTab(&s_NewsButton, Localize("News"), m_ActivePage==PAGE_NEWS, &Button, CUI::CORNER_T))
if(DoButton_MenuTab(&s_NewsButton, Localize("News"), m_ActivePage==PAGE_NEWS, &Button, CUI::CORNER_TL))
{
NewPage = PAGE_NEWS;
m_DoubleClickIndex = -1;
}
Box.VSplitLeft(10.0f, 0, &Box);
Box.VSplitLeft(60.0f, &Button, &Box);
static int s_LearnButton=0;
if(DoButton_MenuTab(&s_LearnButton, Localize("Learn"), false, &Button, CUI::CORNER_TR))
{
if(!open_link("https://wiki.ddnet.tw/"))
{
dbg_msg("menus", "couldn't open link");
}
m_DoubleClickIndex = -1;
}

Box.VSplitLeft(5.0f, 0, &Box);

Box.VSplitLeft(100.0f, &Button, &Box);
static int s_InternetButton=0;
Expand Down Expand Up @@ -739,8 +750,8 @@ int CMenus::RenderMenubar(CUIRect r)
m_DoubleClickIndex = -1;
}

Box.VSplitLeft(10.0f, 0, &Box);
Box.VSplitLeft(100.0f, &Button, &Box);
Box.VSplitLeft(5.0f, 0, &Box);
Box.VSplitLeft(80.0f, &Button, &Box);
static int s_DemosButton=0;
if(DoButton_MenuTab(&s_DemosButton, Localize("Demos"), m_ActivePage==PAGE_DEMOS, &Button, CUI::CORNER_T))
{
Expand Down Expand Up @@ -912,7 +923,7 @@ void CMenus::RenderNews(CUIRect MainView)
else
{
MainView.HSplitTop(20.0f, &Label, &MainView);
UI()->DoLabelScaled(&Label, aLine, 15.f, -1, MainView.w-30.0f);
UI()->DoLabelScaled(&Label, aLine, 15.f, -1, -1);
}
}
}
Expand Down Expand Up @@ -1059,7 +1070,7 @@ int CMenus::Render()
// make sure that other windows doesn't do anything funnay!
//UI()->SetHotItem(0);
//UI()->SetActiveItem(0);
char aBuf[128];
char aBuf[1536];
const char *pTitle = "";
const char *pExtraText = "";
const char *pButtonText = "";
Expand Down Expand Up @@ -1090,7 +1101,7 @@ int CMenus::Render()
pButtonText = Localize("Ok");
if(Client()->m_ReconnectTime > 0)
{
str_format(aBuf, sizeof(aBuf), Localize("\n\nReconnect in %d sec"), (int)((Client()->m_ReconnectTime - time_get()) / time_freq()));
str_format(aBuf, sizeof(aBuf), Localize("Reconnect in %d sec"), (int)((Client()->m_ReconnectTime - time_get()) / time_freq()));
pTitle = Client()->ErrorString();
pExtraText = aBuf;
pButtonText = Localize("Abort");
Expand Down Expand Up @@ -1170,15 +1181,26 @@ int CMenus::Render()
else if(m_Popup == POPUP_FIRST_LAUNCH)
{
pTitle = Localize("Welcome to DDNet");
pExtraText = Localize("As this is the first time you launch the game, please enter your nick name below. It's recommended that you check the settings to adjust them to your liking before joining a server.");
str_format(aBuf, sizeof(aBuf), "%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s",
Localize("DDraceNetwork is a cooperative online game where the goal is for you and your group of tees to reach the finish line of the map. As a newcomer you should start on Novice servers, which host the easiest maps. Consider the ping to choose a server close to you."),
Localize("The maps contain freeze, which temporarily make a tee unable to move. You have to work together to get through these parts."),
Localize("The mouse wheel changes weapons. Hammer (left mouse) can be used to hit other tees and wake them up from being frozen."),
Localize("Hook (right mouse) can be used to swing through the map and to hook other tees to you."),
Localize("Most importantly communication is key: There is no tutorial so you'll have to chat (t key) with other players to learn the basics and tricks of the game."),
Localize("It's recommended that you check the settings to adjust them to your liking before joining a server."),
Localize("Please enter your nick name below."));
pExtraText = aBuf;
pButtonText = Localize("Ok");
ExtraAlign = -1;
}

CUIRect Box, Part;
Box = Screen;
Box.VMargin(150.0f/UI()->Scale(), &Box);
Box.HMargin(150.0f/UI()->Scale(), &Box);
if(m_Popup != POPUP_FIRST_LAUNCH)
{
Box.VMargin(150.0f/UI()->Scale(), &Box);
Box.HMargin(150.0f/UI()->Scale(), &Box);
}

// render the box
RenderTools()->DrawUIRect(&Box, ColorRGBA(0,0,0,0.5f), CUI::CORNER_ALL, 15.0f);
Expand All @@ -1194,14 +1216,16 @@ int CMenus::Render()
Box.HSplitTop(24.f/UI()->Scale(), &Part, &Box);
Part.VMargin(20.f/UI()->Scale(), &Part);

float FontSize = m_Popup == POPUP_FIRST_LAUNCH ? 16.0f : 20.f;

if(ExtraAlign == -1)
UI()->DoLabelScaled(&Part, pExtraText, 20.f, -1, (int)Part.w);
UI()->DoLabelScaled(&Part, pExtraText, FontSize, -1, (int)Part.w);
else
{
if(TextRender()->TextWidth(0, 20.f, pExtraText, -1) > Part.w)
UI()->DoLabelScaled(&Part, pExtraText, 20.f, -1, (int)Part.w);
if(TextRender()->TextWidth(0, FontSize, pExtraText, -1) > Part.w)
UI()->DoLabelScaled(&Part, pExtraText, FontSize, -1, (int)Part.w);
else
UI()->DoLabelScaled(&Part, pExtraText, 20.f, 0, -1);
UI()->DoLabelScaled(&Part, pExtraText, FontSize, 0, -1);
}

if(m_Popup == POPUP_QUIT)
Expand Down Expand Up @@ -1645,22 +1669,6 @@ int CMenus::Render()
Part.VSplitLeft(Button.h, &Button, &Part);
if(DoButton_CheckBox(&g_Config.m_ClVideoSndEnable, Localize("Use sounds"), g_Config.m_ClVideoSndEnable, &Button))
g_Config.m_ClVideoSndEnable ^= 1;
/*
static int s_ButtonInc = 0;
if(DoButton_Menu(&s_ButtonInc, Localize("IncSpeed"), 0, &IncSpeed))
m_Popup = POPUP_NONE;
static int s_ButtonDec = 0;
if(DoButton_Menu(&s_ButtonDec, Localize("DecSpeed"), 0, &DecSpeed))
m_Popup = POPUP_NONE;
*/
//Abort.VMargin(20.0f, &Abort);
//SpeedBox.VSplitLeft(40.0f, 0, &SpeedBox);
//SpeedBox.VSplitRight(80.0f, &SpeedBox, 0);
//UI()->DoLabel(&Label, Localize("Video speed:"), 18.0f, -1);
//static float Offset2 = 0.0f;
//char Speed[10] = "1";
//DoEditBox(&Offset2, &SpeedBox, Speed, sizeof(Speed), 12.0f, &Offset2);

Box.HSplitBottom(20.f, &Box, &Part);
#if defined(__ANDROID__)
Expand Down Expand Up @@ -1759,17 +1767,23 @@ int CMenus::Render()
Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);

Part.VSplitLeft(60.0f, 0, &Part);
if(DoButton_CheckBox(&g_Config.m_BrIndicateFinished, Localize("Show DDNet map finishes in server browser\n(transmits your player name to info.ddnet.tw)"), g_Config.m_BrIndicateFinished, &Part))
Part.VSplitLeft(30.0f, 0, &Part);
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "%s\n(%s)",
Localize("Show DDNet map finishes in server browser"),
Localize("transmits your player name to info.ddnet.tw"));

if(DoButton_CheckBox(&g_Config.m_BrIndicateFinished, aBuf, g_Config.m_BrIndicateFinished, &Part))
g_Config.m_BrIndicateFinished ^= 1;

Box.HSplitBottom(20.f, &Box, &Part);
Box.HSplitBottom(24.f, &Box, &Part);

Part.VSplitLeft(60.0f, 0, &Label);
Label.VSplitLeft(100.0f, 0, &TextBox);
TextBox.VSplitLeft(20.0f, 0, &TextBox);
TextBox.VSplitRight(60.0f, &TextBox, 0);
UI()->DoLabel(&Label, Localize("Nickname"), 18.0f, -1);
UI()->DoLabel(&Label, Localize("Nickname"), 16.0f, -1);
static float Offset = 0.0f;
DoEditBox(&g_Config.m_PlayerName, &TextBox, g_Config.m_PlayerName, sizeof(g_Config.m_PlayerName), 12.0f, &Offset);
}
Expand Down
1 change: 1 addition & 0 deletions src/game/client/components/menus.h
Expand Up @@ -437,5 +437,6 @@ class CMenus : public CComponent
// found in menus_settings.cpp
void RenderSettingsDDNet(CUIRect MainView);
void RenderSettingsHUD(CUIRect MainView);
ColorHSLA RenderHSLScrollbars(CUIRect *pRect, unsigned int *pColor, bool Alpha = false);
};
#endif
2 changes: 1 addition & 1 deletion src/game/client/components/menus_browser.cpp
Expand Up @@ -507,7 +507,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)

// render quick exclude
{
const char *pLabel = Localize("\xEE\x85\x8B"); // U+0e14b
const char *pLabel = "\xEE\x85\x8B"; // U+0e14b
TextRender()->SetCurFont(TextRender()->GetFont(TEXT_FONT_ICON_FONT));
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
UI()->DoLabelScaled(&QuickExclude, pLabel, 16.0f, -1);
Expand Down
6 changes: 1 addition & 5 deletions src/game/client/components/menus_demo.cpp
Expand Up @@ -342,7 +342,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
if(Amount > 0.0f && Amount < 1.0f && absolute(PrevAmount-Amount) >= 0.0001f)
{
//PrevAmount = Amount;
m_pClient->OnReset();
m_pClient->m_SuppressEvents = true;
DemoPlayer()->SeekPercent(Amount);
m_pClient->m_SuppressEvents = false;
Expand All @@ -355,7 +354,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
if(Amount > 0.0f && Amount < 1.0f && absolute(PrevAmount-Amount) >= 0.001f)
{
PrevAmount = Amount;
m_pClient->OnReset();
m_pClient->m_SuppressEvents = true;
DemoPlayer()->SeekPercent(Amount);
m_pClient->m_SuppressEvents = false;
Expand All @@ -377,7 +375,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)

if(CurrentTick == TotalTicks)
{
m_pClient->OnReset();
DemoPlayer()->Pause();
DemoPlayer()->SeekPercent(0.0f);
}
Expand Down Expand Up @@ -409,7 +406,6 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)
static int s_ResetButton = 0;
if(DoButton_Sprite(&s_ResetButton, IMAGE_DEMOBUTTONS, SPRITE_DEMOBUTTON_STOP, false, &Button, CUI::CORNER_ALL))
{
m_pClient->OnReset();
DemoPlayer()->Pause();
DemoPlayer()->SeekPercent(0.0f);
}
Expand Down Expand Up @@ -947,7 +943,7 @@ void CMenus::RenderDemoList(CUIRect MainView)
if(m_lDemos[m_DemolistSelectedIndex].m_MapInfo.m_Sha256 != SHA256_ZEROED)
{
Left.VSplitLeft(150.0f, &Left, &Right);
UI()->DoLabelScaled(&Left, Localize("SHA256:"), 14.0f, -1);
UI()->DoLabelScaled(&Left, "SHA256:", 14.0f, -1);
char aSha[SHA256_MAXSTRSIZE];
sha256_str(m_lDemos[m_DemolistSelectedIndex].m_MapInfo.m_Sha256, aSha, sizeof(aSha)/2);
UI()->DoLabelScaled(&Right, aSha, 14.0f, -1);
Expand Down
4 changes: 2 additions & 2 deletions src/game/client/components/menus_ingame.cpp
Expand Up @@ -120,7 +120,7 @@ void CMenus::RenderGame(CUIRect MainView)

static int s_DemoButton = 0;
bool Recording = DemoRecorder(RECORDER_MANUAL)->IsRecording();
if(DoButton_Menu(&s_DemoButton, Localize(Recording ? "Stop record" : "Record demo"), 0, &Button)) // Localize("Stop record");Localize("Record demo");
if(DoButton_Menu(&s_DemoButton, Recording ? Localize("Stop record") : Localize("Record demo"), 0, &Button))
{
if(!Recording)
Client()->DemoRecorder_Start(Client()->GetCurrentMap(), true, RECORDER_MANUAL);
Expand All @@ -136,7 +136,7 @@ void CMenus::RenderGame(CUIRect MainView)
{
DoButton_Menu(&s_DummyButton, Localize("Connecting dummy"), 1, &Button);
}
else if(DoButton_Menu(&s_DummyButton, Localize(Client()->DummyConnected() ? "Disconnect dummy" : "Connect dummy"), 0, &Button))
else if(DoButton_Menu(&s_DummyButton, Client()->DummyConnected() ? Localize("Disconnect Dummy") : Localize("Connect Dummy"), 0, &Button))
{
if(!Client()->DummyConnected())
{
Expand Down