151 changes: 65 additions & 86 deletions src/engine/server/server.cpp
Expand Up @@ -44,6 +44,10 @@
#include <windows.h>
#endif

#include <engine/server/databases/mysql.h>
#include <engine/server/databases/sqlite.h>
#include <engine/server/databases/connection_pool.h>


CSnapIDPool::CSnapIDPool()
{
Expand Down Expand Up @@ -297,16 +301,7 @@ CServer::CServer(): m_Register(false), m_RegSixup(true)
m_ConnLoggingSocketCreated = false;
#endif

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

CSqlConnector::SetReadServers(m_apSqlReadServers);
CSqlConnector::SetWriteServers(m_apSqlWriteServers);
#endif
m_pConnectionPool = new CDbConnectionPool();

m_aErrorShutdownReason[0] = 0;

Expand Down Expand Up @@ -2004,46 +1999,39 @@ void CServer::SendServerInfo(const NETADDR *pAddr, int Token, int Type, bool Sen
p.Reset();

CCache *pCache = &m_ServerInfoCache[GetCacheIndex(Type, SendClients)];
CCache::CCacheChunk &FirstChunk = pCache->m_lCache.front();

#define ADD_RAW(p, x) (p).AddRaw(x, sizeof(x))
#define ADD_INT(p, x) do { str_format(aBuf, sizeof(aBuf), "%d", x); (p).AddString(aBuf, 0); } while(0)

switch(Type)
{
case SERVERINFO_EXTENDED: ADD_RAW(p, SERVERBROWSE_INFO_EXTENDED); break;
case SERVERINFO_64_LEGACY: ADD_RAW(p, SERVERBROWSE_INFO_64_LEGACY); break;
case SERVERINFO_VANILLA:
case SERVERINFO_INGAME: ADD_RAW(p, SERVERBROWSE_INFO); break;
default: dbg_assert(false, "unknown serverinfo type");
}

ADD_INT(p, Token);
p.AddRaw(FirstChunk.m_aData, FirstChunk.m_DataSize);

CNetChunk Packet;
Packet.m_ClientID = -1;
Packet.m_Address = *pAddr;
Packet.m_Flags = NETSENDFLAG_CONNLESS;
Packet.m_pData = p.Data();
Packet.m_DataSize = p.Size();

m_NetServer.Send(&Packet);

if(Type == SERVERINFO_INGAME || Type == SERVERINFO_VANILLA)
return;

for(const auto &Chunk : pCache->m_lCache)
{
p.Reset();
if(Type == SERVERINFO_EXTENDED)
{
p.AddRaw(SERVERBROWSE_INFO_EXTENDED_MORE, sizeof(SERVERBROWSE_INFO_EXTENDED_MORE));
if(&Chunk == &pCache->m_lCache.front())
p.AddRaw(SERVERBROWSE_INFO_EXTENDED, sizeof(SERVERBROWSE_INFO_EXTENDED));
else
p.AddRaw(SERVERBROWSE_INFO_EXTENDED_MORE, sizeof(SERVERBROWSE_INFO_EXTENDED_MORE));
ADD_INT(p, Token);
}
else if(Type == SERVERINFO_64_LEGACY)
{
p.AddRaw(FirstChunk.m_aData, FirstChunk.m_DataSize);
ADD_RAW(p, SERVERBROWSE_INFO_64_LEGACY);
ADD_INT(p, Token);
}
else if (Type == SERVERINFO_VANILLA || Type == SERVERINFO_INGAME)
{
ADD_RAW(p, SERVERBROWSE_INFO);
ADD_INT(p, Token);
}
else
{
dbg_assert(false, "unknown serverinfo type");
}

p.AddRaw(Chunk.m_aData, Chunk.m_DataSize);
Expand Down Expand Up @@ -2314,6 +2302,23 @@ int CServer::Run()
return -1;
}

if(g_Config.m_SvSqliteFile[0] != '\0')
{
auto pSqlServers = std::unique_ptr<CSqliteConnection>(new CSqliteConnection(
g_Config.m_SvSqliteFile, true));

if(g_Config.m_SvUseSQL)
{
DbPool()->RegisterDatabase(std::move(pSqlServers), CDbConnectionPool::WRITE_BACKUP);
}
else
{
auto pCopy = std::unique_ptr<CSqliteConnection>(pSqlServers->Copy());
DbPool()->RegisterDatabase(std::move(pSqlServers), CDbConnectionPool::READ);
DbPool()->RegisterDatabase(std::move(pCopy), CDbConnectionPool::WRITE);
}
}

// start server
NETADDR BindAddr;
int NetType = g_Config.m_SvIpv4Only ? NETTYPE_IPV4 : NETTYPE_ALL;
Expand Down Expand Up @@ -2591,22 +2596,14 @@ int CServer::Run()
m_Fifo.Shutdown();
#endif

GameServer()->OnShutdown(true);
GameServer()->OnShutdown();
m_pMap->Unload();

for(int i = 0; i < 2; i++)
free(m_apCurrentMapData[i]);

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

if (m_apSqlWriteServers[i])
delete m_apSqlWriteServers[i];
}
#endif
DbPool()->OnShutdown();
delete m_pConnectionPool;

#if defined (CONF_UPNP)
m_UPnP.Shutdown();
Expand Down Expand Up @@ -3072,6 +3069,8 @@ void CServer::ConLogout(IConsole::IResult *pResult, void *pUser)

void CServer::ConShowIps(IConsole::IResult *pResult, void *pUser)
{
if(!g_Config.m_SvUseSQL)
return;
CServer *pServer = (CServer *)pUser;

if(pServer->m_RconClientID >= 0 && pServer->m_RconClientID < MAX_CLIENTS &&
Expand All @@ -3091,10 +3090,10 @@ void CServer::ConShowIps(IConsole::IResult *pResult, void *pUser)
}
}

#if defined (CONF_SQL)

void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData)
{
if(!g_Config.m_SvUseSQL)
return;
CServer *pSelf = (CServer *)pUserData;

if (pResult->NumArguments() != 7 && pResult->NumArguments() != 8)
Expand All @@ -3116,60 +3115,42 @@ void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData)

bool SetUpDb = pResult->NumArguments() == 8 ? pResult->GetInteger(7) : true;

CSqlServer** apSqlServers = ReadOnly ? pSelf->m_apSqlReadServers : pSelf->m_apSqlWriteServers;

for (int i = 0; i < MAX_SQLSERVERS; i++)
{
if (!apSqlServers[i])
{
apSqlServers[i] = new CSqlServer(pResult->GetString(1), pResult->GetString(2), pResult->GetString(3), pResult->GetString(4), pResult->GetString(5), pResult->GetInteger(6), &pSelf->m_GlobalSqlLock, ReadOnly, SetUpDb);
auto pSqlServers = std::unique_ptr<CMysqlConnection>(new CMysqlConnection(
pResult->GetString(1), pResult->GetString(2), pResult->GetString(3),
pResult->GetString(4), pResult->GetString(5), pResult->GetInteger(6),
SetUpDb));

char aBuf[512];
str_format(aBuf, sizeof(aBuf),
"Added new Sql%sServer: %d: DB: '%s' Prefix: '%s' User: '%s' IP: <{'%s'}> Port: %d",
ReadOnly ? "Read" : "Write", i, apSqlServers[i]->GetDatabase(),
apSqlServers[i]->GetPrefix(), apSqlServers[i]->GetUser(),
apSqlServers[i]->GetIP(), apSqlServers[i]->GetPort());
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
if(SetUpDb)
{
if(!apSqlServers[i]->CreateTables())
pSelf->SetErrorShutdown("database create tables failed");
}
return;
}
}
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "failed to add new sqlserver: limit of sqlservers reached");
char aBuf[512];
str_format(aBuf, sizeof(aBuf),
"Added new Sql%sServer: DB: '%s' Prefix: '%s' User: '%s' IP: <{'%s'}> Port: %d",
ReadOnly ? "Read" : "Write",
pResult->GetString(1), pResult->GetString(2), pResult->GetString(3),
pResult->GetString(5), pResult->GetInteger(6));
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
pSelf->DbPool()->RegisterDatabase(std::move(pSqlServers), ReadOnly ? CDbConnectionPool::READ : CDbConnectionPool::WRITE);
}

void CServer::ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData)
{
CServer *pSelf = (CServer *)pUserData;

bool ReadOnly;
if (str_comp_nocase(pResult->GetString(0), "w") == 0)
ReadOnly = false;
else if (str_comp_nocase(pResult->GetString(0), "r") == 0)
ReadOnly = true;
if(str_comp_nocase(pResult->GetString(0), "w") == 0)
{
pSelf->DbPool()->Print(pSelf->Console(), CDbConnectionPool::WRITE);
pSelf->DbPool()->Print(pSelf->Console(), CDbConnectionPool::WRITE_BACKUP);
}
else if(str_comp_nocase(pResult->GetString(0), "r") == 0)
{
pSelf->DbPool()->Print(pSelf->Console(), CDbConnectionPool::READ);
}
else
{
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "choose either 'r' for SqlReadServer or 'w' for SqlWriteServer");
return;
}

CSqlServer** apSqlServers = ReadOnly ? pSelf->m_apSqlReadServers : pSelf->m_apSqlWriteServers;

for (int i = 0; i < MAX_SQLSERVERS; i++)
if (apSqlServers[i])
{
char aBuf[512];
str_format(aBuf, sizeof(aBuf), "SQL-%s %d: DB: '%s' Prefix: '%s' User: '%s' Pass: '%s' IP: <{'%s'}> Port: %d", ReadOnly ? "Read" : "Write", i, apSqlServers[i]->GetDatabase(), apSqlServers[i]->GetPrefix(), apSqlServers[i]->GetUser(), apSqlServers[i]->GetPass(), apSqlServers[i]->GetIP(), apSqlServers[i]->GetPort());
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
}

#endif

void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);
Expand Down Expand Up @@ -3370,10 +3351,8 @@ 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|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("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");
Expand Down
17 changes: 3 additions & 14 deletions src/engine/server/server.h
Expand Up @@ -33,11 +33,6 @@
#include "upnp.h"
#endif

#if defined (CONF_SQL)
#include "sql_connector.h"
#include "sql_server.h"
#endif

class CSnapIDPool
{
enum
Expand Down Expand Up @@ -102,24 +97,20 @@ class CServer : public IServer
CUPnP m_UPnP;
#endif

#if defined(CONF_SQL)
lock m_GlobalSqlLock;

CSqlServer *m_apSqlReadServers[MAX_SQLSERVERS];
CSqlServer *m_apSqlWriteServers[MAX_SQLSERVERS];
#endif

#if defined(CONF_FAMILY_UNIX)
UNIXSOCKETADDR m_ConnLoggingDestAddr;
bool m_ConnLoggingSocketCreated;
UNIXSOCKET m_ConnLoggingSocket;
#endif

class CDbConnectionPool *m_pConnectionPool;

public:
class IGameServer *GameServer() { return m_pGameServer; }
class IConsole *Console() { return m_pConsole; }
class IStorage *Storage() { return m_pStorage; }
class IEngineAntibot *Antibot() { return m_pAntibot; }
class CDbConnectionPool *DbPool() { return m_pConnectionPool; }

enum
{
Expand Down Expand Up @@ -395,11 +386,9 @@ class CServer : public IServer
static void ConNameUnban(IConsole::IResult *pResult, void *pUser);
static void ConNameBans(IConsole::IResult *pResult, void *pUser);

#if defined (CONF_SQL)
// console commands for sqlmasters
static void ConAddSqlServer(IConsole::IResult *pResult, void *pUserData);
static void ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData);
#endif

static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
Expand Down
41 changes: 0 additions & 41 deletions src/engine/server/sql_connector.cpp

This file was deleted.

47 changes: 0 additions & 47 deletions src/engine/server/sql_connector.h

This file was deleted.

223 changes: 0 additions & 223 deletions src/engine/server/sql_server.cpp

This file was deleted.

58 changes: 0 additions & 58 deletions src/engine/server/sql_server.h

This file was deleted.

48 changes: 15 additions & 33 deletions src/engine/server/sql_string_helpers.cpp
@@ -1,8 +1,8 @@
#include "sql_string_helpers.h"

#include <base/system.h>
#include <cmath>
#include <cstring>
#include <base/system.h>

#include "sql_string_helpers.h"

void sqlstr::FuzzyString(char *pString, int size)
{
Expand All @@ -24,39 +24,21 @@ void sqlstr::FuzzyString(char *pString, int size)
delete [] newString;
}

// anti SQL injection
void sqlstr::ClearString(char *pString, int size)
int sqlstr::EscapeLike(char *pDst, const char *pSrc, int DstSize)
{
char *newString = new char [size * 2 - 1];
int pos = 0;

for(int i = 0; i < size; i++)
int Pos = 0;
int DstPos = 0;
while(DstPos + 2 < DstSize)
{
if(pString[i] == '\\')
{
newString[pos++] = '\\';
newString[pos++] = '\\';
}
else if(pString[i] == '\'')
{
newString[pos++] = '\\';
newString[pos++] = '\'';
}
else if(pString[i] == '"')
{
newString[pos++] = '\\';
newString[pos++] = '"';
}
else
{
newString[pos++] = pString[i];
}
}

newString[pos] = '\0';
if(pSrc[Pos] == '\0')
break;
if(pSrc[Pos] == '\\' || pSrc[Pos] == '%' || pSrc[Pos] == '_' || pSrc[Pos] == '[')
pDst[DstPos++] = '\\';
pDst[DstPos++] = pSrc[Pos++];

str_copy(pString, newString, size);
delete [] newString;
}
pDst[DstPos++] = '\0';
return DstPos;
}

void sqlstr::AgoTimeToString(int AgoTime, char *pAgoString)
Expand Down
40 changes: 2 additions & 38 deletions src/engine/server/sql_string_helpers.h
@@ -1,52 +1,16 @@
#ifndef ENGINE_SERVER_SQL_STRING_HELPERS_H
#define ENGINE_SERVER_SQL_STRING_HELPERS_H

#include <base/system.h>

namespace sqlstr
{

void FuzzyString(char *pString, int size);

// anti SQL injection
void ClearString(char *pString, int size = 32);
// written number of added bytes
int EscapeLike(char *pDst, const char *pSrc, int DstSize);

void AgoTimeToString(int agoTime, char *pAgoString);

template<unsigned int size>
class CSqlString
{
public:
CSqlString() {}

CSqlString(const char *pStr)
{
str_copy(m_aString, pStr, size);
str_copy(m_aClearString, pStr, size);
ClearString(m_aClearString, sizeof(m_aClearString));
}

const char* Str() const { return m_aString; }
const char* ClrStr() const { return m_aClearString; }

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];
};

}

#endif
8 changes: 3 additions & 5 deletions src/engine/shared/config_variables.h
Expand Up @@ -120,7 +120,7 @@ MACRO_CONFIG_INT(SvIpv4Only, sv_ipv4only, 0, 0, 1, CFGFLAG_SERVER, "Whether to b
MACRO_CONFIG_INT(SvPort, sv_port, 0, 0, 0, CFGFLAG_SERVER, "Port to use for the server (Only ports 8303-8310 work in LAN server browser, 0 to automatically find a free port in 8303-8310)")
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_STR(SvMap, sv_map, 128, "Gold Mine", 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")
MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
Expand Down Expand Up @@ -217,11 +217,9 @@ MACRO_CONFIG_STR(SvSqlServerName, sv_sql_servername, 5, "UNK", CFGFLAG_SERVER, "
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
MACRO_CONFIG_STR(SvSqliteFile, sv_sqlite_file, 64, "ddnet-server.sqlite", CFGFLAG_SERVER, "File to store ranks in case sv_use_sql is turned off or used as backup sql server")

#if defined(CONF_UPNP)
MACRO_CONFIG_INT(SvUseUPnP, sv_use_upnp, 0, 0, 1, CFGFLAG_SERVER, "Enables UPnP support.")
Expand Down Expand Up @@ -346,7 +344,7 @@ MACRO_CONFIG_INT(ClShowHookCollOther, cl_show_hook_coll_other, 1, 0, 2, CFGFLAG_
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")
MACRO_CONFIG_INT(ClShowDirection, cl_show_direction, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Show tee direction")
MACRO_CONFIG_INT(ClHttpMapDownload, cl_http_map_download, 1, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Try fast HTTP map download first")
MACRO_CONFIG_INT(ClOldGunPosition, cl_old_gun_position, 0, 0, 1, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Tees hold gun a bit higher like in TW 0.6.1 and older")
MACRO_CONFIG_INT(ClConfirmDisconnectTime, cl_confirm_disconnect_time, 20, -1, 1440, CFGFLAG_SAVE|CFGFLAG_CLIENT, "Confirmation popup before disconnecting after game time (in minutes, -1 to turn off, 0 to always turn on)")
Expand Down
2 changes: 1 addition & 1 deletion src/engine/shared/network_server.cpp
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(20)); //omit port, bad idea?
sha256_update(&Sha256, (unsigned char*)&Addr, 20); // omit port, bad idea!

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

Expand Down
4 changes: 3 additions & 1 deletion src/engine/shared/snapshot.cpp
Expand Up @@ -620,14 +620,16 @@ void *CSnapshotBuilder::NewItem(int Type, int ID, int Size)
return 0;
}

bool Extended = false;
if(Type >= OFFSET_UUID)
{
Extended = true;
Type = GetTypeFromIndex(GetExtendedItemTypeIndex(Type));
}

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

if(m_Sixup)
if(m_Sixup && !Extended)
{
if(Type >= 0)
Type = Obj_SixToSeven(Type);
Expand Down
8 changes: 8 additions & 0 deletions src/engine/shared/uuid_manager.cpp
Expand Up @@ -55,6 +55,14 @@ void FormatUuid(CUuid Uuid, char *pBuffer, unsigned BufferLength)
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
}

void ParseUuid(CUuid *pUuid, char *pBuffer)
{
unsigned char *p = pUuid->m_aData;
sscanf(pBuffer, "%02hhX%02hhX%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX",
&p[0], &p[1], &p[2], &p[3], &p[4], &p[5], &p[6], &p[7],
&p[8], &p[9], &p[10], &p[11], &p[12], &p[13], &p[14], &p[15]);
}

bool CUuid::operator==(const CUuid& Other)
{
return mem_comp(this, &Other, sizeof(*this)) == 0;
Expand Down
1 change: 1 addition & 0 deletions src/engine/shared/uuid_manager.h
Expand Up @@ -25,6 +25,7 @@ CUuid RandomUuid();
CUuid CalculateUuid(const char *pName);
// The buffer length should be at least UUID_MAXSTRSIZE.
void FormatUuid(CUuid Uuid, char *pBuffer, unsigned BufferLength);
void ParseUuid(CUuid *pUuid, char *pBuffer);

struct CName
{
Expand Down
4 changes: 2 additions & 2 deletions src/engine/textrender.h
Expand Up @@ -107,8 +107,8 @@ class ITextRender : public IInterface
virtual void TextColor(float r, float g, float b, float a) = 0;
virtual void TextColor(ColorRGBA rgb) = 0;
virtual void TextOutlineColor(float r, float g, float b, float a) = 0;
virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, int MaxWidth) = 0;
virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int Length, float *pAlignedHeight = NULL) = 0;
virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, float LineWidth) = 0;
virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int StrLength, float LineWidth, float *pAlignedHeight = NULL) = 0;
virtual int TextLineCount(void *pFontSetV, float Size, const char *pText, float LineWidth) = 0;

virtual void OnWindowResize() = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/components/camera.cpp
Expand Up @@ -38,7 +38,7 @@ void CCamera::ScaleZoom(float Factor)

void CCamera::ChangeZoom(float Target)
{
if(Target >= 500.0f/ZoomStep)
if(Target >= (Graphics()->IsBufferingEnabled()? 60 : 30))
{
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/components/chat.cpp
Expand Up @@ -1020,7 +1020,7 @@ void CChat::OnRender()
}

TextRender()->TextEx(&Cursor, m_Input.GetString(Editing)+m_ChatStringOffset, m_Input.GetCursorOffset(Editing)-m_ChatStringOffset);
static float MarkerOffset = TextRender()->TextWidth(0, 8.0f, "|", -1)/3;
static float MarkerOffset = TextRender()->TextWidth(0, 8.0f, "|", -1, -1.0f)/3;
CTextCursor Marker = Cursor;
Marker.m_X -= MarkerOffset;
TextRender()->TextEx(&Marker, "|", -1);
Expand Down
10 changes: 5 additions & 5 deletions src/game/client/components/console.cpp
Expand Up @@ -404,7 +404,7 @@ void CGameConsole::PossibleCommandsRenderCallback(const char *pStr, void *pUser)

if(pInfo->m_EnumCount == pInfo->m_WantedCompletion)
{
float tw = pInfo->m_pSelf->TextRender()->TextWidth(pInfo->m_Cursor.m_pFont, pInfo->m_Cursor.m_FontSize, pStr, -1);
float tw = pInfo->m_pSelf->TextRender()->TextWidth(pInfo->m_Cursor.m_pFont, pInfo->m_Cursor.m_FontSize, pStr, -1, -1.0f);
pInfo->m_pSelf->Graphics()->TextureClear();
pInfo->m_pSelf->Graphics()->QuadsBegin();
pInfo->m_pSelf->Graphics()->SetColor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,0.85f);
Expand Down Expand Up @@ -612,7 +612,7 @@ void CGameConsole::OnRender()
Cursor.m_LineWidth = Screen.w - 10.0f - x;

TextRender()->TextEx(&Cursor, aInputString, pConsole->m_Input.GetCursorOffset(Editing));
static float MarkerOffset = TextRender()->TextWidth(0, FontSize, "|", -1)/3;
static float MarkerOffset = TextRender()->TextWidth(0, FontSize, "|", -1, -1.0f)/3;
CTextCursor Marker = Cursor;
Marker.m_X -= MarkerOffset;
Marker.m_LineWidth = -1;
Expand Down Expand Up @@ -707,12 +707,12 @@ void CGameConsole::OnRender()
char aBuf[128];
TextRender()->TextColor(1,1,1,1);
str_format(aBuf, sizeof(aBuf), Localize("-Page %d-"), pConsole->m_BacklogActPage+1);
TextRender()->Text(0, 10.0f, FontSize / 2.f, FontSize, aBuf, -1);
TextRender()->Text(0, 10.0f, FontSize / 2.f, FontSize, aBuf, -1.0f);

// render version
str_format(aBuf, sizeof(aBuf), "v%s", GAME_VERSION);
float Width = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, Screen.w-Width-10.0f, FontSize / 2.f, FontSize, aBuf, -1);
float Width = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, Screen.w-Width-10.0f, FontSize / 2.f, FontSize, aBuf, -1.0f);
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/game/client/components/controls.cpp
Expand Up @@ -173,6 +173,7 @@ void CControls::OnConsoleInit()
{ static CInputState s_State = {this, &m_InputData[0].m_Hook, &m_InputData[1].m_Hook}; Console()->Register("+hook", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Hook"); }
{ static CInputState s_State = {this, &m_InputData[0].m_Fire, &m_InputData[1].m_Fire}; Console()->Register("+fire", "", CFGFLAG_CLIENT, ConKeyInputCounter, (void *)&s_State, "Fire"); }
{ static CInputState s_State = {this, &m_ShowHookColl[0], &m_ShowHookColl[1]}; Console()->Register("+showhookcoll", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Show Hook Collision"); }
{ static CInputState s_State = {this, &m_ResetDummy[0], &m_ResetDummy[1]}; Console()->Register("+resetdummy", "", CFGFLAG_CLIENT, ConKeyInputState, (void *)&s_State, "Reset Dummy"); }

{ static CInputSet s_Set = {this, &m_InputData[0].m_WantedWeapon, &m_InputData[1].m_WantedWeapon, 1}; Console()->Register("+weapon1", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to hammer"); }
{ static CInputSet s_Set = {this, &m_InputData[0].m_WantedWeapon, &m_InputData[1].m_WantedWeapon, 2}; Console()->Register("+weapon2", "", CFGFLAG_CLIENT, ConKeyInputSet, (void *)&s_Set, "Switch to gun"); }
Expand Down Expand Up @@ -290,6 +291,19 @@ int CControls::SnapInput(int *pData)
pDummyInput->m_Hook = g_Config.m_ClDummyHook;
}

if(m_ResetDummy[g_Config.m_ClDummy])
{
ResetInput(!g_Config.m_ClDummy);
m_InputData[!g_Config.m_ClDummy].m_Hook = 0;

CNetObj_PlayerInput *pDummyInput = &m_pClient->m_DummyInput;
pDummyInput->m_Hook = m_InputData[!g_Config.m_ClDummy].m_Hook;
pDummyInput->m_Jump = m_InputData[!g_Config.m_ClDummy].m_Jump;
pDummyInput->m_Direction = m_InputData[!g_Config.m_ClDummy].m_Jump;

pDummyInput->m_Fire = m_InputData[!g_Config.m_ClDummy].m_Fire;
}

// stress testing
#ifdef CONF_DEBUG
if(g_Config.m_DbgStress)
Expand Down
1 change: 1 addition & 0 deletions src/game/client/components/controls.h
Expand Up @@ -29,6 +29,7 @@ class CControls : public CComponent
int m_InputDirectionLeft[2];
int m_InputDirectionRight[2];
int m_ShowHookColl[2];
int m_ResetDummy[2];
int m_LastDummy;
int m_OtherFire;

Expand Down
50 changes: 27 additions & 23 deletions src/game/client/components/debughud.cpp
Expand Up @@ -31,47 +31,51 @@ void CDebugHud::RenderNetCorrections()
float Velspeed = length(vec2(m_pClient->m_Snap.m_pLocalCharacter->m_VelX/256.0f, m_pClient->m_Snap.m_pLocalCharacter->m_VelY/256.0f))*50;
float Ramp = VelocityRamp(Velspeed, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampStart, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampRange, m_pClient->m_Tuning[g_Config.m_ClDummy].m_VelrampCurvature);

const char *paStrings[] = {"velspeed:", "velspeed*ramp:", "ramp:", "Pos", " x:", " y:", "angle:", "netobj corrections", " num:", " on:"};
const char *paStrings[] = {"velspeed:", "velspeed*ramp:", "ramp:", "checkpoint:", "Pos", " x:", " y:", "angle:", "netobj corrections", " num:", " on:"};
const int Num = sizeof(paStrings)/sizeof(char *);
const float LineHeight = 6.0f;
const float Fontsize = 5.0f;

float x = Width-100.0f, y = 50.0f;
for(int i = 0; i < Num; ++i)
TextRender()->Text(0, x, y+i*LineHeight, Fontsize, paStrings[i], -1);
TextRender()->Text(0, x, y+i*LineHeight, Fontsize, paStrings[i], -1.0f);

x = Width-10.0f;
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed/32);
float w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
float w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1.0f);
y += LineHeight;
str_format(aBuf, sizeof(aBuf), "%.0f", Velspeed/32*Ramp);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1.0f);
y += LineHeight;
str_format(aBuf, sizeof(aBuf), "%.2f", Ramp);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1.0f);
y += LineHeight;
str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_GameWorld.GetCharacterByID(m_pClient->m_Snap.m_LocalClientID)->m_TeleCheckpoint);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x - w, y, Fontsize, aBuf, -1.0f);
y += 2*LineHeight;
str_format(aBuf, sizeof(aBuf), "%.2f", static_cast<float>(m_pClient->m_Snap.m_pLocalCharacter->m_X)/32.0f);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1.0f);
y += LineHeight;
str_format(aBuf, sizeof(aBuf), "%.2f", static_cast<float>(m_pClient->m_Snap.m_pLocalCharacter->m_Y)/32.0f);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1.0f);
y += LineHeight;
str_format(aBuf, sizeof(aBuf), "%d", m_pClient->m_Snap.m_pLocalCharacter->m_Angle);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1.0f);
y += 2*LineHeight;
str_format(aBuf, sizeof(aBuf), "%d", m_pClient->NetobjNumCorrections());
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1);
w = TextRender()->TextWidth(0, Fontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-w, y, Fontsize, aBuf, -1.0f);
y += LineHeight;
w = TextRender()->TextWidth(0, Fontsize, m_pClient->NetobjCorrectedOn(), -1);
TextRender()->Text(0, x-w, y, Fontsize, m_pClient->NetobjCorrectedOn(), -1);
w = TextRender()->TextWidth(0, Fontsize, m_pClient->NetobjCorrectedOn(), -1, -1.0f);
TextRender()->Text(0, x-w, y, Fontsize, m_pClient->NetobjCorrectedOn(), -1.0f);
}

void CDebugHud::RenderTuning()
Expand Down Expand Up @@ -103,16 +107,16 @@ void CDebugHud::RenderTuning()

str_format(aBuf, sizeof(aBuf), "%.2f", Standard);
x += 20.0f;
w = TextRender()->TextWidth(0, 5, aBuf, -1);
TextRender()->Text(0x0, x-w, y+Count*6, 5, aBuf, -1);
w = TextRender()->TextWidth(0, 5, aBuf, -1, -1.0f);
TextRender()->Text(0x0, x-w, y+Count*6, 5, aBuf, -1.0f);

str_format(aBuf, sizeof(aBuf), "%.2f", Current);
x += 20.0f;
w = TextRender()->TextWidth(0, 5, aBuf, -1);
TextRender()->Text(0x0, x-w, y+Count*6, 5, aBuf, -1);
w = TextRender()->TextWidth(0, 5, aBuf, -1, -1.0f);
TextRender()->Text(0x0, x-w, y+Count*6, 5, aBuf, -1.0f);

x += 5.0f;
TextRender()->Text(0x0, x, y+Count*6, 5, m_pClient->m_Tuning[g_Config.m_ClDummy].ms_apNames[i], -1);
TextRender()->Text(0x0, x, y+Count*6, 5, m_pClient->m_Tuning[g_Config.m_ClDummy].ms_apNames[i], -1.0f);

Count++;
}
Expand Down
74 changes: 37 additions & 37 deletions src/game/client/components/hud.cpp
Expand Up @@ -134,16 +134,16 @@ void CHud::RenderGameTimer()
float FontSize = 10.0f;
float w;
if(g_Config.m_ClShowDecisecs)
w = TextRender()->TextWidth(0, 12,"00:00.0",-1);
w = TextRender()->TextWidth(0, 12, "00:00.0", -1, -1.0f);
else
w = TextRender()->TextWidth(0, 12,"00:00",-1);
w = TextRender()->TextWidth(0, 12, "00:00", -1, -1.0f);
// last 60 sec red, last 10 sec blink
if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit && Time <= 60 && (m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer <= 0))
{
float Alpha = Time <= 10 && (2*time()/time_freq()) % 2 ? 0.5f : 1.0f;
TextRender()->TextColor(1.0f, 0.25f, 0.25f, Alpha);
}
TextRender()->Text(0, Half-w/2, 2, FontSize, aBuf, -1);
TextRender()->Text(0, Half-w/2, 2, FontSize, aBuf, -1.0f);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}
Expand All @@ -155,8 +155,8 @@ void CHud::RenderPauseNotification()
{
const char *pText = Localize("Game paused");
float FontSize = 20.0f;
float w = TextRender()->TextWidth(0, FontSize,pText, -1);
TextRender()->Text(0, 150.0f*Graphics()->ScreenAspect()+-w/2.0f, 50.0f, FontSize, pText, -1);
float w = TextRender()->TextWidth(0, FontSize,pText, -1, -1.0f);
TextRender()->Text(0, 150.0f*Graphics()->ScreenAspect()+-w/2.0f, 50.0f, FontSize, pText, -1.0f);
}
}

Expand All @@ -167,8 +167,8 @@ void CHud::RenderSuddenDeath()
float Half = 300.0f*Graphics()->ScreenAspect()/2.0f;
const char *pText = Localize("Sudden Death");
float FontSize = 12.0f;
float w = TextRender()->TextWidth(0, FontSize, pText, -1);
TextRender()->Text(0, Half-w/2, 2, FontSize, pText, -1);
float w = TextRender()->TextWidth(0, FontSize, pText, -1, -1.0f);
TextRender()->Text(0, Half-w/2, 2, FontSize, pText, -1.0f);
}
}

Expand Down Expand Up @@ -202,13 +202,13 @@ void CHud::RenderScoreHud()
{
if(RecreateTeamScore[t])
{
m_aScoreInfo[t].m_ScoreTextWidth = TextRender()->TextWidth(0, 14.0f, aScoreTeam[t == 0 ? TEAM_RED : TEAM_BLUE], -1);
m_aScoreInfo[t].m_ScoreTextWidth = TextRender()->TextWidth(0, 14.0f, aScoreTeam[t == 0 ? TEAM_RED : TEAM_BLUE], -1, -1.0f);
mem_copy(m_aScoreInfo[t].m_aScoreText, aScoreTeam[t == 0 ? TEAM_RED : TEAM_BLUE], sizeof(m_aScoreInfo[t].m_aScoreText));
RecreateRect = true;
}
}

static float s_TextWidth100 = TextRender()->TextWidth(0, 14.0f, "100", -1);
static float s_TextWidth100 = TextRender()->TextWidth(0, 14.0f, "100", -1, -1.0f);
float ScoreWidthMax = maximum(maximum(m_aScoreInfo[0].m_ScoreTextWidth, m_aScoreInfo[1].m_ScoreTextWidth), s_TextWidth100);
float Split = 3.0f;
float ImageSize = GameFlags & GAMEFLAG_FLAGS ? 16.0f : Split;
Expand Down Expand Up @@ -273,7 +273,7 @@ void CHud::RenderScoreHud()
if(m_aScoreInfo[t].m_OptionalNameTextContainerIndex != -1)
TextRender()->DeleteTextContainer(m_aScoreInfo[t].m_OptionalNameTextContainerIndex);

float w = TextRender()->TextWidth(0, 8.0f, pName, -1);
float w = TextRender()->TextWidth(0, 8.0f, pName, -1, -1.0f);

CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, minimum(Whole - w - 1.0f, Whole - ScoreWidthMax - ImageSize - 2 * Split), StartY + (t + 1)*20.0f - 2.0f, 8.0f, TEXTFLAG_RENDER);
Expand Down Expand Up @@ -355,7 +355,7 @@ void CHud::RenderScoreHud()
{
if(RecreateScore[t])
{
m_aScoreInfo[t].m_ScoreTextWidth = TextRender()->TextWidth(0, 14.0f, aScore[t], -1);
m_aScoreInfo[t].m_ScoreTextWidth = TextRender()->TextWidth(0, 14.0f, aScore[t], -1, -1.0f);
mem_copy(m_aScoreInfo[t].m_aScoreText, aScore[t], sizeof(m_aScoreInfo[t].m_aScoreText));
RecreateRect = true;
}
Expand All @@ -382,7 +382,7 @@ void CHud::RenderScoreHud()
RecreateRect = true;
}

static float s_TextWidth10 = TextRender()->TextWidth(0, 14.0f, "10", -1);
static float s_TextWidth10 = TextRender()->TextWidth(0, 14.0f, "10", -1, -1.0f);
float ScoreWidthMax = maximum(maximum(m_aScoreInfo[0].m_ScoreTextWidth, m_aScoreInfo[1].m_ScoreTextWidth), s_TextWidth10);
float Split = 3.0f, ImageSize = 16.0f, PosSize = 16.0f;

Expand Down Expand Up @@ -437,7 +437,7 @@ void CHud::RenderScoreHud()
if(m_aScoreInfo[t].m_OptionalNameTextContainerIndex != -1)
TextRender()->DeleteTextContainer(m_aScoreInfo[t].m_OptionalNameTextContainerIndex);

float w = TextRender()->TextWidth(0, 8.0f, pName, -1);
float w = TextRender()->TextWidth(0, 8.0f, pName, -1, -1.0f);

CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, minimum(Whole - w - 1.0f, Whole - ScoreWidthMax - ImageSize - 2 * Split - PosSize), StartY + (t + 1)*20.0f - 2.0f, 8.0f, TEXTFLAG_RENDER);
Expand Down Expand Up @@ -499,16 +499,16 @@ void CHud::RenderWarmupTimer()
{
char Buf[256];
float FontSize = 20.0f;
float w = TextRender()->TextWidth(0, FontSize, Localize("Warmup"), -1);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()+-w/2, 50, FontSize, Localize("Warmup"), -1);
float w = TextRender()->TextWidth(0, FontSize, Localize("Warmup"), -1, -1.0f);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()+-w/2, 50, FontSize, Localize("Warmup"), -1.0f);

int Seconds = m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer/SERVER_TICK_SPEED;
if(Seconds < 5)
str_format(Buf, sizeof(Buf), "%d.%d", Seconds, (m_pClient->m_Snap.m_pGameInfoObj->m_WarmupTimer*10/SERVER_TICK_SPEED)%10);
else
str_format(Buf, sizeof(Buf), "%d", Seconds);
w = TextRender()->TextWidth(0, FontSize, Buf, -1);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()+-w/2, 75, FontSize, Buf, -1);
w = TextRender()->TextWidth(0, FontSize, Buf, -1, -1.0f);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()+-w/2, 75, FontSize, Buf, -1.0f);
}
}

Expand All @@ -530,17 +530,17 @@ void CHud::RenderTextInfo()
int FrameTime = (int)(1.0f / m_FrameTimeAvg + 0.5f);
str_format(Buf, sizeof(Buf), "%d", FrameTime);

static float s_TextWidth0 = TextRender()->TextWidth(0, 12.f, "0", -1);
static float s_TextWidth00 = TextRender()->TextWidth(0, 12.f, "00", -1);
static float s_TextWidth000 = TextRender()->TextWidth(0, 12.f, "000", -1);
static float s_TextWidth0000 = TextRender()->TextWidth(0, 12.f, "0000", -1);
static float s_TextWidth00000 = TextRender()->TextWidth(0, 12.f, "00000", -1);
static float s_TextWidth0 = TextRender()->TextWidth(0, 12.f, "0", -1, -1.0f);
static float s_TextWidth00 = TextRender()->TextWidth(0, 12.f, "00", -1, -1.0f);
static float s_TextWidth000 = TextRender()->TextWidth(0, 12.f, "000", -1, -1.0f);
static float s_TextWidth0000 = TextRender()->TextWidth(0, 12.f, "0000", -1, -1.0f);
static float s_TextWidth00000 = TextRender()->TextWidth(0, 12.f, "00000", -1, -1.0f);
static float s_TextWidth[5] = { s_TextWidth0, s_TextWidth00, s_TextWidth000, s_TextWidth0000, s_TextWidth00000 };

int DigitIndex = (int)log10((FrameTime ? FrameTime : 1));
if(DigitIndex > 4)
DigitIndex = 4;
//TextRender()->Text(0, m_Width-10-TextRender()->TextWidth(0,12,Buf,-1), 5, 12, Buf, -1);
//TextRender()->Text(0, m_Width-10-TextRender()->TextWidth(0,12,Buf,-1,-1.0f), 5, 12, Buf, -1.0f);

CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, m_Width - 10 - s_TextWidth[DigitIndex], 5, 12, TEXTFLAG_RENDER);
Expand All @@ -564,7 +564,7 @@ void CHud::RenderTextInfo()
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "%d", Client()->GetPredictionTime());
TextRender()->Text(0, m_Width-10-TextRender()->TextWidth(0,12,aBuf,-1), g_Config.m_ClShowfps ? 20 : 5, 12, aBuf, -1);
TextRender()->Text(0, m_Width-10-TextRender()->TextWidth(0,12,aBuf,-1,-1.0f), g_Config.m_ClShowfps ? 20 : 5, 12, aBuf, -1.0f);
}
}

Expand All @@ -573,8 +573,8 @@ void CHud::RenderConnectionWarning()
if(Client()->ConnectionProblems())
{
const char *pText = Localize("Connection Problems...");
float w = TextRender()->TextWidth(0, 24, pText, -1);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()-w/2, 50, 24, pText, -1);
float w = TextRender()->TextWidth(0, 24, pText, -1, -1.0f);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()-w/2, 50, 24, pText, -1.0f);
}
}

Expand All @@ -592,7 +592,7 @@ void CHud::RenderTeambalanceWarning()
TextRender()->TextColor(1,1,0.5f,1);
else
TextRender()->TextColor(0.7f,0.7f,0.2f,1.0f);
TextRender()->Text(0x0, 5, 50, 6, pText, -1);
TextRender()->Text(0x0, 5, 50, 6, pText, -1.0f);
TextRender()->TextColor(1,1,1,1);
}
}
Expand All @@ -616,7 +616,7 @@ void CHud::RenderVoting()
CTextCursor Cursor;
char aBuf[512];
str_format(aBuf, sizeof(aBuf), Localize("%ds left"), m_pClient->m_pVoting->SecondsLeft());
float tw = TextRender()->TextWidth(0x0, 6, aBuf, -1);
float tw = TextRender()->TextWidth(0x0, 6, aBuf, -1, -1.0f);
TextRender()->SetCursor(&Cursor, 5.0f+100.0f-tw, 60.0f, 6.0f, TEXTFLAG_RENDER);
TextRender()->TextEx(&Cursor, aBuf, -1);

Expand Down Expand Up @@ -740,7 +740,7 @@ void CHud::RenderSpectatorHud()
char aBuf[128];
str_format(aBuf, sizeof(aBuf), "%s: %s", Localize("Spectate"), m_pClient->m_Snap.m_SpecInfo.m_SpectatorID != SPEC_FREEVIEW ?
m_pClient->m_aClients[m_pClient->m_Snap.m_SpecInfo.m_SpectatorID].m_aName : Localize("Free-View"));
TextRender()->Text(0, m_Width-174.0f, m_Height-15.0f + (15.f - 8.f) / 2.f, 8.0f, aBuf, -1);
TextRender()->Text(0, m_Width-174.0f, m_Height-15.0f + (15.f - 8.f) / 2.f, 8.0f, aBuf, -1.0f);
}

void CHud::RenderLocalTime(float x)
Expand All @@ -759,7 +759,7 @@ void CHud::RenderLocalTime(float x)
//draw the text
char aTimeStr[6];
str_timestamp_format(aTimeStr, sizeof(aTimeStr), "%H:%M");
TextRender()->Text(0, x-25.0f, (12.5f - 5.f) / 2.f, 5.0f, aTimeStr, -1);
TextRender()->Text(0, x-25.0f, (12.5f - 5.f) / 2.f, 5.0f, aTimeStr, -1.0f);
}

void CHud::OnRender()
Expand Down Expand Up @@ -894,7 +894,7 @@ void CHud::RenderDDRaceEffects()
if(m_FinishTime)
{
str_format(aBuf, sizeof(aBuf), "Finish time: %02d:%02d.%02d", m_DDRaceTime/6000, m_DDRaceTime/100-m_DDRaceTime/6000 * 60, m_DDRaceTime % 100);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()-TextRender()->TextWidth(0,12,aBuf,-1)/2, 20, 12, aBuf, -1);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()-TextRender()->TextWidth(0, 12, aBuf, -1, -1.0f)/2, 20, 12, aBuf, -1.0f);
}
else if(m_CheckpointTick + Client()->GameTickSpeed()*6 > Client()->GameTick(g_Config.m_ClDummy))
{
Expand All @@ -914,15 +914,15 @@ void CHud::RenderDDRaceEffects()
TextRender()->TextColor(0.5f,1.0f,0.5f,a); // green
else if(!m_CheckpointDiff)
TextRender()->TextColor(1,1,1,a); // white
TextRender()->Text(0, 150*Graphics()->ScreenAspect()-TextRender()->TextWidth(0, 10, aBuf, -1)/2, 20, 10, aBuf, -1);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()-TextRender()->TextWidth(0, 10, aBuf, -1, -1.0f)/2, 20, 10, aBuf, -1.0f);

TextRender()->TextColor(1,1,1,1);
}
}
/*else if(m_DDRaceTimeReceived)
{
str_format(aBuf, sizeof(aBuf), "%02d:%02d.%d", m_DDRaceTime/60, m_DDRaceTime%60, m_DDRaceTick/10);
TextRender()->Text(0, 150*Graphics()->ScreenAspect()-TextRender()->TextWidth(0, 12,"00:00.0",-1)/2, 20, 12, aBuf, -1); // use fixed value for text width so its not shaky
TextRender()->Text(0, 150*Graphics()->ScreenAspect()-TextRender()->TextWidth(0, 12, "00:00.0", -1, -1.0f)/2, 20, 12, aBuf, -1.0f); // use fixed value for text width so its not shaky
}*/
}

Expand All @@ -932,18 +932,18 @@ void CHud::RenderRecord()
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), Localize("Server best:"));
TextRender()->Text(0, 5, 40, 6, aBuf, -1);
TextRender()->Text(0, 5, 40, 6, aBuf, -1.0f);
str_format(aBuf, sizeof(aBuf), "%02d:%05.2f", (int)m_ServerRecord/60, m_ServerRecord-((int)m_ServerRecord/60*60));
TextRender()->Text(0, 53, 40, 6, aBuf, -1);
TextRender()->Text(0, 53, 40, 6, aBuf, -1.0f);
}

const float PlayerRecord = m_PlayerRecord[g_Config.m_ClDummy];
if(PlayerRecord > 0 )
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), Localize("Personal best:"));
TextRender()->Text(0, 5, 47, 6, aBuf, -1);
TextRender()->Text(0, 5, 47, 6, aBuf, -1.0f);
str_format(aBuf, sizeof(aBuf), "%02d:%05.2f", (int)PlayerRecord/60, PlayerRecord-((int)PlayerRecord/60*60));
TextRender()->Text(0, 53, 47, 6, aBuf, -1);
TextRender()->Text(0, 53, 47, 6, aBuf, -1.0f);
}
}
4 changes: 2 additions & 2 deletions src/game/client/components/killmessages.cpp
Expand Up @@ -97,7 +97,7 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg)
float FontSize = 36.0f;
if(Kill.m_aVictimName[0] != 0)
{
Kill.m_VitctimTextWidth = TextRender()->TextWidth(0, FontSize, Kill.m_aVictimName, -1);
Kill.m_VitctimTextWidth = TextRender()->TextWidth(0, FontSize, Kill.m_aVictimName, -1, -1.0f);

CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER);
Expand All @@ -108,7 +108,7 @@ void CKillMessages::OnMessage(int MsgType, void *pRawMsg)

if(Kill.m_aKillerName[0] != 0)
{
Kill.m_KillerTextWidth = TextRender()->TextWidth(0, FontSize, Kill.m_aKillerName, -1);
Kill.m_KillerTextWidth = TextRender()->TextWidth(0, FontSize, Kill.m_aKillerName, -1, -1.0f);

CTextCursor Cursor;
TextRender()->SetCursor(&Cursor, 0, 0, FontSize, TEXTFLAG_RENDER);
Expand Down
38 changes: 28 additions & 10 deletions src/game/client/components/menus.cpp
Expand Up @@ -264,6 +264,23 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS
Input()->SetClipboardText(pStr);
}

/* TODO: Doesn't work, SetClipboardText doesn't retain the string quickly enough?
if(Input()->KeyIsPressed(KEY_LCTRL) && Input()->KeyPress(KEY_X))
{
Input()->SetClipboardText(pStr);
pStr[0] = '\0';
s_AtIndex = 0;
ReturnValue = true;
}
*/

if(Input()->KeyIsPressed(KEY_LCTRL) && Input()->KeyPress(KEY_U))
{
pStr[0] = '\0';
s_AtIndex = 0;
ReturnValue = true;
}

if(Inside && UI()->MouseButton(0))
{
s_DoScroll = true;
Expand All @@ -272,7 +289,7 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS

for(int i = 1; i <= Len; i++)
{
if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel)
if(TextRender()->TextWidth(0, FontSize, pStr, i, -1.0f) - *Offset > MxRel)
{
s_AtIndex = i - 1;
break;
Expand Down Expand Up @@ -388,11 +405,11 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS
// check if the text has to be moved
if(UI()->LastActiveItem() == pID && !JustGotActive && (UpdateOffset || m_NumInputEvents))
{
float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex, -1.0f);
if(w-*Offset > Textbox.w)
{
// move to the left
float wt = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1);
float wt = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1, -1.0f);
do
{
*Offset += minimum(wt-*Offset-Textbox.w, Textbox.w/3);
Expand Down Expand Up @@ -421,17 +438,17 @@ int CMenus::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrS
{
if(str_length(aInputing))
{
float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex + Input()->GetEditingCursor());
float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex + Input()->GetEditingCursor(), -1.0f);
Textbox = *pRect;
Textbox.VSplitLeft(2.0f, 0, &Textbox);
Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1)/2);
Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1, -1.0f)/2);

UI()->DoLabel(&Textbox, "|", FontSize, -1);
}
float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex, -1.0f);
Textbox = *pRect;
Textbox.VSplitLeft(2.0f, 0, &Textbox);
Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1)/2);
Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1, -1.0f)/2);

if((2*time_get()/time_freq()) % 2) // make it blink
UI()->DoLabel(&Textbox, "|", FontSize, -1);
Expand Down Expand Up @@ -1181,12 +1198,13 @@ int CMenus::Render()
else if(m_Popup == POPUP_FIRST_LAUNCH)
{
pTitle = Localize("Welcome to DDNet");
str_format(aBuf, sizeof(aBuf), "%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s\n\n%s",
str_format(aBuf, sizeof(aBuf), "%s\n\n%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("Use k key to kill (restart), q to pause and watch other players. See settings for other key binds."),
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;
Expand All @@ -1208,7 +1226,7 @@ int CMenus::Render()
Box.HSplitTop(20.f/UI()->Scale(), &Part, &Box);
Box.HSplitTop(24.f/UI()->Scale(), &Part, &Box);
Part.VMargin(20.f/UI()->Scale(), &Part);
if(TextRender()->TextWidth(0, 24.f, pTitle, -1) > Part.w)
if(TextRender()->TextWidth(0, 24.f, pTitle, -1, -1.0f) > Part.w)
UI()->DoLabelScaled(&Part, pTitle, 24.f, -1, (int)Part.w);
else
UI()->DoLabelScaled(&Part, pTitle, 24.f, 0);
Expand All @@ -1222,7 +1240,7 @@ int CMenus::Render()
UI()->DoLabelScaled(&Part, pExtraText, FontSize, -1, (int)Part.w);
else
{
if(TextRender()->TextWidth(0, FontSize, pExtraText, -1) > Part.w)
if(TextRender()->TextWidth(0, FontSize, pExtraText, -1, -1.0f) > Part.w)
UI()->DoLabelScaled(&Part, pExtraText, FontSize, -1, (int)Part.w);
else
UI()->DoLabelScaled(&Part, pExtraText, FontSize, 0, -1);
Expand Down
70 changes: 35 additions & 35 deletions src/game/client/components/menus_browser.cpp
Expand Up @@ -175,44 +175,44 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
else
g_Config.m_UiToolboxPage = (g_Config.m_UiToolboxPage + 3 + 1) % 3;
}
if(m_SelectedIndex > -1)
if(m_SelectedIndex < 0)
m_SelectedIndex = 0;

for(int i = 0; i < m_NumInputEvents; i++)
{
for(int i = 0; i < m_NumInputEvents; i++)
int NewIndex = -1;
if(m_aInputEvents[i].m_Flags&IInput::FLAG_PRESS)
{
int NewIndex = -1;
if(m_aInputEvents[i].m_Flags&IInput::FLAG_PRESS)
{
if(m_aInputEvents[i].m_Key == KEY_DOWN) NewIndex = m_SelectedIndex + 1;
else if(m_aInputEvents[i].m_Key == KEY_UP) NewIndex = m_SelectedIndex - 1;
else if(m_aInputEvents[i].m_Key == KEY_PAGEUP) NewIndex = maximum(m_SelectedIndex - 25, 0);
else if(m_aInputEvents[i].m_Key == KEY_PAGEDOWN) NewIndex = minimum(m_SelectedIndex + 25, NumServers - 1);
else if(m_aInputEvents[i].m_Key == KEY_HOME) NewIndex = 0;
else if(m_aInputEvents[i].m_Key == KEY_END) NewIndex = NumServers - 1;
}
if(NewIndex > -1 && NewIndex < NumServers)
if(m_aInputEvents[i].m_Key == KEY_DOWN) NewIndex = minimum(m_SelectedIndex + 1, NumServers - 1);
else if(m_aInputEvents[i].m_Key == KEY_UP) NewIndex = maximum(m_SelectedIndex - 1, 0);
else if(m_aInputEvents[i].m_Key == KEY_PAGEUP) NewIndex = maximum(m_SelectedIndex - 25, 0);
else if(m_aInputEvents[i].m_Key == KEY_PAGEDOWN) NewIndex = minimum(m_SelectedIndex + 25, NumServers - 1);
else if(m_aInputEvents[i].m_Key == KEY_HOME) NewIndex = 0;
else if(m_aInputEvents[i].m_Key == KEY_END) NewIndex = NumServers - 1;
}
if(NewIndex > -1 && NewIndex < NumServers)
{
//scroll
float IndexY = View.y - s_ScrollValue*ScrollNum*s_aCols[0].m_Rect.h + NewIndex*s_aCols[0].m_Rect.h;
int Scroll = View.y > IndexY ? -1 : View.y+View.h < IndexY+s_aCols[0].m_Rect.h ? 1 : 0;
if(Scroll)
{
//scroll
float IndexY = View.y - s_ScrollValue*ScrollNum*s_aCols[0].m_Rect.h + NewIndex*s_aCols[0].m_Rect.h;
int Scroll = View.y > IndexY ? -1 : View.y+View.h < IndexY+s_aCols[0].m_Rect.h ? 1 : 0;
if(Scroll)
if(Scroll < 0)
{
if(Scroll < 0)
{
int NumScrolls = (View.y-IndexY+s_aCols[0].m_Rect.h-1.0f)/s_aCols[0].m_Rect.h;
s_ScrollValue -= (1.0f/ScrollNum)*NumScrolls;
}
else
{
int NumScrolls = (IndexY+s_aCols[0].m_Rect.h-(View.y+View.h)+s_aCols[0].m_Rect.h-1.0f)/s_aCols[0].m_Rect.h;
s_ScrollValue += (1.0f/ScrollNum)*NumScrolls;
}
int NumScrolls = (View.y-IndexY+s_aCols[0].m_Rect.h-1.0f)/s_aCols[0].m_Rect.h;
s_ScrollValue -= (1.0f/ScrollNum)*NumScrolls;
}
else
{
int NumScrolls = (IndexY+s_aCols[0].m_Rect.h-(View.y+View.h)+s_aCols[0].m_Rect.h-1.0f)/s_aCols[0].m_Rect.h;
s_ScrollValue += (1.0f/ScrollNum)*NumScrolls;
}
}

m_SelectedIndex = NewIndex;
m_SelectedIndex = NewIndex;

const CServerInfo *pItem = ServerBrowser()->SortedGet(m_SelectedIndex);
str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress));
}
const CServerInfo *pItem = ServerBrowser()->SortedGet(m_SelectedIndex);
str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress));
}
}

Expand Down Expand Up @@ -492,7 +492,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
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(&QuickSearch, pLabel, 16.0f, -1);
float w = TextRender()->TextWidth(0, 16.0f, pLabel, -1);
float w = TextRender()->TextWidth(0, 16.0f, pLabel, -1, -1.0f);
TextRender()->SetRenderFlags(0);
TextRender()->SetCurFont(NULL);
QuickSearch.VSplitLeft(w, 0, &QuickSearch);
Expand All @@ -511,7 +511,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
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);
float w = TextRender()->TextWidth(0, 16.0f, pLabel, -1);
float w = TextRender()->TextWidth(0, 16.0f, pLabel, -1, -1.0f);
TextRender()->SetRenderFlags(0);
TextRender()->SetCurFont(NULL);
QuickExclude.VSplitLeft(w, 0, &QuickExclude);
Expand All @@ -528,7 +528,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
// render status
char aBuf[128];
str_format(aBuf, sizeof(aBuf), Localize("%d of %d servers, %d players"), ServerBrowser()->NumSortedServers(), ServerBrowser()->NumServers(), NumPlayers);
Status3.VSplitRight(TextRender()->TextWidth(0, 14.0f, aBuf, -1), 0, &Status3);
Status3.VSplitRight(TextRender()->TextWidth(0, 14.0f, aBuf, -1, -1.0f), 0, &Status3);
UI()->DoLabelScaled(&Status3, aBuf, 14.0f, -1);
}

Expand Down Expand Up @@ -1321,7 +1321,7 @@ void CMenus::RenderServerbrowser(CUIRect MainView)
UI()->DoLabelScaled(&Button, aBuf, 14.0f, -1);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);

Button.VSplitLeft(TextRender()->TextWidth(0, 14.0f, aBuf, -1) + 10.0f, &Button, &Part);
Button.VSplitLeft(TextRender()->TextWidth(0, 14.0f, aBuf, -1, -1.0f) + 10.0f, &Button, &Part);

if(State == IUpdater::CLEAN && NeedUpdate)
{
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/components/menus_demo.cpp
Expand Up @@ -241,7 +241,7 @@ void CMenus::RenderDemoPlayer(CUIRect MainView)

char aSpeedBuf[256];
str_format(aSpeedBuf, sizeof(aSpeedBuf), "×%.2f", pInfo->m_Speed);
TextRender()->Text(0, 120.0f, Screen.y+Screen.h - 120.0f - TotalHeight, 60.0f, aSpeedBuf, -1);
TextRender()->Text(0, 120.0f, Screen.y+Screen.h - 120.0f - TotalHeight, 60.0f, aSpeedBuf, -1.0f);
}

if(!m_MenuActive)
Expand Down
16 changes: 8 additions & 8 deletions src/game/client/components/menus_ingame.cpp
Expand Up @@ -391,7 +391,7 @@ void CMenus::RenderServerInfo(CUIRect MainView)
x = 5.0f;
y = 0.0f;

TextRender()->Text(0, ServerInfo.x+x, ServerInfo.y+y, 32, Localize("Server info"), 250);
TextRender()->Text(0, ServerInfo.x+x, ServerInfo.y+y, 32, Localize("Server info"), 250.0f);
y += 32.0f+5.0f;

mem_zero(aBuf, sizeof(aBuf));
Expand All @@ -410,7 +410,7 @@ void CMenus::RenderServerInfo(CUIRect MainView)
Localize("Password"), CurrentServerInfo.m_Flags &1 ? Localize("Yes") : Localize("No")
);

TextRender()->Text(0, ServerInfo.x+x, ServerInfo.y+y, 20, aBuf, 250);
TextRender()->Text(0, ServerInfo.x+x, ServerInfo.y+y, 20, aBuf, 250.0f);

{
CUIRect Button;
Expand All @@ -435,7 +435,7 @@ void CMenus::RenderServerInfo(CUIRect MainView)
x = 5.0f;
y = 0.0f;

TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 32, Localize("Game info"), 250);
TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 32, Localize("Game info"), 250.0f);
y += 32.0f+5.0f;

if(m_pClient->m_Snap.m_pGameInfoObj)
Expand All @@ -457,7 +457,7 @@ void CMenus::RenderServerInfo(CUIRect MainView)
Localize("Time limit"), m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit,
Localize("Players"), m_pClient->m_Snap.m_NumPlayers, CurrentServerInfo.m_MaxClients
);
TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 20, aBuf, 250);
TextRender()->Text(0, GameInfo.x+x, GameInfo.y+y, 20, aBuf, 250.0f);
}

// motd
Expand All @@ -466,9 +466,9 @@ void CMenus::RenderServerInfo(CUIRect MainView)
Motd.Margin(5.0f, &Motd);
y = 0.0f;
x = 5.0f;
TextRender()->Text(0, Motd.x+x, Motd.y+y, 32, Localize("MOTD"), -1);
TextRender()->Text(0, Motd.x+x, Motd.y+y, 32, Localize("MOTD"), -1.0f);
y += 32.0f+5.0f;
TextRender()->Text(0, Motd.x+x, Motd.y+y, 16, m_pClient->m_pMotd->m_aServerMotd, (int)Motd.w);
TextRender()->Text(0, Motd.x+x, Motd.y+y, 16, m_pClient->m_pMotd->m_aServerMotd, Motd.w);
}

bool CMenus::RenderServerControlServer(CUIRect MainView)
Expand Down Expand Up @@ -617,7 +617,7 @@ void CMenus::RenderServerControl(CUIRect MainView)
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(&QuickSearch, pSearchLabel, 14.0f, -1);
float wSearch = TextRender()->TextWidth(0, 14.0f, pSearchLabel, -1);
float wSearch = TextRender()->TextWidth(0, 14.0f, pSearchLabel, -1, -1.0f);
TextRender()->SetRenderFlags(0);
TextRender()->SetCurFont(NULL);
QuickSearch.VSplitLeft(wSearch, 0, &QuickSearch);
Expand Down Expand Up @@ -672,7 +672,7 @@ void CMenus::RenderServerControl(CUIRect MainView)
Reason.HSplitTop(5.0f, 0, &Reason);
const char *pLabel = Localize("Reason:");
UI()->DoLabelScaled(&Reason, pLabel, 14.0f, -1);
float w = TextRender()->TextWidth(0, 14.0f, pLabel, -1);
float w = TextRender()->TextWidth(0, 14.0f, pLabel, -1, -1.0f);
Reason.VSplitLeft(w+10.0f, 0, &Reason);
static float s_Offset = 0.0f;
if(Input()->KeyPress(KEY_R) && (Input()->KeyIsPressed(KEY_LCTRL) || Input()->KeyIsPressed(KEY_RCTRL)))
Expand Down
65 changes: 46 additions & 19 deletions src/game/client/components/menus_settings.cpp
Expand Up @@ -467,7 +467,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)

SkinPrefix.HSplitTop(2.0f, 0, &SkinPrefix);
{
static const char *s_aSkinPrefixes[] = {"kitty", "coala", "santa"};
static const char *s_aSkinPrefixes[] = {"kitty", "santa"};
for(unsigned i = 0; i < sizeof(s_aSkinPrefixes) / sizeof(s_aSkinPrefixes[0]); i++)
{
const char *pPrefix = s_aSkinPrefixes[i];
Expand Down Expand Up @@ -620,7 +620,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
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(&QuickSearch, pSearchLabel, 14.0f, -1);
float wSearch = TextRender()->TextWidth(0, 14.0f, pSearchLabel, -1);
float wSearch = TextRender()->TextWidth(0, 14.0f, pSearchLabel, -1, -1.0f);
TextRender()->SetRenderFlags(0);
TextRender()->SetCurFont(NULL);
QuickSearch.VSplitLeft(wSearch, 0, &QuickSearch);
Expand Down Expand Up @@ -787,7 +787,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView)
RenderTools()->DrawUIRect(&MovementSettings, ColorRGBA(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f);
MovementSettings.VMargin(10.0f, &MovementSettings);

TextRender()->Text(0, MovementSettings.x, MovementSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Movement"), -1);
TextRender()->Text(0, MovementSettings.x, MovementSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Movement"), -1.0f);

MovementSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &MovementSettings);

Expand Down Expand Up @@ -828,7 +828,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView)
RenderTools()->DrawUIRect(&WeaponSettings, ColorRGBA(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f);
WeaponSettings.VMargin(10.0f, &WeaponSettings);

TextRender()->Text(0, WeaponSettings.x, WeaponSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Weapon"), -1);
TextRender()->Text(0, WeaponSettings.x, WeaponSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Weapon"), -1.0f);

WeaponSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &WeaponSettings);
UiDoGetButtons(18, 25, WeaponSettings, MainView);
Expand All @@ -854,7 +854,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView)
RenderTools()->DrawUIRect(&VotingSettings, ColorRGBA(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f);
VotingSettings.VMargin(10.0f, &VotingSettings);

TextRender()->Text(0, VotingSettings.x, VotingSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Voting"), -1);
TextRender()->Text(0, VotingSettings.x, VotingSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Voting"), -1.0f);

VotingSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &VotingSettings);
UiDoGetButtons(25, 27, VotingSettings, MainView);
Expand All @@ -867,7 +867,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView)
RenderTools()->DrawUIRect(&ChatSettings, ColorRGBA(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f);
ChatSettings.VMargin(10.0f, &ChatSettings);

TextRender()->Text(0, ChatSettings.x, ChatSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Chat"), -1);
TextRender()->Text(0, ChatSettings.x, ChatSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Chat"), -1.0f);

ChatSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &ChatSettings);
UiDoGetButtons(27, 31, ChatSettings, MainView);
Expand All @@ -880,7 +880,7 @@ void CMenus::RenderSettingsControls(CUIRect MainView)
RenderTools()->DrawUIRect(&MiscSettings, ColorRGBA(1,1,1,0.25f), CUI::CORNER_ALL, 10.0f);
MiscSettings.VMargin(10.0f, &MiscSettings);

TextRender()->Text(0, MiscSettings.x, MiscSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Miscellaneous"), -1);
TextRender()->Text(0, MiscSettings.x, MiscSettings.y + (14.0f + 5.0f + 10.0f - 14.0f*UI()->Scale()) / 2.f, 14.0f*UI()->Scale(), Localize("Miscellaneous"), -1.0f);

MiscSettings.HSplitTop(14.0f+5.0f+10.0f, 0, &MiscSettings);
UiDoGetButtons(31, 43, MiscSettings, MainView);
Expand Down Expand Up @@ -1433,7 +1433,7 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)
MainView.HSplitTop(150.0f, &HUD, &MainView);

HUD.HSplitTop(30.0f, &Label, &HUD);
float tw = TextRender()->TextWidth(0, 20.0f, Localize("HUD"), -1);
float tw = TextRender()->TextWidth(0, 20.0f, Localize("HUD"), -1, -1.0f);
Label.VSplitLeft(tw + 10.0f, &Label, &Page1Tab);
Page1Tab.VSplitLeft(60.0f, &Page1Tab, 0);
Page1Tab.VSplitLeft(30.0f, &Page1Tab, &Page2Tab);
Expand Down Expand Up @@ -1537,7 +1537,7 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)
char name[16];
str_copy(name, g_Config.m_PlayerName, sizeof(name));
str_format(aBuf, sizeof(aBuf), "*** '%s' entered and joined the spectators", name);
while(TextRender()->TextWidth(0, 12.0f, aBuf, -1) > Label.w)
while(TextRender()->TextWidth(0, 12.0f, aBuf, -1, -1.0f) > Label.w)
{
name[str_length(name) - 1] = 0;
str_format(aBuf, sizeof(aBuf), "*** '%s' entered and joined the spectators", name);
Expand Down Expand Up @@ -1565,7 +1565,7 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)
Right.HSplitTop(10.0f, &Label, &Right);

TextRender()->TextColor(0.75f, 0.5f, 0.75f, 1.0f);
float tw = TextRender()->TextWidth(0, 12.0f, Localize("Spectator"), -1);
float tw = TextRender()->TextWidth(0, 12.0f, Localize("Spectator"), -1, -1.0f);
Label.VSplitLeft(tw, &Label, &Button);
UI()->DoLabelScaled(&Label, Localize("Spectator"), 12.0f, -1);

Expand All @@ -1574,7 +1574,7 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)
char name[16];
str_copy(name, g_Config.m_PlayerName, sizeof(name));
str_format(aBuf, sizeof(aBuf), ": %s: %s", name, Localize ("Look out!"));
while(TextRender()->TextWidth(0, 12.0f, aBuf, -1) > Button.w)
while(TextRender()->TextWidth(0, 12.0f, aBuf, -1, -1.0f) > Button.w)
{
name[str_length(name) - 1] = 0;
str_format(aBuf, sizeof(aBuf), ": %s: %s", name, Localize("Look out!"));
Expand Down Expand Up @@ -1604,7 +1604,7 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)

ColorRGBA rgbn = CalculateNameColor(TMColor);
TextRender()->TextColor(rgbn);
float tw = TextRender()->TextWidth(0, 12.0f, Localize("Player"), -1);
float tw = TextRender()->TextWidth(0, 12.0f, Localize("Player"), -1, -1.0f);
Label.VSplitLeft(tw, &Label, &Button);
UI()->DoLabelScaled(&Label, Localize("Player"), 12.0f, -1);

Expand All @@ -1620,7 +1620,7 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)
char aBuf[64];
Right.HSplitTop(20.0f, &Label, &Right);
Label.VSplitRight(50.0f, &Label, &Button);
float twh = TextRender()->TextWidth(0, 16.0f, Localize("Friend message"), -1) ;
float twh = TextRender()->TextWidth(0, 16.0f, Localize("Friend message"), -1, -1.0f) ;
Label.VSplitLeft(twh + 5.0f, &Label, &Enable);
Enable.VSplitLeft(20.0f, &Enable, 0);
UI()->DoLabelScaled(&Label, Localize("Friend message"), 16.0f, -1);
Expand All @@ -1641,12 +1641,12 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)

ColorRGBA rgbf = color_cast<ColorRGBA>(FMColor);
TextRender()->TextColor(rgbf);
float hw = TextRender()->TextWidth(0, 12.0f, "♥ ", -1);
float hw = TextRender()->TextWidth(0, 12.0f, "♥ ", -1, -1.0f);
Label.VSplitLeft(hw, &Heart, &Label);
UI()->DoLabelScaled(&Heart, "♥ ", 12.0f, -1);

TextRender()->TextColor(0.8f, 0.8f, 0.8f, 1.0f);
float tw = TextRender()->TextWidth(0, 12.0f, Localize("Friend"), -1);
float tw = TextRender()->TextWidth(0, 12.0f, Localize("Friend"), -1, -1.0f);
Label.VSplitLeft(tw, &Label, &Button);
UI()->DoLabelScaled(&Label, Localize("Friend"), 12.0f, -1);

Expand Down Expand Up @@ -1677,7 +1677,7 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)
Left.HSplitTop(10.0f, &Label, &Left);

TextRender()->TextColor(0.8f, 0.8f, 0.8f, 1.0f);
float tw = TextRender()->TextWidth(0, 12.0f, Localize("Player"), -1);
float tw = TextRender()->TextWidth(0, 12.0f, Localize("Player"), -1, -1.0f);
Label.VSplitLeft(tw, &Label, &Button);
UI()->DoLabelScaled(&Label, Localize("Player"), 12.0f, -1);

Expand All @@ -1688,14 +1688,41 @@ void CMenus::RenderSettingsHUD(CUIRect MainView)

TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
}
{
char aBuf[64];
Right.HSplitTop(20.0f, &Label, &Right);
Label.VSplitRight(50.0f, &Label, &Button);
str_format(aBuf, sizeof(aBuf), "%s (echo)", Localize("Client message"));
UI()->DoLabelScaled(&Label, aBuf, 16.0f, -1);
{
static int s_DefaultButton = 0;
if(DoButton_Menu(&s_DefaultButton, Localize("Reset"), 0, &Button))
{
ColorHSLA HSL = color_cast<ColorHSLA>(ColorRGBA(0.5f, 0.78f, 1.0f));
g_Config.m_ClMessageClientColor = HSL.Pack(false);
}
}

ColorHSLA CMColor = RenderHSLScrollbars(&Right, &g_Config.m_ClMessageClientColor);

Right.HSplitTop(10.0f, &Label, &Right);

ColorRGBA rgb = color_cast<ColorRGBA>(CMColor);
TextRender()->TextColor(rgb);


UI()->DoLabelScaled(&Label, "*** Dynamic camera activated", 12.0f, -1);
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);
Right.HSplitTop(20.0f, 0, &Right);
}
}
else if(Page == 2)
{
Left.HSplitTop(220.0f, &Laser, &Left);
//RenderTools()->DrawUIRect(&Laser, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_ALL, 5.0f);
//Laser.Margin(10.0f, &Laser);
Laser.HSplitTop(30.0f, &Label, &Laser);
Label.VSplitLeft(TextRender()->TextWidth(0, 20.0f, Localize("Laser"), -1) + 5.0f, &Label, &Weapon);
Label.VSplitLeft(TextRender()->TextWidth(0, 20.0f, Localize("Laser"), -1, -1.0f) + 5.0f, &Label, &Weapon);
UI()->DoLabelScaled(&Label, Localize("Laser"), 20.0f, -1);

Laser.HSplitTop(20.0f, &Label, &Laser);
Expand Down Expand Up @@ -2087,7 +2114,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
if(NeedUpdate && State <= IUpdater::CLEAN)
{
str_format(aBuf, sizeof(aBuf), Localize("DDNet %s is available:"), Client()->LatestVersion());
Label.VSplitLeft(TextRender()->TextWidth(0, 14.0f, aBuf, -1) + 10.0f, &Label, &Button);
Label.VSplitLeft(TextRender()->TextWidth(0, 14.0f, aBuf, -1, -1.0f) + 10.0f, &Label, &Button);
Button.VSplitLeft(100.0f, &Button, 0);
static int s_ButtonUpdate = 0;
if(DoButton_Menu(&s_ButtonUpdate, Localize("Update now"), 0, &Button))
Expand All @@ -2103,7 +2130,7 @@ void CMenus::RenderSettingsDDNet(CUIRect MainView)
else
{
str_format(aBuf, sizeof(aBuf), Localize("No updates available"));
Label.VSplitLeft(TextRender()->TextWidth(0, 14.0f, aBuf, -1) + 10.0f, &Label, &Button);
Label.VSplitLeft(TextRender()->TextWidth(0, 14.0f, aBuf, -1, -1.0f) + 10.0f, &Label, &Button);
Button.VSplitLeft(100.0f, &Button, 0);
static int s_ButtonUpdate = 0;
if(DoButton_Menu(&s_ButtonUpdate, Localize("Check now"), 0, &Button))
Expand Down
2 changes: 1 addition & 1 deletion src/game/client/components/motd.cpp
Expand Up @@ -49,7 +49,7 @@ void CMotd::OnRender()
RenderTools()->DrawRoundRect(x, y, w, h, 40.0f);
Graphics()->QuadsEnd();

TextRender()->Text(0, x+40.0f, y+40.0f, 32.0f, m_aServerMotd, (int)(w-80.0f));
TextRender()->Text(0, x+40.0f, y+40.0f, 32.0f, m_aServerMotd, w-80.0f);
}

void CMotd::OnMessage(int MsgType, void *pRawMsg)
Expand Down
50 changes: 46 additions & 4 deletions src/game/client/components/nameplates.cpp
Expand Up @@ -73,7 +73,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
MapscreenToGroup(m_pClient->m_pCamera->m_Center.x, m_pClient->m_pCamera->m_Center.y, Layers()->GameGroup());

m_aNamePlates[ClientID].m_NameTextWidth = TextRender()->TextWidth(0, FontSize, pName, -1);
m_aNamePlates[ClientID].m_NameTextWidth = TextRender()->TextWidth(0, FontSize, pName, -1, -1.0f);

m_aNamePlates[ClientID].m_NameTextContainerIndex = TextRender()->CreateTextContainer(&Cursor, pName);
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
Expand All @@ -99,7 +99,7 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
MapscreenToGroup(m_pClient->m_pCamera->m_Center.x, m_pClient->m_pCamera->m_Center.y, Layers()->GameGroup());

m_aNamePlates[ClientID].m_ClanNameTextWidth = TextRender()->TextWidth(0, FontSizeClan, pClan, -1);
m_aNamePlates[ClientID].m_ClanNameTextWidth = TextRender()->TextWidth(0, FontSizeClan, pClan, -1, -1.0f);

m_aNamePlates[ClientID].m_ClanNameTextContainerIndex = TextRender()->CreateTextContainer(&Cursor, pClan);
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
Expand Down Expand Up @@ -149,9 +149,51 @@ void CNamePlates::RenderNameplatePos(vec2 Position, const CNetObj_PlayerInfo *pP
char aBuf[128];
str_format(aBuf, sizeof(aBuf),"%d", pPlayerInfo->m_ClientID);
float Offset = g_Config.m_ClNameplatesClan ? (FontSize * 2 + FontSizeClan) : (FontSize * 2);
float tw_id = TextRender()->TextWidth(0, FontSize, aBuf, -1);
float tw_id = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->TextColor(rgb);
TextRender()->Text(0, Position.x-tw_id/2.0f, Position.y-Offset-38.0f, 28.0f, aBuf, -1);
TextRender()->Text(0, Position.x-tw_id/2.0f, Position.y-Offset-38.0f, 28.0f, aBuf, -1.0f);
}

if(g_Config.m_ClNameplatesHA) // render health and armor in nameplate
{
int Health = m_pClient->m_Snap.m_aCharacters[ClientID].m_Cur.m_Health;
if(Health > 0)
{
int Armor = m_pClient->m_Snap.m_aCharacters[ClientID].m_Cur.m_Armor;
float HFontSize = 5.0f + 20.0f * g_Config.m_ClNameplatesHASize / 100.0f;
float AFontSize = 6.0f + 24.0f * g_Config.m_ClNameplatesHASize / 100.0f;
char aHealth[40] = "\0";
char aArmor[40] = "\0";
for(int i = 0; i < Health; i++)
str_append(aHealth, "♥", sizeof(aHealth));
for(int i = Health; i < 10; i++)
str_append(aHealth, "♡", sizeof(aHealth));
str_append(aHealth, "\0", sizeof(aHealth));
for(int i = 0; i < Armor; i++)
str_append(aArmor, "âš«", sizeof(aArmor));
for(int i = Armor; i < 10; i++)
str_append(aArmor, "⚪", sizeof(aArmor));
str_append(aArmor, "\0", sizeof(aArmor));

float Offset;

if(g_Config.m_ClNameplatesClan && (g_Config.m_Debug || g_Config.m_ClNameplatesIDs))
Offset = (FontSize * 3 + FontSizeClan);
else if (g_Config.m_ClNameplatesClan)
Offset = (FontSize * 2 + FontSizeClan);
else if (g_Config.m_Debug || g_Config.m_ClNameplatesIDs)
Offset = (FontSize * 3);
else
Offset = (FontSize * 2);

float PosHealth = TextRender()->TextWidth(0, HFontSize, aHealth, -1, -1.0f);
TextRender()->TextColor(ColorRGBA(1.0f, 0.0f, 0.0f));
TextRender()->Text(0, Position.x-PosHealth/2.0f, Position.y-Offset-HFontSize-AFontSize, HFontSize, aHealth, -1);

float PosArmor = TextRender()->TextWidth(0, AFontSize, aArmor, -1, -1.0f);
TextRender()->TextColor(ColorRGBA(1.0f, 1.0f, 0.0f));
TextRender()->Text(0, Position.x-PosArmor/2.0f, Position.y-Offset-AFontSize-3.0f, AFontSize, aArmor, -1);
}
}

TextRender()->TextColor(1,1,1,1);
Expand Down
60 changes: 27 additions & 33 deletions src/game/client/components/scoreboard.cpp
Expand Up @@ -77,20 +77,20 @@ void CScoreboard::RenderGoals(float x, float y, float w)
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Score limit"), m_pClient->m_Snap.m_pGameInfoObj->m_ScoreLimit);
TextRender()->Text(0, x+10.0f, y + (h - 20.f) / 2.f, 20.0f, aBuf, -1);
TextRender()->Text(0, x+10.0f, y + (h - 20.f) / 2.f, 20.0f, aBuf, -1.0f);
}
if(m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit)
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), Localize("Time limit: %d min"), m_pClient->m_Snap.m_pGameInfoObj->m_TimeLimit);
TextRender()->Text(0, x+230.0f, y + (h - 20.f) / 2.f, 20.0f, aBuf, -1);
TextRender()->Text(0, x+230.0f, y + (h - 20.f) / 2.f, 20.0f, aBuf, -1.0f);
}
if(m_pClient->m_Snap.m_pGameInfoObj->m_RoundNum && m_pClient->m_Snap.m_pGameInfoObj->m_RoundCurrent)
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "%s %d/%d", Localize("Round"), m_pClient->m_Snap.m_pGameInfoObj->m_RoundCurrent, m_pClient->m_Snap.m_pGameInfoObj->m_RoundNum);
float tw = TextRender()->TextWidth(0, 20.0f, aBuf, -1);
TextRender()->Text(0, x+w-tw-10.0f, y + (h - 20.f) / 2.f, 20.0f, aBuf, -1);
float tw = TextRender()->TextWidth(0, 20.0f, aBuf, -1, -1.0f);
TextRender()->Text(0, x+w-tw-10.0f, y + (h - 20.f) / 2.f, 20.0f, aBuf, -1.0f);
}
}
}
Expand Down Expand Up @@ -201,19 +201,13 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
pTitle = Localize("Game over");
else
{
if(str_length(Client()->GetCurrentMap()) > 16)
{
str_truncate(aBuf, sizeof(aBuf), Client()->GetCurrentMap(), 16);
str_append(aBuf, "...", sizeof(aBuf));
pTitle = aBuf;
}
else
{
pTitle = Client()->GetCurrentMap();
}
str_utf8_truncate(aBuf, sizeof(aBuf), Client()->GetCurrentMap(), 16);
if(str_comp(aBuf, Client()->GetCurrentMap()))
str_append(aBuf, "…", sizeof(aBuf));
pTitle = aBuf;
}
}
TextRender()->Text(0, x + 20.0f, y + (50.f - TitleFontsize) / 2.f, TitleFontsize, pTitle, -1);
TextRender()->Text(0, x + 20.0f, y + (50.f - TitleFontsize) / 2.f, TitleFontsize, pTitle, -1.0f);

if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
{
Expand Down Expand Up @@ -252,8 +246,8 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch

if (!lower16 && !lower32 && !lower24)
{
tw = TextRender()->TextWidth(0, TitleFontsize, aBuf, -1);
TextRender()->Text(0, x+w-tw-20.0f, y + (50.f - TitleFontsize) / 2.f, TitleFontsize, aBuf, -1);
tw = TextRender()->TextWidth(0, TitleFontsize, aBuf, -1, -1.0f);
TextRender()->Text(0, x+w-tw-20.0f, y + (50.f - TitleFontsize) / 2.f, TitleFontsize, aBuf, -1.0f);
}

// calculate measurements
Expand Down Expand Up @@ -291,7 +285,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
RoundRadius = 15.0f;
}

float ScoreOffset = x+10.0f, ScoreLength = TextRender()->TextWidth(0, 22.0f/*HeadlineFontsize*/, "00:00:0", -1);
float ScoreOffset = x+10.0f, ScoreLength = TextRender()->TextWidth(0, 22.0f/*HeadlineFontsize*/, "00:00:0", -1, -1.0f);
float TeeOffset = ScoreOffset+ScoreLength, TeeLength = 60*TeeSizeMod;
float NameOffset = TeeOffset+TeeLength, NameLength = 300.0f-TeeLength;
float PingOffset = x+610.0f, PingLength = 65.0f;
Expand All @@ -302,17 +296,17 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
y += 50.0f;
float HeadlineFontsize = 22.0f;
const char *pScore = (m_pClient->m_GameInfo.m_TimeScore && g_Config.m_ClDDRaceScoreBoard) ? Localize("Time") : Localize("Score");
float ScoreWidth = TextRender()->TextWidth(0, HeadlineFontsize, pScore, -1);
float ScoreWidth = TextRender()->TextWidth(0, HeadlineFontsize, pScore, -1, -1.0f);
tw = ScoreLength > ScoreWidth ? ScoreLength : ScoreWidth;
TextRender()->Text(0, ScoreOffset+ScoreLength-tw, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, pScore, -1);
TextRender()->Text(0, ScoreOffset+ScoreLength-tw, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, pScore, -1.0f);

TextRender()->Text(0, NameOffset, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, Localize("Name"), -1);
TextRender()->Text(0, NameOffset, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, Localize("Name"), -1.0f);

tw = TextRender()->TextWidth(0, HeadlineFontsize, Localize("Clan"), -1);
TextRender()->Text(0, ClanOffset+ClanLength/2-tw/2, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, Localize("Clan"), -1);
tw = TextRender()->TextWidth(0, HeadlineFontsize, Localize("Clan"), -1, -1.0f);
TextRender()->Text(0, ClanOffset+ClanLength/2-tw/2, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, Localize("Clan"), -1.0f);

tw = TextRender()->TextWidth(0, HeadlineFontsize, Localize("Ping"), -1);
TextRender()->Text(0, PingOffset+PingLength-tw, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, Localize("Ping"), -1);
tw = TextRender()->TextWidth(0, HeadlineFontsize, Localize("Ping"), -1, -1.0f);
TextRender()->Text(0, PingOffset+PingLength-tw, y + (HeadlineFontsize * 2.f - HeadlineFontsize) / 2.f, HeadlineFontsize, Localize("Ping"), -1.0f);

// render player entries
y += HeadlineFontsize*2.0f;
Expand Down Expand Up @@ -400,7 +394,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
else
{
str_format(aBuf, sizeof(aBuf),"Team %d", DDTeam);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->SetCursor(&Cursor, ScoreOffset+w/2.0f-tw/2.0f, y + LineHeight, FontSize/1.5f, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = NameLength+3;
}
Expand Down Expand Up @@ -433,7 +427,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
}
else
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Score, -999, 99999));
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->SetCursor(&Cursor, ScoreOffset+ScoreLength-tw, y + (LineHeight - FontSize) / 2.f, FontSize, TEXTFLAG_RENDER);
TextRender()->TextEx(&Cursor, aBuf, -1);

Expand Down Expand Up @@ -491,7 +485,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
else
TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f);

tw = TextRender()->TextWidth(nullptr, FontSize, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1);
tw = TextRender()->TextWidth(nullptr, FontSize, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1, -1.0f);
TextRender()->SetCursor(&Cursor, ClanOffset + ClanLength / 2 - tw / 2, y + (LineHeight - FontSize) / 2.f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = ClanLength;
TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1);
Expand All @@ -510,7 +504,7 @@ void CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const ch
TextRender()->TextColor(rgb);
}
str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_Latency, 0, 999));
tw = TextRender()->TextWidth(nullptr, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(nullptr, FontSize, aBuf, -1, -1.0f);
TextRender()->SetCursor(&Cursor, PingOffset+PingLength-tw, y + (LineHeight - FontSize) / 2.f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = PingLength;
TextRender()->TextEx(&Cursor, aBuf, -1);
Expand Down Expand Up @@ -568,7 +562,7 @@ void CScoreboard::RenderRecordingNotification(float x)
str_append(aBuf, aBuf2, sizeof(aBuf));
}

float w = TextRender()->TextWidth(0, 20.0f, aBuf, -1);
float w = TextRender()->TextWidth(0, 20.0f, aBuf, -1, -1.0f);

//draw the box
Graphics()->BlendNormal();
Expand All @@ -584,7 +578,7 @@ void CScoreboard::RenderRecordingNotification(float x)
RenderTools()->DrawRoundRect(x+20, 15.0f, 20.0f, 20.0f, 10.0f);
Graphics()->QuadsEnd();

TextRender()->Text(0, x+50.0f, (50.f - 20.f) / 2.f, 20.0f, aBuf, -1);
TextRender()->Text(0, x+50.0f, (50.f - 20.f) / 2.f, 20.0f, aBuf, -1.0f);
}


Expand Down Expand Up @@ -651,8 +645,8 @@ void CScoreboard::OnRender()
str_copy(aText, Localize("Blue team wins!"), sizeof(aText));
}

float w = TextRender()->TextWidth(0, 86.0f, aText, -1);
TextRender()->Text(0, Width/2-w/2, 39, 86.0f, aText, -1);
float w = TextRender()->TextWidth(0, 86.0f, aText, -1, -1.0f);
TextRender()->Text(0, Width/2-w/2, 39, 86.0f, aText, -1.0f);
}

RenderScoreboard(Width/2-w-5.0f, 150.0f, w, TEAM_RED, pRedClanName ? pRedClanName : Localize("Red team"));
Expand Down
4 changes: 2 additions & 2 deletions src/game/client/components/spectator.cpp
Expand Up @@ -306,7 +306,7 @@ void CSpectator::OnRender()
Selected = true;
}
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected?1.0f:0.5f);
TextRender()->Text(0, Width/2.0f-(ObjWidth-60.0f), Height/2.0f-280.f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Free-View"), -1);
TextRender()->Text(0, Width/2.0f-(ObjWidth-60.0f), Height/2.0f-280.f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Free-View"), -1.0f);

if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
{
Expand All @@ -318,7 +318,7 @@ void CSpectator::OnRender()
Selected = true;
}
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Selected?1.0f:0.5f);
TextRender()->Text(0, Width/2.0f-(ObjWidth-350.0f), Height/2.0f-280.0f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Follow"), -1);
TextRender()->Text(0, Width/2.0f-(ObjWidth-350.0f), Height/2.0f-280.0f + (60.f - BigFontSize) / 2.f, BigFontSize, Localize("Follow"), -1.0f);
}

float x = -(ObjWidth - 30.0f), y = StartY;
Expand Down
54 changes: 27 additions & 27 deletions src/game/client/components/statboard.cpp
Expand Up @@ -85,7 +85,7 @@ void CStatboard::OnMessage(int MsgType, void *pRawMsg)

if(t <= p)
return;
str_truncate(aName, sizeof(aName), p, t - p);
str_utf8_truncate(aName, sizeof(aName), p, t - p);

for(int i = 0; i < MAX_CLIENTS; i++)
{
Expand Down Expand Up @@ -201,7 +201,7 @@ void CStatboard::RenderGlobalStats()
float tw;
int px = 325;

TextRender()->Text(0, x+10, y-5, 22.0f, Localize("Name"), -1);
TextRender()->Text(0, x+10, y-5, 22.0f, Localize("Name"), -1.0f);
const char *apHeaders[] = {
Localize("Frags"), Localize("Deaths"), Localize("Suicides"),
Localize("Ratio"), Localize("Net"), Localize("FPM"),
Expand All @@ -213,8 +213,8 @@ void CStatboard::RenderGlobalStats()
px += 10.0f; // Suicides
if(i == 8 && !GameWithFlags) // Don't draw "Grabs" in game with no flag
continue;
tw = TextRender()->TextWidth(0, 22.0f, apHeaders[i], -1);
TextRender()->Text(0, x+px-tw, y-5, 22.0f, apHeaders[i], -1);
tw = TextRender()->TextWidth(0, 22.0f, apHeaders[i], -1, -1.0f);
TextRender()->Text(0, x+px-tw, y-5, 22.0f, apHeaders[i], -1.0f);
px += 85;
}

Expand Down Expand Up @@ -285,7 +285,7 @@ void CStatboard::RenderGlobalStats()

char aBuf[128];
CTextCursor Cursor;
tw = TextRender()->TextWidth(0, FontSize, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, -1);
tw = TextRender()->TextWidth(0, FontSize, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, -1, -1.0f);
TextRender()->SetCursor(&Cursor, x+64, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
Cursor.m_LineWidth = 220;
TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, -1);
Expand All @@ -295,23 +295,23 @@ void CStatboard::RenderGlobalStats()
// FRAGS
{
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_Frags);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// DEATHS
{
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_Deaths);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// SUICIDES
{
px += 10;
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_Suicides);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// RATIO
Expand All @@ -320,45 +320,45 @@ void CStatboard::RenderGlobalStats()
str_format(aBuf, sizeof(aBuf), "--");
else
str_format(aBuf, sizeof(aBuf), "%.2f", (float)(pStats->m_Frags)/pStats->m_Deaths);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// NET
{
str_format(aBuf, sizeof(aBuf), "%+d", pStats->m_Frags-pStats->m_Deaths);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// FPM
{
float Fpm = pStats->GetFPM(Client()->GameTick(g_Config.m_ClDummy), Client()->GameTickSpeed());
str_format(aBuf, sizeof(aBuf), "%.1f", Fpm);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// SPREE
{
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_CurrentSpree);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// BEST SPREE
{
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_BestSpree);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// GRABS
if(GameWithFlags)
{
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_FlagGrabs);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
// WEAPONS
Expand All @@ -369,16 +369,16 @@ void CStatboard::RenderGlobalStats()
continue;

str_format(aBuf, sizeof(aBuf), "%d/%d", pStats->m_aFragsWith[i], pStats->m_aDeathsFrom[i]);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x+px-tw/2, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x+px-tw/2, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 80;
}
// FLAGS
if(GameWithFlags)
{
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_FlagCaptures);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1.0f);
px += 85;
}
y += LineHeight;
Expand Down
17 changes: 11 additions & 6 deletions src/game/client/gameclient.cpp
Expand Up @@ -511,10 +511,10 @@ void CGameClient::OnConnected()

void CGameClient::OnReset()
{
// clear out the invalid pointers
m_LastNewPredictedTick[0] = -1;
m_LastNewPredictedTick[1] = -1;
mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap));

InvalidateSnapshot();

for(int i = 0; i < MAX_CLIENTS; i++)
m_aClients[i].Reset();
Expand Down Expand Up @@ -1084,13 +1084,18 @@ static CGameInfo GetGameInfo(const CNetObj_GameInfoEx *pInfoEx, int InfoExSize,
return Info;
}

void CGameClient::OnNewSnapshot()
void CGameClient::InvalidateSnapshot()
{
m_NewTick = true;

// clear out the invalid pointers
// clear all pointers
mem_zero(&g_GameClient.m_Snap, sizeof(g_GameClient.m_Snap));
m_Snap.m_LocalClientID = -1;
}

void CGameClient::OnNewSnapshot()
{
InvalidateSnapshot();

m_NewTick = true;

// secure snapshot
{
Expand Down
1 change: 1 addition & 0 deletions src/game/client/gameclient.h
Expand Up @@ -346,6 +346,7 @@ class CGameClient : public IGameClient
virtual void OnConsoleInit();
virtual void OnStateChange(int NewState, int OldState);
virtual void OnMessage(int MsgId, CUnpacker *pUnpacker, bool IsDummy = 0);
virtual void InvalidateSnapshot();
virtual void OnNewSnapshot();
virtual void OnPredict();
virtual void OnActivateEditor();
Expand Down