mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-05 23:58:19 +00:00
Merge #2247
2247: Thread safe SQL interaction r=def- a=Zwelf This PR intends to make the database interaction thread safe and optimizes some SQL queries. This is still a WIP, but since it is a rather big PR I wanted to get feedback early on. Benefits: * remove race conditions leading to undefined behavior and potential crashes * logging game related database results in teehistorian would be possible Behavior change: * /top5team prints ranks in reverse order when passing a negative number (like /top5, /top5points) * Optimize SQL statements for /rank /teamrank /top5 /top5team /points /top5points * /load without parameters doesn't pass the SQL error to the user (as most other functions) * Simplify IScore interface * Add UUID to /save table (update of database schema necessary): ``` ALTER TABLE record_saves ADD SaveID varchar(64); ``` * /save immediately kills team and loads it again if the database insert fails. still TBD: * [x] saving (team) score when finishing * [x] loading team save * [x] loading initial time and birthday check * [x] /map and random map votes * [x] RFC: generate a passphrase (2-3 word) if save-code exists or no save-code is given making /save failures much more rare and save-codes more secure * [x] clean up code (removing now unused structs, ordering of functions in IScore) Co-authored-by: Zwelf <zwelf@strct.cc>
This commit is contained in:
commit
98b448ebcc
1296
data/wordlist.txt
Normal file
1296
data/wordlist.txt
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2828,12 +2828,18 @@ void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData)
|
|||
{
|
||||
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);
|
||||
|
||||
if(SetUpDb)
|
||||
thread_init(CreateTablesThread, apSqlServers[i], "CreateTables");
|
||||
|
||||
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());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -2866,11 +2872,6 @@ void CServer::ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData)
|
|||
}
|
||||
}
|
||||
|
||||
void CServer::CreateTablesThread(void *pData)
|
||||
{
|
||||
((CSqlServer *)pData)->CreateTables();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
||||
|
|
|
@ -377,8 +377,6 @@ public:
|
|||
// 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);
|
||||
|
|
|
@ -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];
|
||||
|
@ -186,13 +187,14 @@ 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), 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)
|
||||
{
|
||||
|
@ -200,6 +202,7 @@ void CSqlServer::CreateTables()
|
|||
}
|
||||
|
||||
Disconnect();
|
||||
return Success;
|
||||
}
|
||||
|
||||
void CSqlServer::executeSql(const char *pCommand)
|
||||
|
|
|
@ -17,7 +17,7 @@ public:
|
|||
|
||||
bool Connect();
|
||||
void Disconnect();
|
||||
void CreateTables();
|
||||
bool CreateTables();
|
||||
|
||||
void executeSql(const char *pCommand);
|
||||
void executeSqlQuery(const char *pQuery);
|
||||
|
@ -30,6 +30,7 @@ public:
|
|||
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;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef ENGINE_SERVER_SQL_STRING_HELPERS_H
|
||||
#define ENGINE_SERVER_SQL_STRING_HELPERS_H
|
||||
|
||||
#include <base/system.h>
|
||||
|
||||
namespace sqlstr
|
||||
{
|
||||
|
||||
|
@ -30,7 +32,7 @@ public:
|
|||
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);
|
||||
|
@ -38,6 +40,11 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
bool operator<(const CSqlString& other) const
|
||||
{
|
||||
return strcmp(m_aString, other.m_aString) < 0;
|
||||
}
|
||||
|
||||
private:
|
||||
char m_aString[size];
|
||||
char m_aClearString[size * 2 - 1];
|
||||
|
|
|
@ -386,10 +386,9 @@ void CGameContext::ConTeamTop5(IConsole::IResult *pResult, void *pUserData)
|
|||
}
|
||||
|
||||
if (pResult->NumArguments() > 0)
|
||||
pSelf->Score()->ShowTeamTop5(pResult, pResult->m_ClientID, pUserData,
|
||||
pResult->GetInteger(0));
|
||||
pSelf->Score()->ShowTeamTop5(pResult->m_ClientID, pResult->GetInteger(0));
|
||||
else
|
||||
pSelf->Score()->ShowTeamTop5(pResult, pResult->m_ClientID, pUserData);
|
||||
pSelf->Score()->ShowTeamTop5(pResult->m_ClientID);
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL)
|
||||
|
@ -417,10 +416,9 @@ void CGameContext::ConTop5(IConsole::IResult *pResult, void *pUserData)
|
|||
}
|
||||
|
||||
if (pResult->NumArguments() > 0)
|
||||
pSelf->Score()->ShowTop5(pResult, pResult->m_ClientID, pUserData,
|
||||
pResult->GetInteger(0));
|
||||
pSelf->Score()->ShowTop5(pResult->m_ClientID, pResult->GetInteger(0));
|
||||
else
|
||||
pSelf->Score()->ShowTop5(pResult, pResult->m_ClientID, pUserData);
|
||||
pSelf->Score()->ShowTop5(pResult->m_ClientID);
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL)
|
||||
|
@ -520,13 +518,16 @@ void CGameContext::ConMap(IConsole::IResult *pResult, void *pUserData)
|
|||
if (!pPlayer)
|
||||
return;
|
||||
|
||||
if(pSelf->RateLimitPlayerVote(pResult->m_ClientID) || pSelf->RateLimitPlayerMapVote(pResult->m_ClientID))
|
||||
return;
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
if(g_Config.m_SvUseSQL)
|
||||
if(pPlayer->m_LastSQLQuery + g_Config.m_SvSqlQueriesDelay * pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick())
|
||||
return;
|
||||
#endif
|
||||
|
||||
pSelf->Score()->MapVote(&pSelf->m_pMapVoteResult, pResult->m_ClientID, pResult->GetString(0));
|
||||
pSelf->Score()->MapVote(pResult->m_ClientID, pResult->GetString(0));
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
if(g_Config.m_SvUseSQL)
|
||||
|
@ -695,19 +696,20 @@ void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData)
|
|||
if(pPlayer->m_LastSQLQuery + g_Config.m_SvSqlQueriesDelay * pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick())
|
||||
return;
|
||||
|
||||
int Team = ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.m_Core.Team(pResult->m_ClientID);
|
||||
const char* pCode = "";
|
||||
if(pResult->NumArguments() > 0)
|
||||
pCode = pResult->GetString(0);
|
||||
|
||||
const char* pCode = pResult->GetString(0);
|
||||
char aCountry[5];
|
||||
if(str_length(pCode) > 3 && pCode[0] >= 'A' && pCode[0] <= 'Z' && pCode[1] >= 'A'
|
||||
if(str_length(pCode) >= 3 && pCode[0] >= 'A' && pCode[0] <= 'Z' && pCode[1] >= 'A'
|
||||
&& pCode[1] <= 'Z' && pCode[2] >= 'A' && pCode[2] <= 'Z')
|
||||
{
|
||||
if(pCode[3] == ' ')
|
||||
if(str_length(pCode) == 3 || pCode[3] == ' ')
|
||||
{
|
||||
str_copy(aCountry, pCode, 4);
|
||||
pCode = str_skip_whitespaces_const(pCode + 4);
|
||||
}
|
||||
else if(str_length(pCode) > 4 && pCode[4] == ' ')
|
||||
else if(str_length(pCode) == 4 || (str_length(pCode) > 4 && pCode[4] == ' '))
|
||||
{
|
||||
str_copy(aCountry, pCode, 5);
|
||||
pCode = str_skip_whitespaces_const(pCode + 5);
|
||||
|
@ -724,7 +726,7 @@ void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData)
|
|||
|
||||
if(str_in_list(g_Config.m_SvSqlValidServerNames, ",", aCountry))
|
||||
{
|
||||
pSelf->Score()->SaveTeam(Team, pCode, pResult->m_ClientID, aCountry);
|
||||
pSelf->Score()->SaveTeam(pResult->m_ClientID, pCode, aCountry);
|
||||
|
||||
if(g_Config.m_SvUseSQL)
|
||||
pPlayer->m_LastSQLQuery = pSelf->Server()->Tick();
|
||||
|
@ -790,8 +792,7 @@ void CGameContext::ConTeamRank(IConsole::IResult *pResult, void *pUserData)
|
|||
|
||||
if (pResult->NumArguments() > 0)
|
||||
if (!g_Config.m_SvHideScore)
|
||||
pSelf->Score()->ShowTeamRank(pResult->m_ClientID, pResult->GetString(0),
|
||||
true);
|
||||
pSelf->Score()->ShowTeamRank(pResult->m_ClientID, pResult->GetString(0));
|
||||
else
|
||||
pSelf->Console()->Print(
|
||||
IConsole::OUTPUT_LEVEL_STANDARD,
|
||||
|
@ -825,8 +826,7 @@ void CGameContext::ConRank(IConsole::IResult *pResult, void *pUserData)
|
|||
|
||||
if (pResult->NumArguments() > 0)
|
||||
if (!g_Config.m_SvHideScore)
|
||||
pSelf->Score()->ShowRank(pResult->m_ClientID, pResult->GetString(0),
|
||||
true);
|
||||
pSelf->Score()->ShowRank(pResult->m_ClientID, pResult->GetString(0));
|
||||
else
|
||||
pSelf->Console()->Print(
|
||||
IConsole::OUTPUT_LEVEL_STANDARD,
|
||||
|
@ -1548,8 +1548,7 @@ void CGameContext::ConPoints(IConsole::IResult *pResult, void *pUserData)
|
|||
|
||||
if (pResult->NumArguments() > 0)
|
||||
if (!g_Config.m_SvHideScore)
|
||||
pSelf->Score()->ShowPoints(pResult->m_ClientID, pResult->GetString(0),
|
||||
true);
|
||||
pSelf->Score()->ShowPoints(pResult->m_ClientID, pResult->GetString(0));
|
||||
else
|
||||
pSelf->Console()->Print(
|
||||
IConsole::OUTPUT_LEVEL_STANDARD,
|
||||
|
@ -1583,10 +1582,9 @@ void CGameContext::ConTopPoints(IConsole::IResult *pResult, void *pUserData)
|
|||
}
|
||||
|
||||
if (pResult->NumArguments() > 0)
|
||||
pSelf->Score()->ShowTopPoints(pResult, pResult->m_ClientID, pUserData,
|
||||
pResult->GetInteger(0));
|
||||
pSelf->Score()->ShowTopPoints(pResult->m_ClientID, pResult->GetInteger(0));
|
||||
else
|
||||
pSelf->Score()->ShowTopPoints(pResult, pResult->m_ClientID, pUserData);
|
||||
pSelf->Score()->ShowTopPoints(pResult->m_ClientID);
|
||||
|
||||
if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL)
|
||||
pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery = pSelf->Server()->Tick();
|
||||
|
|
|
@ -27,7 +27,7 @@ CHAT_COMMAND("dnd", "", CFGFLAG_CHAT|CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConD
|
|||
CHAT_COMMAND("mapinfo", "?r[map]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMapInfo, this, "Show info about the map with name r gives (current map by default)")
|
||||
CHAT_COMMAND("timeout", "?s[code]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTimeout, this, "Set timeout protection code s")
|
||||
CHAT_COMMAND("practice", "?i['0'|'1']", CFGFLAG_CHAT|CFGFLAG_SERVER, ConPractice, this, "Enable cheats (currently only /rescue) for your current team's run, but you can't earn a rank")
|
||||
CHAT_COMMAND("save", "r[code]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSave, this, "Save team with code r to current server. To save to another server, use '/save s r' where s = server (case-sensitive: GER, RUS, etc) and r = code.")
|
||||
CHAT_COMMAND("save", "?r[code]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSave, this, "Save team with code r to current server. To save to another server, use '/save s r' where s = server (case-sensitive: GER, RUS, etc) and r = code.")
|
||||
CHAT_COMMAND("load", "?r[code]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConLoad, this, "Load with code r. /load to check your existing saves")
|
||||
CHAT_COMMAND("map", "?r[map]", CFGFLAG_CHAT|CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConMap, this, "Vote a map by name")
|
||||
CHAT_COMMAND("rankteam", "?r[player name]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTeamRank, this, "Shows the team rank of player with name r (your team rank by default)")
|
||||
|
|
|
@ -1503,6 +1503,26 @@ void CCharacter::HandleTiles(int Index)
|
|||
// start
|
||||
if(((m_TileIndex == TILE_BEGIN) || (m_TileFIndex == TILE_BEGIN) || FTile1 == TILE_BEGIN || FTile2 == TILE_BEGIN || FTile3 == TILE_BEGIN || FTile4 == TILE_BEGIN || Tile1 == TILE_BEGIN || Tile2 == TILE_BEGIN || Tile3 == TILE_BEGIN || Tile4 == TILE_BEGIN) && (m_DDRaceState == DDRACE_NONE || m_DDRaceState == DDRACE_FINISHED || (m_DDRaceState == DDRACE_STARTED && !Team() && g_Config.m_SvTeam != 3)))
|
||||
{
|
||||
if(Teams()->GetSaving(Team()))
|
||||
{
|
||||
if(m_LastStartWarning < Server()->Tick() - 3 * Server()->TickSpeed())
|
||||
{
|
||||
GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You can't start while loading/saving of team is in progress");
|
||||
m_LastStartWarning = Server()->Tick();
|
||||
}
|
||||
Die(GetPlayer()->GetCID(), WEAPON_WORLD);
|
||||
return;
|
||||
}
|
||||
if(g_Config.m_SvTeam == 2 && (Team() == TEAM_FLOCK || Teams()->Count(Team()) <= 1))
|
||||
{
|
||||
if(m_LastStartWarning < Server()->Tick() - 3 * Server()->TickSpeed())
|
||||
{
|
||||
GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You have to be in a team with other tees to start");
|
||||
m_LastStartWarning = Server()->Tick();
|
||||
}
|
||||
Die(GetPlayer()->GetCID(), WEAPON_WORLD);
|
||||
return;
|
||||
}
|
||||
if(g_Config.m_SvResetPickups)
|
||||
{
|
||||
for (int i = WEAPON_SHOTGUN; i < NUM_WEAPONS; ++i)
|
||||
|
@ -1512,16 +1532,6 @@ void CCharacter::HandleTiles(int Index)
|
|||
m_Core.m_ActiveWeapon = WEAPON_GUN;
|
||||
}
|
||||
}
|
||||
if(g_Config.m_SvTeam == 2 && (Team() == TEAM_FLOCK || Teams()->Count(Team()) <= 1))
|
||||
{
|
||||
if(m_LastStartWarning < Server()->Tick() - 3 * Server()->TickSpeed())
|
||||
{
|
||||
GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You have to be in a team with other tees to start");
|
||||
m_LastStartWarning = Server()->Tick();
|
||||
}
|
||||
Die(GetPlayer()->GetCID(), WEAPON_WORLD);
|
||||
return;
|
||||
}
|
||||
|
||||
Teams()->OnCharacterStart(m_pPlayer->GetCID());
|
||||
m_CpActive = -2;
|
||||
|
|
|
@ -57,9 +57,6 @@ void CGameContext::Construct(int Resetting)
|
|||
m_ChatResponseTargetID = -1;
|
||||
m_aDeleteTempfile[0] = 0;
|
||||
m_TeeHistorianActive = false;
|
||||
|
||||
m_pRandomMapResult = nullptr;
|
||||
m_pMapVoteResult = nullptr;
|
||||
}
|
||||
|
||||
CGameContext::CGameContext(int Resetting)
|
||||
|
@ -901,6 +898,7 @@ void CGameContext::OnTick()
|
|||
}
|
||||
|
||||
if(Collision()->m_NumSwitchers > 0)
|
||||
{
|
||||
for (int i = 0; i < Collision()->m_NumSwitchers+1; ++i)
|
||||
{
|
||||
for (int j = 0; j < MAX_CLIENTS; ++j)
|
||||
|
@ -919,28 +917,6 @@ void CGameContext::OnTick()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(m_pRandomMapResult && m_pRandomMapResult->m_Done)
|
||||
{
|
||||
str_copy(g_Config.m_SvMap, m_pRandomMapResult->m_aMap, sizeof(g_Config.m_SvMap));
|
||||
m_pRandomMapResult = NULL;
|
||||
}
|
||||
|
||||
if(m_pMapVoteResult && m_pMapVoteResult->m_Done)
|
||||
{
|
||||
m_VoteKick = false;
|
||||
m_VoteSpec = false;
|
||||
m_LastMapVote = time_get();
|
||||
|
||||
char aCmd[256];
|
||||
str_format(aCmd, sizeof(aCmd), "sv_reset_file types/%s/flexreset.cfg; change_map \"%s\"", m_pMapVoteResult->m_aServer, m_pMapVoteResult->m_aMap);
|
||||
|
||||
char aChatmsg[512];
|
||||
str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", Server()->ClientName(m_pMapVoteResult->m_ClientID), m_pMapVoteResult->m_aMap, "/map");
|
||||
|
||||
CallVote(m_pMapVoteResult->m_ClientID, m_pMapVoteResult->m_aMap, aCmd, "/map", aChatmsg);
|
||||
|
||||
m_pMapVoteResult = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONF_DEBUG
|
||||
|
@ -1076,8 +1052,7 @@ void CGameContext::OnClientEnter(int ClientID)
|
|||
|
||||
// Can't set score here as LoadScore() is threaded, run it in
|
||||
// LoadScoreThreaded() instead
|
||||
Score()->LoadScore(ClientID);
|
||||
Score()->CheckBirthday(ClientID);
|
||||
Score()->LoadPlayerData(ClientID);
|
||||
|
||||
{
|
||||
int Empty = -1;
|
||||
|
@ -1424,68 +1399,8 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
|
|||
}
|
||||
else if(MsgID == NETMSGTYPE_CL_CALLVOTE)
|
||||
{
|
||||
int64 Now = Server()->Tick();
|
||||
int64 TickSpeed = Server()->TickSpeed();
|
||||
|
||||
if(g_Config.m_SvRconVote && !Server()->GetAuthedState(ClientID))
|
||||
{
|
||||
SendChatTarget(ClientID, "You can only vote after logging in.");
|
||||
if(RateLimitPlayerVote(ClientID))
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_Config.m_SvDnsblVote && !m_pServer->DnsblWhite(ClientID) && Server()->DistinctClientCount() > 1)
|
||||
{
|
||||
// blacklisted by dnsbl
|
||||
SendChatTarget(ClientID, "You are not allowed to vote due to DNSBL.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry + TickSpeed * 3 > Now)
|
||||
return;
|
||||
|
||||
pPlayer->m_LastVoteTry = Now;
|
||||
if(g_Config.m_SvSpectatorVotes == 0 && pPlayer->GetTeam() == TEAM_SPECTATORS)
|
||||
{
|
||||
SendChatTarget(ClientID, "Spectators aren't allowed to start a vote.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_VoteCloseTime)
|
||||
{
|
||||
SendChatTarget(ClientID, "Wait for current vote to end before calling a new one.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(Now < pPlayer->m_FirstVoteTick)
|
||||
{
|
||||
char aBuf[64];
|
||||
str_format(aBuf, sizeof(aBuf), "You must wait %d seconds before making your first vote.", (int)((pPlayer->m_FirstVoteTick - Now) / TickSpeed) + 1);
|
||||
SendChatTarget(ClientID, aBuf);
|
||||
return;
|
||||
}
|
||||
|
||||
int TimeLeft = pPlayer->m_LastVoteCall + TickSpeed * g_Config.m_SvVoteDelay - Now;
|
||||
if(pPlayer->m_LastVoteCall && TimeLeft > 0)
|
||||
{
|
||||
char aChatmsg[64];
|
||||
str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote.", (int)(TimeLeft / TickSpeed) + 1);
|
||||
SendChatTarget(ClientID, aChatmsg);
|
||||
return;
|
||||
}
|
||||
|
||||
NETADDR Addr;
|
||||
Server()->GetClientAddr(ClientID, &Addr);
|
||||
int VoteMuted = 0;
|
||||
for(int i = 0; i < m_NumVoteMutes && !VoteMuted; i++)
|
||||
if(!net_addr_comp_noport(&Addr, &m_aVoteMutes[i].m_Addr))
|
||||
VoteMuted = (m_aVoteMutes[i].m_Expire - Server()->Tick()) / Server()->TickSpeed();
|
||||
if(VoteMuted > 0)
|
||||
{
|
||||
char aChatmsg[64];
|
||||
str_format(aChatmsg, sizeof(aChatmsg), "You are not permitted to vote for the next %d seconds.", VoteMuted);
|
||||
SendChatTarget(ClientID, aChatmsg);
|
||||
return;
|
||||
}
|
||||
|
||||
char aChatmsg[512] = {0};
|
||||
char aDesc[VOTE_DESC_LENGTH] = {0};
|
||||
|
@ -1516,11 +1431,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
|
|||
SendChatTarget(ClientID, "Invalid option");
|
||||
return;
|
||||
}
|
||||
if(!Authed && (str_startswith(pOption->m_aCommand, "sv_map ") || str_startswith(pOption->m_aCommand, "change_map ") || str_startswith(pOption->m_aCommand, "random_map") || str_startswith(pOption->m_aCommand, "random_unfinished_map")) && time_get() < m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay))
|
||||
if((str_startswith(pOption->m_aCommand, "sv_map ")
|
||||
|| str_startswith(pOption->m_aCommand, "change_map ")
|
||||
|| str_startswith(pOption->m_aCommand, "random_map")
|
||||
|| str_startswith(pOption->m_aCommand, "random_unfinished_map"))
|
||||
&& RateLimitPlayerMapVote(ClientID))
|
||||
{
|
||||
str_format(aChatmsg, sizeof(aChatmsg), "There's a %d second delay between map-votes, please wait %d seconds.", g_Config.m_SvVoteMapTimeDelay, (int)(((m_LastMapVote+(g_Config.m_SvVoteMapTimeDelay * time_freq()))/time_freq())-(time_get()/time_freq())));
|
||||
SendChatTarget(ClientID, aChatmsg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1877,18 +1793,8 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID)
|
|||
// reload scores
|
||||
|
||||
Score()->PlayerData(ClientID)->Reset();
|
||||
Score()->LoadScore(ClientID);
|
||||
Score()->PlayerData(ClientID)->m_CurrentTime = Score()->PlayerData(ClientID)->m_BestTime;
|
||||
|
||||
// -9999 stands for no time and isn't displayed in scoreboard, so
|
||||
// shift the time by a second if the player actually took 9999
|
||||
// seconds to finish the map.
|
||||
if(!Score()->PlayerData(ClientID)->m_BestTime)
|
||||
m_apPlayers[ClientID]->m_Score = -9999;
|
||||
else if((int)Score()->PlayerData(ClientID)->m_BestTime == -9999)
|
||||
m_apPlayers[ClientID]->m_Score = -10000;
|
||||
else
|
||||
m_apPlayers[ClientID]->m_Score = Score()->PlayerData(ClientID)->m_BestTime;
|
||||
m_apPlayers[ClientID]->m_Score = -9999;
|
||||
Score()->LoadPlayerData(ClientID);
|
||||
}
|
||||
Server()->SetClientClan(ClientID, pMsg->m_pClan);
|
||||
Server()->SetClientCountry(ClientID, pMsg->m_Country);
|
||||
|
@ -2241,7 +2147,7 @@ void CGameContext::ConRandomMap(IConsole::IResult *pResult, void *pUserData)
|
|||
|
||||
int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1;
|
||||
|
||||
pSelf->m_pScore->RandomMap(&pSelf->m_pRandomMapResult, pSelf->m_VoteCreator, Stars);
|
||||
pSelf->m_pScore->RandomMap(pSelf->m_VoteCreator, Stars);
|
||||
}
|
||||
|
||||
void CGameContext::ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUserData)
|
||||
|
@ -2250,7 +2156,7 @@ void CGameContext::ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUse
|
|||
|
||||
int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1;
|
||||
|
||||
pSelf->m_pScore->RandomUnfinishedMap(&pSelf->m_pRandomMapResult, pSelf->m_VoteCreator, Stars);
|
||||
pSelf->m_pScore->RandomUnfinishedMap(pSelf->m_VoteCreator, Stars);
|
||||
}
|
||||
|
||||
void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData)
|
||||
|
@ -3591,3 +3497,84 @@ void CGameContext::ForceVote(int EnforcerID, bool Success)
|
|||
str_format(aBuf, sizeof(aBuf), "forcing vote %s", pOption);
|
||||
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
||||
}
|
||||
|
||||
bool CGameContext::RateLimitPlayerVote(int ClientID)
|
||||
{
|
||||
int64 Now = Server()->Tick();
|
||||
int64 TickSpeed = Server()->TickSpeed();
|
||||
CPlayer *pPlayer = m_apPlayers[ClientID];
|
||||
|
||||
if(g_Config.m_SvRconVote && !Server()->GetAuthedState(ClientID))
|
||||
{
|
||||
SendChatTarget(ClientID, "You can only vote after logging in.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (g_Config.m_SvDnsblVote && !m_pServer->DnsblWhite(ClientID) && Server()->DistinctClientCount() > 1)
|
||||
{
|
||||
// blacklisted by dnsbl
|
||||
SendChatTarget(ClientID, "You are not allowed to vote due to DNSBL.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry + TickSpeed * 3 > Now)
|
||||
return true;
|
||||
|
||||
pPlayer->m_LastVoteTry = Now;
|
||||
if(g_Config.m_SvSpectatorVotes == 0 && pPlayer->GetTeam() == TEAM_SPECTATORS)
|
||||
{
|
||||
SendChatTarget(ClientID, "Spectators aren't allowed to start a vote.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(m_VoteCloseTime)
|
||||
{
|
||||
SendChatTarget(ClientID, "Wait for current vote to end before calling a new one.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(Now < pPlayer->m_FirstVoteTick)
|
||||
{
|
||||
char aBuf[64];
|
||||
str_format(aBuf, sizeof(aBuf), "You must wait %d seconds before making your first vote.", (int)((pPlayer->m_FirstVoteTick - Now) / TickSpeed) + 1);
|
||||
SendChatTarget(ClientID, aBuf);
|
||||
return true;
|
||||
}
|
||||
|
||||
int TimeLeft = pPlayer->m_LastVoteCall + TickSpeed * g_Config.m_SvVoteDelay - Now;
|
||||
if(pPlayer->m_LastVoteCall && TimeLeft > 0)
|
||||
{
|
||||
char aChatmsg[64];
|
||||
str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote.", (int)(TimeLeft / TickSpeed) + 1);
|
||||
SendChatTarget(ClientID, aChatmsg);
|
||||
return true;
|
||||
}
|
||||
|
||||
NETADDR Addr;
|
||||
Server()->GetClientAddr(ClientID, &Addr);
|
||||
int VoteMuted = 0;
|
||||
for(int i = 0; i < m_NumVoteMutes && !VoteMuted; i++)
|
||||
if(!net_addr_comp_noport(&Addr, &m_aVoteMutes[i].m_Addr))
|
||||
VoteMuted = (m_aVoteMutes[i].m_Expire - Server()->Tick()) / Server()->TickSpeed();
|
||||
if(VoteMuted > 0)
|
||||
{
|
||||
char aChatmsg[64];
|
||||
str_format(aChatmsg, sizeof(aChatmsg), "You are not permitted to vote for the next %d seconds.", VoteMuted);
|
||||
SendChatTarget(ClientID, aChatmsg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CGameContext::RateLimitPlayerMapVote(int ClientID)
|
||||
{
|
||||
if(!Server()->GetAuthedState(ClientID) && time_get() < m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay))
|
||||
{
|
||||
char aChatmsg[512] = {0};
|
||||
str_format(aChatmsg, sizeof(aChatmsg), "There's a %d second delay between map-votes, please wait %d seconds.",
|
||||
g_Config.m_SvVoteMapTimeDelay, (int)((m_LastMapVote + g_Config.m_SvVoteMapTimeDelay * time_freq() - time_get())/time_freq()));
|
||||
SendChatTarget(ClientID, aChatmsg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -57,8 +57,6 @@ enum
|
|||
class IConsole;
|
||||
class IEngine;
|
||||
class IStorage;
|
||||
class CRandomMapResult;
|
||||
class CMapVoteResult;
|
||||
struct CAntibotData;
|
||||
|
||||
class CGameContext : public IGameServer
|
||||
|
@ -81,9 +79,6 @@ class CGameContext : public IGameServer
|
|||
CMapBugs m_MapBugs;
|
||||
CPrng m_Prng;
|
||||
|
||||
std::shared_ptr<CRandomMapResult> m_pRandomMapResult;
|
||||
std::shared_ptr<CMapVoteResult> m_pMapVoteResult;
|
||||
|
||||
static void CommandCallback(int ClientID, int FlagMask, const char *pCmd, IConsole::IResult *pResult, void *pUser);
|
||||
static void TeeHistorianWrite(const void *pData, int DataSize, void *pUser);
|
||||
|
||||
|
@ -264,6 +259,10 @@ public:
|
|||
bool PlayerModerating();
|
||||
void ForceVote(int EnforcerID, bool Success);
|
||||
|
||||
// Checks if player can vote and notify them about the reason
|
||||
bool RateLimitPlayerVote(int ClientID);
|
||||
bool RateLimitPlayerMapVote(int ClientID);
|
||||
|
||||
private:
|
||||
|
||||
bool m_VoteWillPass;
|
||||
|
|
|
@ -9,8 +9,12 @@
|
|||
#include "DDRace.h"
|
||||
#include "gamemode.h"
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
#include <game/server/score/sql_score.h>
|
||||
#endif
|
||||
|
||||
CGameControllerDDRace::CGameControllerDDRace(class CGameContext *pGameServer) :
|
||||
IGameController(pGameServer), m_Teams(pGameServer)
|
||||
IGameController(pGameServer), m_Teams(pGameServer), m_pInitResult(nullptr)
|
||||
{
|
||||
m_pGameType = g_Config.m_SvTestingCommands ? TEST_NAME : GAME_NAME;
|
||||
|
||||
|
@ -25,6 +29,18 @@ CGameControllerDDRace::~CGameControllerDDRace()
|
|||
void CGameControllerDDRace::Tick()
|
||||
{
|
||||
IGameController::Tick();
|
||||
#if defined(CONF_SQL)
|
||||
m_Teams.ProcessSaveTeam();
|
||||
|
||||
if(m_pInitResult != nullptr && m_pInitResult.use_count() == 1)
|
||||
{
|
||||
if(m_pInitResult->m_Done)
|
||||
{
|
||||
m_CurrentRecord = m_pInitResult->m_CurrentRecord;
|
||||
}
|
||||
m_pInitResult = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CGameControllerDDRace::InitTeleporter()
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
struct CSqlInitResult;
|
||||
class CGameControllerDDRace: public IGameController
|
||||
{
|
||||
public:
|
||||
|
@ -22,5 +23,7 @@ public:
|
|||
|
||||
void InitTeleporter();
|
||||
virtual void Tick();
|
||||
|
||||
std::shared_ptr<CSqlInitResult> m_pInitResult;
|
||||
};
|
||||
#endif // GAME_SERVER_GAMEMODES_DDRACE_H
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#include "gamemodes/DDRace.h"
|
||||
#include <time.h>
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
#include "score/sql_score.h"
|
||||
#endif
|
||||
|
||||
MACRO_ALLOC_POOL_ID_IMPL(CPlayer, MAX_CLIENTS)
|
||||
|
||||
IServer *CPlayer::Server() const { return m_pGameServer->Server(); }
|
||||
|
@ -41,7 +45,6 @@ void CPlayer::Reset()
|
|||
m_JoinTick = Server()->Tick();
|
||||
delete m_pCharacter;
|
||||
m_pCharacter = 0;
|
||||
m_KillMe = 0;
|
||||
m_SpectatorID = SPEC_FREEVIEW;
|
||||
m_LastActionTick = Server()->Tick();
|
||||
m_TeamChangeTick = Server()->Tick();
|
||||
|
@ -119,6 +122,8 @@ void CPlayer::Reset()
|
|||
m_Last_Team = 0;
|
||||
#if defined(CONF_SQL)
|
||||
m_LastSQLQuery = 0;
|
||||
m_SqlQueryResult = nullptr;
|
||||
m_SqlFinishResult = nullptr;
|
||||
#endif
|
||||
|
||||
int64 Now = Server()->Tick();
|
||||
|
@ -127,7 +132,7 @@ void CPlayer::Reset()
|
|||
// non-empty, allow them to vote immediately. This allows players to
|
||||
// vote after map changes or when they join an empty server.
|
||||
//
|
||||
// Otherwise, block voting in the begnning after joining.
|
||||
// Otherwise, block voting in the beginning after joining.
|
||||
if(Now > GameServer()->m_NonEmptySince + 10 * TickSpeed)
|
||||
m_FirstVoteTick = Now + g_Config.m_SvJoinVoteDelay * TickSpeed;
|
||||
else
|
||||
|
@ -143,16 +148,35 @@ void CPlayer::Tick()
|
|||
#ifdef CONF_DEBUG
|
||||
if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies)
|
||||
#endif
|
||||
#if defined(CONF_SQL)
|
||||
if(m_SqlQueryResult != nullptr && m_SqlQueryResult.use_count() == 1)
|
||||
{
|
||||
ProcessSqlResult(*m_SqlQueryResult);
|
||||
m_SqlQueryResult = nullptr;
|
||||
}
|
||||
if(m_SqlFinishResult != nullptr && m_SqlFinishResult.use_count() == 1)
|
||||
{
|
||||
ProcessSqlResult(*m_SqlFinishResult);
|
||||
m_SqlFinishResult = nullptr;
|
||||
}
|
||||
if(m_SqlRandomMapResult!= nullptr && m_SqlRandomMapResult.use_count() == 1)
|
||||
{
|
||||
if(m_SqlRandomMapResult->m_Done)
|
||||
{
|
||||
if(m_SqlRandomMapResult->m_aMessage[0] != '\0')
|
||||
GameServer()->SendChatTarget(m_ClientID, m_SqlRandomMapResult->m_aMessage);
|
||||
if(m_SqlRandomMapResult->m_Map[0] != '\0')
|
||||
str_copy(g_Config.m_SvMap, m_SqlRandomMapResult->m_Map, sizeof(g_Config.m_SvMap));
|
||||
else
|
||||
GameServer()->m_LastMapVote = 0;
|
||||
}
|
||||
m_SqlRandomMapResult = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!Server()->ClientIngame(m_ClientID))
|
||||
return;
|
||||
|
||||
if(m_KillMe != 0)
|
||||
{
|
||||
KillCharacter(m_KillMe);
|
||||
m_KillMe = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ChatScore > 0)
|
||||
m_ChatScore--;
|
||||
|
||||
|
@ -233,7 +257,7 @@ void CPlayer::Tick()
|
|||
int CurrentIndex = GameServer()->Collision()->GetMapIndex(m_ViewPos);
|
||||
m_TuneZone = GameServer()->Collision()->IsTune(CurrentIndex);
|
||||
|
||||
if (m_TuneZone != m_TuneZoneOld) // don't send tunigs all the time
|
||||
if (m_TuneZone != m_TuneZoneOld) // don't send tunings all the time
|
||||
{
|
||||
GameServer()->SendTuningParams(m_ClientID, m_TuneZone);
|
||||
}
|
||||
|
@ -258,11 +282,11 @@ void CPlayer::PostTick()
|
|||
|
||||
void CPlayer::PostPostTick()
|
||||
{
|
||||
#ifdef CONF_DEBUG
|
||||
if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies)
|
||||
#endif
|
||||
if(!Server()->ClientIngame(m_ClientID))
|
||||
return;
|
||||
#ifdef CONF_DEBUG
|
||||
if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies)
|
||||
#endif
|
||||
if(!Server()->ClientIngame(m_ClientID))
|
||||
return;
|
||||
|
||||
if(!GameServer()->m_World.m_Paused && !m_pCharacter && m_Spawning && m_WeakHookSpawn)
|
||||
TryRespawn();
|
||||
|
@ -491,11 +515,6 @@ CCharacter *CPlayer::GetCharacter()
|
|||
return 0;
|
||||
}
|
||||
|
||||
void CPlayer::ThreadKillCharacter(int Weapon)
|
||||
{
|
||||
m_KillMe = Weapon;
|
||||
}
|
||||
|
||||
void CPlayer::KillCharacter(int Weapon)
|
||||
{
|
||||
if(m_pCharacter)
|
||||
|
@ -782,3 +801,79 @@ void CPlayer::SpectatePlayerName(const char *pName)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
void CPlayer::ProcessSqlResult(CSqlPlayerResult &Result)
|
||||
{
|
||||
if(Result.m_Done) // SQL request was successful
|
||||
{
|
||||
int NumMessages = (int)(sizeof(Result.m_aaMessages)/sizeof(Result.m_aaMessages[0]));
|
||||
switch(Result.m_MessageKind)
|
||||
{
|
||||
case CSqlPlayerResult::DIRECT:
|
||||
for(int i = 0; i < NumMessages; i++)
|
||||
{
|
||||
if(Result.m_aaMessages[i][0] == 0)
|
||||
break;
|
||||
GameServer()->SendChatTarget(m_ClientID, Result.m_aaMessages[i]);
|
||||
}
|
||||
break;
|
||||
case CSqlPlayerResult::ALL:
|
||||
for(int i = 0; i < NumMessages; i++)
|
||||
{
|
||||
if(Result.m_aaMessages[i][0] == 0)
|
||||
break;
|
||||
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, Result.m_aaMessages[i]);
|
||||
}
|
||||
break;
|
||||
case CSqlPlayerResult::BROADCAST:
|
||||
if(Result.m_Data.m_Broadcast[0] != 0)
|
||||
GameServer()->SendBroadcast(Result.m_Data.m_Broadcast, -1);
|
||||
break;
|
||||
case CSqlPlayerResult::MAP_VOTE:
|
||||
GameServer()->m_VoteKick = false;
|
||||
GameServer()->m_VoteSpec = false;
|
||||
GameServer()->m_LastMapVote = time_get();
|
||||
|
||||
char aCmd[256];
|
||||
str_format(aCmd, sizeof(aCmd),
|
||||
"sv_reset_file types/%s/flexreset.cfg; change_map \"%s\"",
|
||||
Result.m_Data.m_MapVote.m_Server, Result.m_Data.m_MapVote.m_Map);
|
||||
|
||||
char aChatmsg[512];
|
||||
str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)",
|
||||
Server()->ClientName(m_ClientID), Result.m_Data.m_MapVote.m_Map, "/map");
|
||||
|
||||
GameServer()->CallVote(m_ClientID, Result.m_Data.m_MapVote.m_Map, aCmd, "/map", aChatmsg);
|
||||
break;
|
||||
case CSqlPlayerResult::PLAYER_INFO:
|
||||
GameServer()->Score()->PlayerData(m_ClientID)->Set(
|
||||
Result.m_Data.m_Info.m_Time,
|
||||
Result.m_Data.m_Info.m_CpTime
|
||||
);
|
||||
m_Score = Result.m_Data.m_Info.m_Score;
|
||||
m_HasFinishScore = Result.m_Data.m_Info.m_HasFinishScore;
|
||||
// -9999 stands for no time and isn't displayed in scoreboard, so
|
||||
// shift the time by a second if the player actually took 9999
|
||||
// seconds to finish the map.
|
||||
if(m_HasFinishScore && m_Score == -9999)
|
||||
m_Score = -10000;
|
||||
Server()->ExpireServerInfo();
|
||||
int Birthday = Result.m_Data.m_Info.m_Birthday;
|
||||
if(Birthday != 0)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"Happy DDNet birthday to %s for finishing their first map %d year%s ago!",
|
||||
Server()->ClientName(m_ClientID), Birthday, Birthday > 1 ? "s" : "");
|
||||
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, m_ClientID);
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"Happy DDNet birthday, %s!\nYou have finished your first map exactly %d year%s ago!",
|
||||
Server()->ClientName(m_ClientID), Birthday, Birthday > 1 ? "s" : "");
|
||||
GameServer()->SendBroadcast(aBuf, m_ClientID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -5,7 +5,14 @@
|
|||
|
||||
// this include should perhaps be removed
|
||||
#include "entities/character.h"
|
||||
#include "score.h"
|
||||
#include "gamecontext.h"
|
||||
#include <memory>
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
class CSqlPlayerResult;
|
||||
class CSqlRandomMapResult;
|
||||
#endif
|
||||
|
||||
// player object
|
||||
class CPlayer
|
||||
|
@ -41,7 +48,6 @@ public:
|
|||
void OnPredictedEarlyInput(CNetObj_PlayerInput *NewInput);
|
||||
void OnDisconnect(const char *pReason);
|
||||
|
||||
void ThreadKillCharacter(int Weapon = WEAPON_GAME);
|
||||
void KillCharacter(int Weapon = WEAPON_GAME);
|
||||
CCharacter *GetCharacter();
|
||||
|
||||
|
@ -168,7 +174,6 @@ public:
|
|||
bool m_SpecTeam;
|
||||
bool m_NinjaJetpack;
|
||||
bool m_Afk;
|
||||
int m_KillMe;
|
||||
bool m_HasFinishScore;
|
||||
|
||||
int m_ChatScore;
|
||||
|
@ -194,7 +199,11 @@ public:
|
|||
bool m_Halloween;
|
||||
bool m_FirstPacket;
|
||||
#if defined(CONF_SQL)
|
||||
void ProcessSqlResult(CSqlPlayerResult &Result);
|
||||
int64 m_LastSQLQuery;
|
||||
std::shared_ptr<CSqlPlayerResult> m_SqlQueryResult;
|
||||
std::shared_ptr<CSqlPlayerResult> m_SqlFinishResult;
|
||||
std::shared_ptr<CSqlRandomMapResult> m_SqlRandomMapResult;
|
||||
#endif
|
||||
bool m_NotEligibleForFinish;
|
||||
int64 m_EligibleForFinishCheck;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#include "save.h"
|
||||
|
||||
#include <new>
|
||||
#include <cstdio>
|
||||
|
||||
#include "save.h"
|
||||
#include "teams.h"
|
||||
#include "./gamemodes/DDRace.h"
|
||||
#include "gamemodes/DDRace.h"
|
||||
#include <engine/shared/config.h>
|
||||
|
||||
CSaveTee::CSaveTee()
|
||||
|
@ -16,13 +17,14 @@ CSaveTee::~CSaveTee()
|
|||
|
||||
void CSaveTee::save(CCharacter *pChr)
|
||||
{
|
||||
str_copy(m_aName, pChr->m_pPlayer->Server()->ClientName(pChr->m_pPlayer->GetCID()), sizeof(m_aName));
|
||||
m_ClientID = pChr->m_pPlayer->GetCID();
|
||||
str_copy(m_aName, pChr->m_pPlayer->Server()->ClientName(m_ClientID), sizeof(m_aName));
|
||||
|
||||
m_Alive = pChr->m_Alive;
|
||||
m_Paused = abs(pChr->m_pPlayer->IsPaused());
|
||||
m_NeededFaketuning = pChr->m_NeededFaketuning;
|
||||
|
||||
m_TeeFinished = pChr->Teams()->TeeFinished(pChr->m_pPlayer->GetCID());
|
||||
m_TeeFinished = pChr->Teams()->TeeFinished(m_ClientID);
|
||||
m_IsSolo = pChr->m_Solo;
|
||||
|
||||
for(int i = 0; i< NUM_WEAPONS; i++)
|
||||
|
@ -51,7 +53,9 @@ void CSaveTee::save(CCharacter *pChr)
|
|||
m_TuneZoneOld = pChr->m_TuneZoneOld;
|
||||
|
||||
if(pChr->m_StartTime)
|
||||
m_Time = pChr->Server()->Tick() - pChr->m_StartTime + 60 * pChr->Server()->TickSpeed();
|
||||
m_Time = pChr->Server()->Tick() - pChr->m_StartTime;
|
||||
else
|
||||
m_Time = 0;
|
||||
|
||||
m_Pos = pChr->m_Pos;
|
||||
m_PrevPos = pChr->m_PrevPos;
|
||||
|
@ -182,6 +186,9 @@ void CSaveTee::load(CCharacter *pChr, int Team)
|
|||
|
||||
char* CSaveTee::GetString()
|
||||
{
|
||||
// Add time penalty of 60 seconds (only to the database)
|
||||
int Time = m_Time + 60 * SERVER_TICK_SPEED;
|
||||
|
||||
str_format(m_aString, sizeof(m_aString),
|
||||
"%s\t%d\t%d\t%d\t%d\t%d\t"
|
||||
// weapons
|
||||
|
@ -222,7 +229,7 @@ char* CSaveTee::GetString()
|
|||
m_LastWeapon, m_QueuedWeapon,
|
||||
// tee states
|
||||
m_SuperJump, m_Jetpack, m_NinjaJetpack, m_FreezeTime, m_FreezeTick, m_DeepFreeze, m_EndlessHook,
|
||||
m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, m_Time,
|
||||
m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, Time,
|
||||
(int)m_Pos.x, (int)m_Pos.y, (int)m_PrevPos.x, (int)m_PrevPos.y,
|
||||
m_TeleCheckpoint, m_LastPenalty,
|
||||
(int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y,
|
||||
|
@ -243,7 +250,7 @@ char* CSaveTee::GetString()
|
|||
return m_aString;
|
||||
}
|
||||
|
||||
int CSaveTee::LoadString(char* String)
|
||||
int CSaveTee::LoadString(const char* String)
|
||||
{
|
||||
int Num;
|
||||
Num = sscanf(String,
|
||||
|
@ -398,6 +405,8 @@ bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameCon
|
|||
{
|
||||
switch(Result)
|
||||
{
|
||||
case 0:
|
||||
return false;
|
||||
case 1:
|
||||
pGameContext->SendChatTarget(ClientID, "You have to be in a team (from 1-63)");
|
||||
break;
|
||||
|
@ -408,44 +417,19 @@ bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameCon
|
|||
pGameContext->SendChatTarget(ClientID, "Unable to find all Characters");
|
||||
break;
|
||||
case 4:
|
||||
pGameContext->SendChatTarget(ClientID, "Your team is not started yet");
|
||||
pGameContext->SendChatTarget(ClientID, "Your team has not started yet");
|
||||
break;
|
||||
default: // this state should never be reached
|
||||
pGameContext->SendChatTarget(ClientID, "Unknown error while saving");
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CSaveTeam::load(int Team)
|
||||
void CSaveTeam::load(int Team)
|
||||
{
|
||||
if(Team <= 0 || Team >= MAX_CLIENTS)
|
||||
return 1;
|
||||
|
||||
CGameTeams* pTeams = &(((CGameControllerDDRace*)m_pController)->m_Teams);
|
||||
|
||||
if(pTeams->Count(Team) > m_MembersCount)
|
||||
return 2;
|
||||
|
||||
CCharacter *pChr;
|
||||
|
||||
for (int i = 0; i < m_MembersCount; i++)
|
||||
{
|
||||
int ID = MatchPlayer(m_pSavedTees[i].GetName());
|
||||
if(ID == -1) // first check if team can be loaded / do not load half teams
|
||||
{
|
||||
return i+10; // +10 to leave space for other return-values
|
||||
}
|
||||
if(m_pController->GameServer()->m_apPlayers[ID] && m_pController->GameServer()->m_apPlayers[ID]->GetCharacter() && m_pController->GameServer()->m_apPlayers[ID]->GetCharacter()->m_DDRaceState)
|
||||
{
|
||||
return i+100; // +100 to leave space for other return-values
|
||||
}
|
||||
if(Team != pTeams->m_Core.Team(ID))
|
||||
{
|
||||
return i+200; // +100 to leave space for other return-values
|
||||
}
|
||||
}
|
||||
|
||||
pTeams->ChangeTeamState(Team, m_TeamState);
|
||||
pTeams->SetTeamLock(Team, m_TeamLocked);
|
||||
if(m_Practice)
|
||||
|
@ -453,14 +437,16 @@ int CSaveTeam::load(int Team)
|
|||
|
||||
for (int i = 0; i < m_MembersCount; i++)
|
||||
{
|
||||
pChr = MatchCharacter(m_pSavedTees[i].GetName(), i);
|
||||
if(pChr)
|
||||
int ClientID = m_pSavedTees[i].GetClientID();
|
||||
if(m_pController->GameServer()->m_apPlayers[ClientID] && pTeams->m_Core.Team(ClientID) == Team)
|
||||
{
|
||||
CCharacter *pChr = MatchCharacter(m_pSavedTees[i].GetClientID(), i);
|
||||
m_pSavedTees[i].load(pChr, Team);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_pController->GameServer()->Collision()->m_NumSwitchers)
|
||||
{
|
||||
for(int i=1; i < m_pController->GameServer()->Collision()->m_NumSwitchers+1; i++)
|
||||
{
|
||||
m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Status[Team] = m_pSwitchers[i].m_Status;
|
||||
|
@ -468,33 +454,15 @@ int CSaveTeam::load(int Team)
|
|||
m_pController->GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team] = m_pController->Server()->Tick() - m_pSwitchers[i].m_EndTime;
|
||||
m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Type[Team] = m_pSwitchers[i].m_Type;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int CSaveTeam::MatchPlayer(char name[16])
|
||||
CCharacter* CSaveTeam::MatchCharacter(int ClientID, int SaveID)
|
||||
{
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(str_comp(m_pController->Server()->ClientName(i), name) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
CCharacter* CSaveTeam::MatchCharacter(char name[16], int SaveID)
|
||||
{
|
||||
int ID = MatchPlayer(name);
|
||||
if(ID >= 0 && m_pController->GameServer()->m_apPlayers[ID])
|
||||
{
|
||||
if(m_pController->GameServer()->m_apPlayers[ID]->GetCharacter())
|
||||
return m_pController->GameServer()->m_apPlayers[ID]->GetCharacter();
|
||||
else
|
||||
return m_pController->GameServer()->m_apPlayers[ID]->ForceSpawn(m_pSavedTees[SaveID].GetPos());
|
||||
}
|
||||
|
||||
return 0;
|
||||
if(m_pController->GameServer()->m_apPlayers[ClientID]->GetCharacter())
|
||||
return m_pController->GameServer()->m_apPlayers[ClientID]->GetCharacter();
|
||||
else
|
||||
return m_pController->GameServer()->m_apPlayers[ClientID]->ForceSpawn(m_pSavedTees[SaveID].GetPos());
|
||||
}
|
||||
|
||||
char* CSaveTeam::GetString()
|
||||
|
@ -667,3 +635,48 @@ int CSaveTeam::LoadString(const char* String)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CSaveTeam::MatchPlayers(const char (*paNames)[MAX_NAME_LENGTH], const int *pClientID, int NumPlayer, char *pMessage, int MessageLen)
|
||||
{
|
||||
if(NumPlayer > m_MembersCount)
|
||||
{
|
||||
str_format(pMessage, MessageLen, "Too many players in this team, should be %d", m_MembersCount);
|
||||
return false;
|
||||
}
|
||||
// check for wrong players
|
||||
for(int i = 0; i < NumPlayer; i++)
|
||||
{
|
||||
int Found = false;
|
||||
for(int j = 0; j < m_MembersCount; j++)
|
||||
{
|
||||
if(strcmp(paNames[i], m_pSavedTees[j].GetName()) == 0)
|
||||
{
|
||||
Found = true;
|
||||
}
|
||||
}
|
||||
if(!Found)
|
||||
{
|
||||
str_format(pMessage, MessageLen, "'%s' don't belong to this team", paNames[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// check for missing players
|
||||
for(int i = 0; i < m_MembersCount; i++)
|
||||
{
|
||||
int Found = false;
|
||||
for(int j = 0; j < NumPlayer; j++)
|
||||
{
|
||||
if(strcmp(m_pSavedTees[i].GetName(), paNames[j]) == 0)
|
||||
{
|
||||
m_pSavedTees[i].SetClientID(pClientID[j]);
|
||||
Found = true;
|
||||
}
|
||||
}
|
||||
if(!Found)
|
||||
{
|
||||
str_format(pMessage, MessageLen, "'%s' has to be in this team", m_pSavedTees[i].GetName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#ifndef GAME_SERVER_SAVE_H
|
||||
#define GAME_SERVER_SAVE_H
|
||||
|
||||
#include "./entities/character.h"
|
||||
#include <game/server/gamecontroller.h>
|
||||
#include "entities/character.h"
|
||||
class IGameController;
|
||||
class CGameContext;
|
||||
|
||||
class CSaveTee
|
||||
{
|
||||
|
@ -12,11 +13,14 @@ public:
|
|||
void save(CCharacter* pchr);
|
||||
void load(CCharacter* pchr, int Team);
|
||||
char* GetString();
|
||||
int LoadString(char* String);
|
||||
vec2 GetPos() { return m_Pos; }
|
||||
char* GetName() { return m_aName; }
|
||||
int LoadString(const char* String);
|
||||
vec2 GetPos() const { return m_Pos; }
|
||||
const char* GetName() const { return m_aName; }
|
||||
int GetClientID() const { return m_ClientID; }
|
||||
void SetClientID(int ClientID) { m_ClientID = ClientID; };
|
||||
|
||||
private:
|
||||
int m_ClientID;
|
||||
|
||||
char m_aString [2048];
|
||||
char m_aName [16];
|
||||
|
@ -94,16 +98,19 @@ public:
|
|||
CSaveTeam(IGameController* Controller);
|
||||
~CSaveTeam();
|
||||
char* GetString();
|
||||
int GetMembersCount() { return m_MembersCount; }
|
||||
int GetMembersCount() const { return m_MembersCount; }
|
||||
// MatchPlayers has to be called afterwards
|
||||
int LoadString(const char* String);
|
||||
// returns true if a team can load, otherwise writes a nice error Message in pMessage
|
||||
bool MatchPlayers(const char (*paNames)[MAX_NAME_LENGTH], const int *pClientID, int NumPlayer, char *pMessage, int MessageLen);
|
||||
int save(int Team);
|
||||
int load(int Team);
|
||||
void load(int Team);
|
||||
CSaveTee* m_pSavedTees;
|
||||
|
||||
// returns true if an error occured
|
||||
static bool HandleSaveError(int Result, int ClientID, CGameContext *pGameContext);
|
||||
private:
|
||||
int MatchPlayer(char name[16]);
|
||||
CCharacter* MatchCharacter(char name[16], int SaveID);
|
||||
CCharacter* MatchCharacter(int ClientID, int SaveID);
|
||||
|
||||
IGameController* m_pController;
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
#define GAME_SERVER_SCORE_H
|
||||
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
#include "entities/character.h"
|
||||
#include "gamecontext.h"
|
||||
#include "save.h"
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -19,6 +19,7 @@ public:
|
|||
{
|
||||
Reset();
|
||||
}
|
||||
~CPlayerData() {}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
|
@ -31,6 +32,7 @@ public:
|
|||
void Set(float Time, float CpTime[NUM_CHECKPOINTS])
|
||||
{
|
||||
m_BestTime = Time;
|
||||
m_CurrentTime = Time;
|
||||
for(int i = 0; i < NUM_CHECKPOINTS; i++)
|
||||
m_aBestCpTime[i] = CpTime[i];
|
||||
}
|
||||
|
@ -40,28 +42,6 @@ public:
|
|||
float m_aBestCpTime[NUM_CHECKPOINTS];
|
||||
};
|
||||
|
||||
// Watch this: TODO(2019-05-20): Temporary fix for the random maps race
|
||||
// condition. See you in ten years.
|
||||
class CRandomMapResult
|
||||
{
|
||||
public:
|
||||
bool m_Done;
|
||||
char m_aMap[64];
|
||||
|
||||
CRandomMapResult() : m_Done(false) {}
|
||||
};
|
||||
|
||||
class CMapVoteResult
|
||||
{
|
||||
public:
|
||||
bool m_Done;
|
||||
char m_aMap[64];
|
||||
char m_aServer[32];
|
||||
int m_ClientID;
|
||||
|
||||
CMapVoteResult() : m_Done(false) {}
|
||||
};
|
||||
|
||||
class IScore
|
||||
{
|
||||
CPlayerData m_aPlayerData[MAX_CLIENTS];
|
||||
|
@ -72,26 +52,25 @@ public:
|
|||
CPlayerData *PlayerData(int ID) { return &m_aPlayerData[ID]; }
|
||||
|
||||
virtual void MapInfo(int ClientID, const char *pMapName) = 0;
|
||||
virtual void MapVote(std::shared_ptr<CMapVoteResult> *ppResult, int ClientID, const char *pMapName) = 0;
|
||||
virtual void CheckBirthday(int ClientID) = 0;
|
||||
virtual void LoadScore(int ClientID) = 0;
|
||||
virtual void MapVote(int ClientID, const char *pMapName) = 0;
|
||||
virtual void LoadPlayerData(int ClientID) = 0;
|
||||
virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, float aCpTime[NUM_CHECKPOINTS], bool NotEligible) = 0;
|
||||
|
||||
virtual void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp) = 0;
|
||||
|
||||
virtual void ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0;
|
||||
virtual void ShowRank(int ClientID, const char *pName, bool Search=false) = 0;
|
||||
virtual void ShowTop5(int ClientID, int Offset=1) = 0;
|
||||
virtual void ShowRank(int ClientID, const char *pName) = 0;
|
||||
|
||||
virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0;
|
||||
virtual void ShowTeamRank(int ClientID, const char *pName, bool Search=false) = 0;
|
||||
virtual void ShowTeamTop5(int ClientID, int Offset=1) = 0;
|
||||
virtual void ShowTeamRank(int ClientID, const char *pName) = 0;
|
||||
|
||||
virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0;
|
||||
virtual void ShowPoints(int ClientID, const char *pName, bool Search=false) = 0;
|
||||
virtual void ShowTopPoints(int ClientID, int Offset=1) = 0;
|
||||
virtual void ShowPoints(int ClientID, const char *pName) = 0;
|
||||
|
||||
virtual void RandomMap(std::shared_ptr<CRandomMapResult> *ppResult, int ClientID, int Stars) = 0;
|
||||
virtual void RandomUnfinishedMap(std::shared_ptr<CRandomMapResult> *ppResult, int ClientID, int Stars) = 0;
|
||||
virtual void RandomMap(int ClientID, int Stars) = 0;
|
||||
virtual void RandomUnfinishedMap(int ClientID, int Stars) = 0;
|
||||
|
||||
virtual void SaveTeam(int Team, const char *pCode, int ClientID, const char *pServer) = 0;
|
||||
virtual void SaveTeam(int ClientID, const char *pCode, const char *pServer) = 0;
|
||||
virtual void LoadTeam(const char *pCode, int ClientID) = 0;
|
||||
virtual void GetSaves(int ClientID) = 0;
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ void CFileScore::MapInfo(int ClientID, const char* MapName)
|
|||
// TODO: implement
|
||||
}
|
||||
|
||||
void CFileScore::MapVote(std::shared_ptr<CMapVoteResult> *ppResult, int ClientID, const char* MapName)
|
||||
void CFileScore::MapVote(int ClientID, const char* MapName)
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
|
@ -205,12 +205,7 @@ void CFileScore::UpdatePlayer(int ID, float Score,
|
|||
Save();
|
||||
}
|
||||
|
||||
void CFileScore::CheckBirthday(int ClientID)
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void CFileScore::LoadScore(int ClientID)
|
||||
void CFileScore::LoadPlayerData(int ClientID)
|
||||
{
|
||||
CPlayerScore *pPlayer = SearchScore(ClientID, 0);
|
||||
if (pPlayer)
|
||||
|
@ -241,37 +236,32 @@ void CFileScore::SaveScore(int ClientID, float Time, const char *pTimestamp,
|
|||
UpdatePlayer(ClientID, Time, CpTime);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTop5(IConsole::IResult *pResult, int ClientID,
|
||||
void *pUserData, int Debut)
|
||||
void CFileScore::ShowTop5(int ClientID, int Offset)
|
||||
{
|
||||
CGameContext *pSelf = (CGameContext *) pUserData;
|
||||
char aBuf[512];
|
||||
Debut = maximum(1, Debut < 0 ? m_Top.size() + Debut - 3 : Debut);
|
||||
pSelf->SendChatTarget(ClientID, "----------- Top 5 -----------");
|
||||
Offset = maximum(1, Offset < 0 ? m_Top.size() + Offset - 3 : Offset);
|
||||
GameServer()->SendChatTarget(ClientID, "----------- Top 5 -----------");
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (i + Debut > m_Top.size())
|
||||
if (i + Offset > m_Top.size())
|
||||
break;
|
||||
CPlayerScore *r = &m_Top[i + Debut - 1];
|
||||
CPlayerScore *r = &m_Top[i + Offset - 1];
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"%d. %s Time: %d minute(s) %5.2f second(s)", i + Debut,
|
||||
"%d. %s Time: %d minute(s) %5.2f second(s)", i + Offset,
|
||||
r->m_aName, (int)r->m_Score / 60,
|
||||
r->m_Score - ((int)r->m_Score / 60 * 60));
|
||||
pSelf->SendChatTarget(ClientID, aBuf);
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
pSelf->SendChatTarget(ClientID, "------------------------------");
|
||||
GameServer()->SendChatTarget(ClientID, "------------------------------");
|
||||
}
|
||||
|
||||
void CFileScore::ShowRank(int ClientID, const char* pName, bool Search)
|
||||
void CFileScore::ShowRank(int ClientID, const char* pName)
|
||||
{
|
||||
CPlayerScore *pScore;
|
||||
int Pos = -2;
|
||||
char aBuf[512];
|
||||
|
||||
if (!Search)
|
||||
pScore = SearchScore(ClientID, &Pos);
|
||||
else
|
||||
pScore = SearchName(pName, &Pos, 1);
|
||||
pScore = SearchName(pName, &Pos, 1);
|
||||
|
||||
if (pScore && Pos > -1)
|
||||
{
|
||||
|
@ -285,66 +275,63 @@ void CFileScore::ShowRank(int ClientID, const char* pName, bool Search)
|
|||
"%d. %s Time: %d minute(s) %5.2f second(s), requested by (%s)", Pos,
|
||||
pScore->m_aName, (int)Time / 60,
|
||||
Time - ((int)Time / 60 * 60), Server()->ClientName(ClientID));
|
||||
if (!Search)
|
||||
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, ClientID);
|
||||
else
|
||||
if (g_Config.m_SvHideScore)
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
else
|
||||
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, ClientID);
|
||||
return;
|
||||
}
|
||||
else if (Pos == -1)
|
||||
str_format(aBuf, sizeof(aBuf), "Several players were found.");
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), "%s is not ranked",
|
||||
Search ? pName : Server()->ClientName(ClientID));
|
||||
str_format(aBuf, sizeof(aBuf), "%s is not ranked", pName);
|
||||
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTeamTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut)
|
||||
void CFileScore::ShowTeamTop5(int ClientID, int Offset)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTeamRank(int ClientID, const char* pName, bool Search)
|
||||
void CFileScore::ShowTeamRank(int ClientID, const char* pName)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut)
|
||||
void CFileScore::ShowTopPoints(int ClientID, int Offset)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowPoints(int ClientID, const char* pName, bool Search)
|
||||
void CFileScore::ShowPoints(int ClientID, const char* pName)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Points not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::RandomMap(std::shared_ptr<CRandomMapResult> *ppResult, int ClientID, int stars)
|
||||
void CFileScore::RandomMap(int ClientID, int Stars)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Random map not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
*ppResult = NULL;
|
||||
}
|
||||
|
||||
void CFileScore::RandomUnfinishedMap(std::shared_ptr<CRandomMapResult> *ppResult, int ClientID, int stars)
|
||||
void CFileScore::RandomUnfinishedMap(int ClientID, int Stars)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Random unfinished map not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
*ppResult = NULL;
|
||||
}
|
||||
|
||||
void CFileScore::SaveTeam(int Team, const char* Code, int ClientID, const char* Server)
|
||||
void CFileScore::SaveTeam(int ClientID, const char* Code, const char* Server)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Save-function not supported in file based servers");
|
||||
|
|
|
@ -21,10 +21,8 @@ class CFileScore: public IScore
|
|||
float m_Score;
|
||||
float m_aCpTime[NUM_CHECKPOINTS];
|
||||
|
||||
CPlayerScore()
|
||||
{
|
||||
}
|
||||
;
|
||||
CPlayerScore() {}
|
||||
|
||||
CPlayerScore(const char *pName, float Score,
|
||||
float aCpTime[NUM_CHECKPOINTS]);
|
||||
|
||||
|
@ -49,7 +47,6 @@ class CFileScore: public IScore
|
|||
{
|
||||
return SearchName(Server()->ClientName(ID), pPosition, 0);
|
||||
}
|
||||
;
|
||||
|
||||
CPlayerScore *SearchName(const char *pName, int *pPosition, bool MatchCase);
|
||||
void UpdatePlayer(int ID, float Score, float aCpTime[NUM_CHECKPOINTS]);
|
||||
|
@ -63,27 +60,24 @@ public:
|
|||
CFileScore(CGameContext *pGameServer);
|
||||
~CFileScore();
|
||||
|
||||
virtual void CheckBirthday(int ClientID);
|
||||
virtual void LoadScore(int ClientID);
|
||||
virtual void LoadPlayerData(int ClientID);
|
||||
virtual void MapInfo(int ClientID, const char* MapName);
|
||||
virtual void MapVote(std::shared_ptr<CMapVoteResult> *ppResult, int ClientID, const char* MapName);
|
||||
virtual void MapVote(int ClientID, const char* MapName);
|
||||
virtual void SaveScore(int ClientID, float Time, const char *pTimestamp,
|
||||
float CpTime[NUM_CHECKPOINTS], bool NotEligible);
|
||||
virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time, const char *pTimestamp);
|
||||
|
||||
virtual void ShowTop5(IConsole::IResult *pResult, int ClientID,
|
||||
void *pUserData, int Debut = 1);
|
||||
virtual void ShowRank(int ClientID, const char* pName, bool Search = false);
|
||||
virtual void ShowTop5(int ClientID, int Offset = 1);
|
||||
virtual void ShowRank(int ClientID, const char* pName);
|
||||
|
||||
virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID,
|
||||
void *pUserData, int Debut = 1);
|
||||
virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false);
|
||||
virtual void ShowTeamTop5(int ClientID, int Offset = 1);
|
||||
virtual void ShowTeamRank(int ClientID, const char* pName);
|
||||
|
||||
virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut);
|
||||
virtual void ShowPoints(int ClientID, const char* pName, bool Search);
|
||||
virtual void RandomMap(std::shared_ptr<CRandomMapResult> *ppResult, int ClientID, int stars);
|
||||
virtual void RandomUnfinishedMap(std::shared_ptr<CRandomMapResult> *ppResult, int ClientID, int stars);
|
||||
virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server);
|
||||
virtual void ShowTopPoints(int ClientID, int Offset);
|
||||
virtual void ShowPoints(int ClientID, const char* pName);
|
||||
virtual void RandomMap(int ClientID, int Stars);
|
||||
virtual void RandomUnfinishedMap(int ClientID, int Stars);
|
||||
virtual void SaveTeam(int ClientID, const char* Code, const char* Server);
|
||||
virtual void LoadTeam(const char* Code, int ClientID);
|
||||
virtual void GetSaves(int ClientID);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,113 +1,146 @@
|
|||
/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
/* Based on Race mod stuff and tweaked by GreYFoX@GTi and others to fit our DDRace needs. */
|
||||
/* CSqlScore Class by Sushi Tee*/
|
||||
#ifndef GAME_SERVER_SCORE_SQL_SCORE_H
|
||||
#define GAME_SERVER_SCORE_SQL_SCORE_H
|
||||
#ifndef GAME_SERVER_SCORE_SQL_H
|
||||
#define GAME_SERVER_SCORE_SQL_H
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <base/system.h>
|
||||
#include <engine/console.h>
|
||||
#include <engine/server/sql_connector.h>
|
||||
#include <engine/server/sql_string_helpers.h>
|
||||
#include <game/prng.h>
|
||||
#include <game/voting.h>
|
||||
|
||||
#include "../score.h"
|
||||
|
||||
class CSqlServer;
|
||||
|
||||
class CGameContextError : public std::runtime_error
|
||||
struct CSqlPlayerResult
|
||||
{
|
||||
public:
|
||||
CGameContextError(const char* pMsg) : std::runtime_error(pMsg) {}
|
||||
std::atomic_bool m_Done;
|
||||
CSqlPlayerResult();
|
||||
|
||||
enum Variant
|
||||
{
|
||||
DIRECT,
|
||||
ALL,
|
||||
BROADCAST,
|
||||
MAP_VOTE,
|
||||
PLAYER_INFO,
|
||||
} m_MessageKind;
|
||||
char m_aaMessages[7][512];
|
||||
union {
|
||||
char m_Broadcast[1024];
|
||||
struct {
|
||||
float m_Time;
|
||||
float m_CpTime[NUM_CHECKPOINTS];
|
||||
int m_Score;
|
||||
int m_HasFinishScore;
|
||||
int m_Birthday; // 0 indicates no birthday
|
||||
} m_Info;
|
||||
struct
|
||||
{
|
||||
char m_Reason[VOTE_REASON_LENGTH];
|
||||
char m_Server[32+1];
|
||||
char m_Map[128+1];
|
||||
} m_MapVote;
|
||||
} m_Data; // PLAYER_INFO
|
||||
|
||||
void SetVariant(Variant v);
|
||||
};
|
||||
|
||||
struct CSqlRandomMapResult
|
||||
{
|
||||
std::atomic_bool m_Done;
|
||||
CSqlRandomMapResult() :
|
||||
m_Done(false)
|
||||
{
|
||||
m_Map[0] = '\0';
|
||||
m_aMessage[0] = '\0';
|
||||
}
|
||||
char m_Map[128];
|
||||
char m_aMessage[512];
|
||||
};
|
||||
|
||||
// generic implementation to provide gameserver and server
|
||||
struct CSqlSaveResult {
|
||||
CSqlSaveResult(int PlayerID, IGameController* Controller) :
|
||||
m_Status(SAVE_FAILED),
|
||||
m_SavedTeam(CSaveTeam(Controller)),
|
||||
m_RequestingPlayer(PlayerID)
|
||||
{
|
||||
m_aMessage[0] = '\0';
|
||||
m_aBroadcast[0] = '\0';
|
||||
}
|
||||
enum
|
||||
{
|
||||
SAVE_SUCCESS,
|
||||
// load team in the following two cases
|
||||
SAVE_FAILED,
|
||||
LOAD_SUCCESS,
|
||||
LOAD_FAILED,
|
||||
} m_Status;
|
||||
char m_aMessage[512];
|
||||
char m_aBroadcast[512];
|
||||
CSaveTeam m_SavedTeam;
|
||||
int m_RequestingPlayer;
|
||||
CUuid m_SaveID;
|
||||
};
|
||||
|
||||
struct CSqlInitResult
|
||||
{
|
||||
CSqlInitResult() :
|
||||
m_Done(false),
|
||||
m_CurrentRecord(0)
|
||||
{ }
|
||||
std::atomic_bool m_Done;
|
||||
float m_CurrentRecord;
|
||||
};
|
||||
|
||||
// holding relevant data for one thread, and function pointer for return values
|
||||
template < typename TResult >
|
||||
struct CSqlData
|
||||
{
|
||||
CSqlData() : m_Map(ms_pMap), m_GameUuid(ms_pGameUuid)
|
||||
{
|
||||
m_Instance = ms_Instance;
|
||||
}
|
||||
|
||||
virtual ~CSqlData() {}
|
||||
|
||||
bool isGameContextVaild() const
|
||||
{
|
||||
return m_Instance == ms_Instance && ms_GameContextAvailable;
|
||||
}
|
||||
|
||||
CGameContext* GameServer() const { return isGameContextVaild() ? ms_pGameServer : throw CGameContextError("[CSqlData]: GameServer() unavailable."); }
|
||||
IServer* Server() const { return isGameContextVaild() ? ms_pServer : throw CGameContextError("[CSqlData]: Server() unavailable."); }
|
||||
CPlayerData* PlayerData(int ID) const { return isGameContextVaild() ? &ms_pPlayerData[ID] : throw CGameContextError("[CSqlData]: PlayerData() unavailable."); }
|
||||
CSqlData(std::shared_ptr<TResult> pSqlResult) :
|
||||
m_pResult(pSqlResult)
|
||||
{ }
|
||||
std::shared_ptr<TResult> m_pResult;
|
||||
virtual ~CSqlData() = default;
|
||||
};
|
||||
|
||||
struct CSqlInitData : CSqlData<CSqlInitResult>
|
||||
{
|
||||
using CSqlData<CSqlInitResult>::CSqlData;
|
||||
// current map
|
||||
sqlstr::CSqlString<128> m_Map;
|
||||
sqlstr::CSqlString<UUID_MAXSTRSIZE> m_GameUuid;
|
||||
|
||||
// counter to keep track to which instance of GameServer this object belongs to.
|
||||
int m_Instance;
|
||||
|
||||
static CGameContext *ms_pGameServer;
|
||||
static IServer *ms_pServer;
|
||||
static CPlayerData *ms_pPlayerData;
|
||||
static const char *ms_pMap;
|
||||
static const char *ms_pGameUuid;
|
||||
|
||||
static bool ms_GameContextAvailable;
|
||||
// contains the instancecount of the current GameServer
|
||||
static int ms_Instance;
|
||||
};
|
||||
|
||||
struct CSqlExecData
|
||||
struct CSqlPlayerRequest : CSqlData<CSqlPlayerResult>
|
||||
{
|
||||
CSqlExecData(bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), CSqlData *pSqlData, bool ReadOnly = true) :
|
||||
m_pFuncPtr(pFuncPtr),
|
||||
m_pSqlData(pSqlData),
|
||||
m_ReadOnly(ReadOnly)
|
||||
{
|
||||
++ms_InstanceCount;
|
||||
}
|
||||
~CSqlExecData()
|
||||
{
|
||||
--ms_InstanceCount;
|
||||
}
|
||||
|
||||
bool (*m_pFuncPtr) (CSqlServer*, const CSqlData *, bool);
|
||||
CSqlData *m_pSqlData;
|
||||
bool m_ReadOnly;
|
||||
|
||||
// keeps track of score-threads
|
||||
volatile static int ms_InstanceCount;
|
||||
using CSqlData<CSqlPlayerResult>::CSqlData;
|
||||
// object being requested, either map (128 bytes) or player (16 bytes)
|
||||
sqlstr::CSqlString<128> m_Name;
|
||||
// current map
|
||||
sqlstr::CSqlString<128> m_Map;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_RequestingPlayer;
|
||||
// relevant for /top5 kind of requests
|
||||
int m_Offset;
|
||||
};
|
||||
|
||||
struct CSqlPlayerData : CSqlData
|
||||
struct CSqlRandomMapRequest : CSqlData<CSqlRandomMapResult>
|
||||
{
|
||||
int m_ClientID;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_Name;
|
||||
using CSqlData<CSqlRandomMapResult>::CSqlData;
|
||||
sqlstr::CSqlString<32> m_ServerType;
|
||||
sqlstr::CSqlString<32> m_CurrentMap;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_RequestingPlayer;
|
||||
int m_Stars;
|
||||
};
|
||||
|
||||
// used for mapinfo
|
||||
struct CSqlMapData : CSqlData
|
||||
struct CSqlScoreData : CSqlData<CSqlPlayerResult>
|
||||
{
|
||||
int m_ClientID;
|
||||
|
||||
sqlstr::CSqlString<128> m_RequestedMap;
|
||||
char m_aFuzzyMap[128];
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_Name;
|
||||
};
|
||||
|
||||
// used for mapvote
|
||||
struct CSqlMapVoteData : CSqlMapData
|
||||
{
|
||||
std::shared_ptr<CMapVoteResult> m_pResult;
|
||||
};
|
||||
|
||||
struct CSqlScoreData : CSqlData
|
||||
{
|
||||
int m_ClientID;
|
||||
using CSqlData<CSqlPlayerResult>::CSqlData;
|
||||
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_Map;
|
||||
char m_GameUuid[UUID_MAXSTRSIZE];
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_Name;
|
||||
|
||||
bool m_NotEligible;
|
||||
int m_ClientID;
|
||||
float m_Time;
|
||||
char m_aTimestamp[TIMESTAMP_STR_LENGTH];
|
||||
float m_aCpCurrent[NUM_CHECKPOINTS];
|
||||
|
@ -116,111 +149,147 @@ struct CSqlScoreData : CSqlData
|
|||
char m_aRequestingPlayer[MAX_NAME_LENGTH];
|
||||
};
|
||||
|
||||
struct CSqlTeamScoreData : CSqlData
|
||||
struct CSqlTeamScoreData : CSqlData<void>
|
||||
{
|
||||
bool m_NotEligible;
|
||||
using CSqlData<void>::CSqlData;
|
||||
char m_GameUuid[UUID_MAXSTRSIZE];
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_Map;
|
||||
float m_Time;
|
||||
char m_aTimestamp[TIMESTAMP_STR_LENGTH];
|
||||
unsigned int m_Size;
|
||||
int m_aClientIDs[MAX_CLIENTS];
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_aNames[MAX_CLIENTS];
|
||||
};
|
||||
|
||||
struct CSqlTeamSave : CSqlData
|
||||
struct CSqlTeamSave : CSqlData<CSqlSaveResult>
|
||||
{
|
||||
virtual ~CSqlTeamSave();
|
||||
using CSqlData<CSqlSaveResult>::CSqlData;
|
||||
virtual ~CSqlTeamSave() {};
|
||||
|
||||
int m_Team;
|
||||
int m_ClientID;
|
||||
char m_ClientName[MAX_NAME_LENGTH];
|
||||
sqlstr::CSqlString<128> m_Code;
|
||||
|
||||
char m_Map[128];
|
||||
char m_Code[128];
|
||||
char m_aGeneratedCode[128];
|
||||
char m_Server[5];
|
||||
};
|
||||
|
||||
struct CSqlTeamLoad : CSqlData
|
||||
struct CSqlTeamLoad : CSqlData<CSqlSaveResult>
|
||||
{
|
||||
using CSqlData<CSqlSaveResult>::CSqlData;
|
||||
sqlstr::CSqlString<128> m_Code;
|
||||
sqlstr::CSqlString<128> m_Map;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_RequestingPlayer;
|
||||
int m_ClientID;
|
||||
char m_ClientName[MAX_NAME_LENGTH];
|
||||
// struct holding all player names in the team or an empty string
|
||||
char m_aClientNames[MAX_CLIENTS][MAX_NAME_LENGTH];
|
||||
int m_aClientID[MAX_CLIENTS];
|
||||
int m_NumPlayer;
|
||||
};
|
||||
|
||||
struct CSqlGetSavesData: CSqlData
|
||||
// controls one thread
|
||||
template < typename TResult >
|
||||
struct CSqlExecData
|
||||
{
|
||||
int m_ClientID;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_Name;
|
||||
CSqlExecData(
|
||||
bool (*pFuncPtr) (CSqlServer*, const CSqlData<TResult> *, bool),
|
||||
CSqlData<TResult> *pSqlResult,
|
||||
bool ReadOnly = true
|
||||
);
|
||||
~CSqlExecData();
|
||||
|
||||
bool (*m_pFuncPtr) (CSqlServer*, const CSqlData<TResult> *, bool);
|
||||
CSqlData<TResult> *m_pSqlData;
|
||||
bool m_ReadOnly;
|
||||
|
||||
static void ExecSqlFunc(void *pUser);
|
||||
};
|
||||
|
||||
struct CSqlRandomMap : CSqlScoreData
|
||||
{
|
||||
std::shared_ptr<CRandomMapResult> m_pResult;
|
||||
};
|
||||
class IServer;
|
||||
class CGameContext;
|
||||
|
||||
class CSqlScore: public IScore
|
||||
{
|
||||
static LOCK ms_FailureFileLock;
|
||||
|
||||
static bool Init(CSqlServer* pSqlServer, const CSqlData<CSqlInitResult> *pGameData, bool HandleFailure);
|
||||
|
||||
static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData<CSqlRandomMapResult> *pGameData, bool HandleFailure = false);
|
||||
static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData<CSqlRandomMapResult> *pGameData, bool HandleFailure = false);
|
||||
static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
|
||||
static bool LoadPlayerDataThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool MapInfoThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowRankThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTimesThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowPointsThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool GetSavesThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
|
||||
static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData<CSqlSaveResult> *pGameData, bool HandleFailure = false);
|
||||
static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData<CSqlSaveResult> *pGameData, bool HandleFailure = false);
|
||||
|
||||
static bool SaveScoreThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData<void> *pGameData, bool HandleFailure = false);
|
||||
|
||||
CGameContext *GameServer() { return m_pGameServer; }
|
||||
IServer *Server() { return m_pServer; }
|
||||
|
||||
CGameContext *m_pGameServer;
|
||||
IServer *m_pServer;
|
||||
|
||||
static void ExecSqlFunc(void *pUser);
|
||||
std::vector<std::string> m_aWordlist;
|
||||
CPrng m_Prng;
|
||||
void GeneratePassphrase(char *pBuf, int BufSize);
|
||||
|
||||
static bool Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure);
|
||||
|
||||
char m_aMap[64];
|
||||
char m_aGameUuid[UUID_MAXSTRSIZE];
|
||||
|
||||
static LOCK ms_FailureFileLock;
|
||||
|
||||
static bool CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
static bool GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false);
|
||||
// returns new SqlResult bound to the player, if no current Thread is active for this player
|
||||
std::shared_ptr<CSqlPlayerResult> NewSqlPlayerResult(int ClientID);
|
||||
// Creates for player database requests
|
||||
void ExecPlayerThread(
|
||||
bool (*pFuncPtr) (CSqlServer*, const CSqlData<CSqlPlayerResult> *, bool),
|
||||
const char* pThreadName,
|
||||
int ClientID,
|
||||
const char* pName,
|
||||
int Offset
|
||||
);
|
||||
|
||||
public:
|
||||
// keeps track of score-threads
|
||||
static std::atomic_int ms_InstanceCount;
|
||||
|
||||
CSqlScore(CGameContext *pGameServer);
|
||||
~CSqlScore();
|
||||
~CSqlScore() {}
|
||||
|
||||
virtual void CheckBirthday(int ClientID);
|
||||
virtual void LoadScore(int ClientID);
|
||||
// Requested by game context, shouldn't fail in case the player started another thread
|
||||
virtual void RandomMap(int ClientID, int Stars);
|
||||
virtual void RandomUnfinishedMap(int ClientID, int Stars);
|
||||
virtual void MapVote(int ClientID, const char* MapName);
|
||||
|
||||
virtual void LoadPlayerData(int ClientID);
|
||||
// Requested by players (fails if another request by this player is active)
|
||||
virtual void MapInfo(int ClientID, const char* MapName);
|
||||
virtual void MapVote(std::shared_ptr<CMapVoteResult> *ppResult, int ClientID, const char* MapName);
|
||||
virtual void ShowRank(int ClientID, const char* pName);
|
||||
virtual void ShowTeamRank(int ClientID, const char* pName);
|
||||
virtual void ShowPoints(int ClientID, const char* pName);
|
||||
virtual void ShowTimes(int ClientID, const char* pName, int Offset = 1);
|
||||
virtual void ShowTimes(int ClientID, int Offset = 1);
|
||||
virtual void ShowTop5(int ClientID, int Offset = 1);
|
||||
virtual void ShowTeamTop5(int ClientID, int Offset = 1);
|
||||
virtual void ShowTopPoints(int ClientID, int Offset = 1);
|
||||
virtual void GetSaves(int ClientID);
|
||||
|
||||
// requested by teams
|
||||
virtual void SaveTeam(int ClientID, const char* Code, const char* Server);
|
||||
virtual void LoadTeam(const char* Code, int ClientID);
|
||||
|
||||
// Game relevant not allowed to fail due to an ongoing SQL request.
|
||||
virtual void SaveScore(int ClientID, float Time, const char *pTimestamp,
|
||||
float CpTime[NUM_CHECKPOINTS], bool NotEligible);
|
||||
virtual void SaveTeamScore(int* aClientIDs, unsigned int Size, float Time, const char *pTimestamp);
|
||||
virtual void ShowRank(int ClientID, const char* pName, bool Search = false);
|
||||
virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false);
|
||||
virtual void ShowTimes(int ClientID, const char* pName, int Debut = 1);
|
||||
virtual void ShowTimes(int ClientID, int Debut = 1);
|
||||
virtual void ShowTop5(IConsole::IResult *pResult, int ClientID,
|
||||
void *pUserData, int Debut = 1);
|
||||
virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID,
|
||||
void *pUserData, int Debut = 1);
|
||||
virtual void ShowPoints(int ClientID, const char* pName, bool Search = false);
|
||||
virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID,
|
||||
void *pUserData, int Debut = 1);
|
||||
virtual void RandomMap(std::shared_ptr<CRandomMapResult> *ppResult, int ClientID, int stars);
|
||||
virtual void RandomUnfinishedMap(std::shared_ptr<CRandomMapResult> *ppResult, int ClientID, int stars);
|
||||
virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server);
|
||||
virtual void LoadTeam(const char* Code, int ClientID);
|
||||
virtual void GetSaves(int ClientID);
|
||||
|
||||
virtual void OnShutdown();
|
||||
};
|
||||
|
||||
#endif // GAME_SERVER_SCORE_SQL_SCORE_H
|
||||
#endif // GAME_SERVER_SCORE_SQL_H
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#include "teams.h"
|
||||
#include "score.h"
|
||||
#include <engine/shared/config.h>
|
||||
#if defined(CONF_SQL)
|
||||
#include "score/sql_score.h"
|
||||
#endif
|
||||
|
||||
CGameTeams::CGameTeams(CGameContext *pGameContext) :
|
||||
m_pGameContext(pGameContext)
|
||||
|
@ -19,9 +22,11 @@ void CGameTeams::Reset()
|
|||
m_MembersCount[i] = 0;
|
||||
m_LastChat[i] = 0;
|
||||
m_TeamLocked[i] = false;
|
||||
m_IsSaving[i] = false;
|
||||
m_Invited[i] = 0;
|
||||
m_Practice[i] = false;
|
||||
#if defined(CONF_SQL)
|
||||
m_pSaveTeamResult[i] = nullptr;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,6 +255,11 @@ bool CGameTeams::SetCharacterTeam(int ClientID, int Team)
|
|||
if (m_Practice[m_Core.Team(ClientID)])
|
||||
return false;
|
||||
|
||||
//you can not join a team which is currently in the process of saving,
|
||||
//because the save-process can fail and then the team is reset into the game
|
||||
if((Team != TEAM_SUPER && GetSaving(Team))
|
||||
|| (m_Core.Team(ClientID) != TEAM_SUPER && GetSaving(m_Core.Team(ClientID))))
|
||||
return false;
|
||||
SetForceCharacterTeam(ClientID, Team);
|
||||
|
||||
//GameServer()->CreatePlayerSpawn(Character(id)->m_Core.m_Pos, TeamMask());
|
||||
|
@ -327,6 +337,7 @@ void CGameTeams::ForceLeaveTeam(int ClientID)
|
|||
SetTeamLock(m_Core.Team(ClientID), false);
|
||||
ResetInvited(m_Core.Team(ClientID));
|
||||
m_Practice[m_Core.Team(ClientID)] = false;
|
||||
// do not reset SaveTeamResult, because it should be logged into teehistorian even if the team leaves
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -670,10 +681,56 @@ void CGameTeams::OnFinish(CPlayer* Player, float Time, const char *pTimestamp)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
void CGameTeams::ProcessSaveTeam()
|
||||
{
|
||||
for(int Team = 0; Team < MAX_CLIENTS; Team++)
|
||||
{
|
||||
if(m_pSaveTeamResult[Team] == nullptr || m_pSaveTeamResult[Team].use_count() != 1)
|
||||
continue;
|
||||
if(m_pSaveTeamResult[Team]->m_aBroadcast[0] != '\0')
|
||||
GameServer()->SendBroadcast(m_pSaveTeamResult[Team]->m_aBroadcast, -1);
|
||||
if(m_pSaveTeamResult[Team]->m_aMessage[0] != '\0')
|
||||
GameServer()->SendChatTeam(Team, m_pSaveTeamResult[Team]->m_aMessage);
|
||||
// TODO: log load/save success/fail in teehistorian
|
||||
switch(m_pSaveTeamResult[Team]->m_Status)
|
||||
{
|
||||
case CSqlSaveResult::SAVE_SUCCESS:
|
||||
{
|
||||
ResetSavedTeam(m_pSaveTeamResult[Team]->m_RequestingPlayer, Team);
|
||||
char aSaveID[UUID_MAXSTRSIZE];
|
||||
FormatUuid(m_pSaveTeamResult[Team]->m_SaveID, aSaveID, UUID_MAXSTRSIZE);
|
||||
dbg_msg("save", "Save successful: %s", aSaveID);
|
||||
break;
|
||||
}
|
||||
case CSqlSaveResult::SAVE_FAILED:
|
||||
if(m_MembersCount[Team] > 0)
|
||||
m_pSaveTeamResult[Team]->m_SavedTeam.load(Team);
|
||||
break;
|
||||
case CSqlSaveResult::LOAD_SUCCESS:
|
||||
{
|
||||
if(m_MembersCount[Team] > 0)
|
||||
m_pSaveTeamResult[Team]->m_SavedTeam.load(Team);
|
||||
char aSaveID[UUID_MAXSTRSIZE];
|
||||
FormatUuid(m_pSaveTeamResult[Team]->m_SaveID, aSaveID, UUID_MAXSTRSIZE);
|
||||
dbg_msg("save", "Load successful: %s", aSaveID);
|
||||
break;
|
||||
}
|
||||
case CSqlSaveResult::LOAD_FAILED:
|
||||
break;
|
||||
}
|
||||
m_pSaveTeamResult[Team] = nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void CGameTeams::OnCharacterSpawn(int ClientID)
|
||||
{
|
||||
m_Core.SetSolo(ClientID, false);
|
||||
|
||||
if(GetSaving(m_Core.Team(ClientID)))
|
||||
return;
|
||||
|
||||
if (m_Core.Team(ClientID) >= TEAM_SUPER || !m_TeamLocked[m_Core.Team(ClientID)])
|
||||
// Important to only set a new team here, don't remove from an existing
|
||||
// team since a newly joined player does by definition not have an old team
|
||||
|
@ -686,6 +743,8 @@ void CGameTeams::OnCharacterDeath(int ClientID, int Weapon)
|
|||
m_Core.SetSolo(ClientID, false);
|
||||
|
||||
int Team = m_Core.Team(ClientID);
|
||||
if(GetSaving(Team))
|
||||
return;
|
||||
bool Locked = TeamLocked(Team) && Weapon != WEAPON_GAME;
|
||||
|
||||
if(!Locked)
|
||||
|
@ -746,30 +805,27 @@ void CGameTeams::SetClientInvited(int Team, int ClientID, bool Invited)
|
|||
}
|
||||
}
|
||||
|
||||
void CGameTeams::KillSavedTeam(int Team)
|
||||
#if defined(CONF_SQL)
|
||||
void CGameTeams::KillSavedTeam(int ClientID, int Team)
|
||||
{
|
||||
// Set so that no finish is accidentally given to some of the players
|
||||
ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN);
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i])
|
||||
{
|
||||
GameServer()->m_apPlayers[i]->m_VotedForPractice = false;
|
||||
GameServer()->m_apPlayers[i]->KillCharacter(WEAPON_SELF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameTeams::ResetSavedTeam(int ClientID, int Team)
|
||||
{
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i])
|
||||
{
|
||||
// Set so that no finish is accidentally given to some of the players
|
||||
GameServer()->m_apPlayers[i]->GetCharacter()->m_DDRaceState = DDRACE_NONE;
|
||||
m_TeeFinished[i] = false;
|
||||
SetForceCharacterTeam(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i])
|
||||
GameServer()->m_apPlayers[i]->ThreadKillCharacter(-2);
|
||||
|
||||
ChangeTeamState(Team, CGameTeams::TEAMSTATE_EMPTY);
|
||||
|
||||
// unlock team when last player leaves
|
||||
SetTeamLock(Team, false);
|
||||
ResetInvited(Team);
|
||||
|
||||
m_Practice[Team] = false;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -5,15 +5,21 @@
|
|||
#include <game/teamscore.h>
|
||||
#include <game/server/gamecontext.h>
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
class CSqlSaveResult;
|
||||
#endif
|
||||
|
||||
class CGameTeams
|
||||
{
|
||||
int m_TeamState[MAX_CLIENTS];
|
||||
int m_MembersCount[MAX_CLIENTS];
|
||||
bool m_TeeFinished[MAX_CLIENTS];
|
||||
bool m_TeamLocked[MAX_CLIENTS];
|
||||
bool m_IsSaving[MAX_CLIENTS];
|
||||
uint64_t m_Invited[MAX_CLIENTS];
|
||||
bool m_Practice[MAX_CLIENTS];
|
||||
#if defined(CONF_SQL)
|
||||
std::shared_ptr<CSqlSaveResult> m_pSaveTeamResult[MAX_CLIENTS];
|
||||
#endif
|
||||
|
||||
class CGameContext * m_pGameContext;
|
||||
|
||||
|
@ -85,7 +91,11 @@ public:
|
|||
void SetDDRaceState(CPlayer* Player, int DDRaceState);
|
||||
void SetStartTime(CPlayer* Player, int StartTime);
|
||||
void SetCpActive(CPlayer* Player, int CpActive);
|
||||
void KillSavedTeam(int Team);
|
||||
#if defined(CONF_SQL)
|
||||
void KillSavedTeam(int ClientID, int Team);
|
||||
void ResetSavedTeam(int ClientID, int Team);
|
||||
void ProcessSaveTeam();
|
||||
#endif
|
||||
|
||||
bool TeeFinished(int ClientID)
|
||||
{
|
||||
|
@ -114,15 +124,20 @@ public:
|
|||
{
|
||||
m_TeeFinished[ClientID] = finished;
|
||||
}
|
||||
|
||||
void SetSaving(int TeamID, bool Value)
|
||||
#if defined(CONF_SQL)
|
||||
void SetSaving(int TeamID, std::shared_ptr<CSqlSaveResult> SaveResult)
|
||||
{
|
||||
m_IsSaving[TeamID] = Value;
|
||||
m_pSaveTeamResult[TeamID] = SaveResult;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GetSaving(int TeamID)
|
||||
{
|
||||
return m_IsSaving[TeamID];
|
||||
#if defined(CONF_SQL)
|
||||
return m_pSaveTeamResult[TeamID] != nullptr;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void EnablePractice(int Team)
|
||||
|
|
Loading…
Reference in a new issue