diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 67fea0725..05e52dd70 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -504,6 +504,37 @@ void CGameContext::ConDND(IConsole::IResult *pResult, void *pUserData) } } +void CGameContext::ConMap(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *) pUserData; + if (!CheckClientID(pResult->m_ClientID)) + return; + + if (pResult->NumArguments() <= 0) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "map", "Example: /map adr3 to call vote for Adrenaline 3"); + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "map", "This means that the map name must start with 'a' and contain the characters 'd', 'r' and '3' in that order"); + return; + } + +#if defined(CONF_SQL) + if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) + if(pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery + pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) + return; +#endif + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if (!pPlayer) + return; + + pSelf->Score()->MapVote(pResult->m_ClientID, pResult->GetString(0)); + +#if defined(CONF_SQL) + if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) + pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery = pSelf->Server()->Tick(); +#endif +} + void CGameContext::ConMapPoints(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *) pUserData; diff --git a/src/game/server/ddracechat.h b/src/game/server/ddracechat.h index 6e14b0e24..af52db7fc 100644 --- a/src/game/server/ddracechat.h +++ b/src/game/server/ddracechat.h @@ -20,7 +20,8 @@ CHAT_COMMAND("converse", "r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConConverse, this, "C CHAT_COMMAND("pause", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTogglePause, this, "Toggles pause (if not activated on the server, it toggles spec)") CHAT_COMMAND("spec", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConToggleSpec, this, "Toggles spec") CHAT_COMMAND("dnd", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConDND, this, "Toggle Do Not Disturb (no chat and server messages)") -CHAT_COMMAND("mappoints", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMapPoints, this, "Show how man points the map with name r gives (current map by default)") +CHAT_COMMAND("mapinfo", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMapPoints, this, "Show info about the map with name r gives (current map by default)") +CHAT_COMMAND("map", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMap, this, "Vote a map by name") CHAT_COMMAND("rankteam", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTeamRank, this, "Shows the team rank of player with name r (your team rank by default)") CHAT_COMMAND("rank", "?r", CFGFLAG_CHAT|CFGFLAG_SERVER, ConRank, this, "Shows the rank of player with name r (your rank by default)") CHAT_COMMAND("rules", "", CFGFLAG_CHAT|CFGFLAG_SERVER, ConRules, this, "Shows the server rules") diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 97b7e372d..0d4b97f64 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -245,6 +245,22 @@ void CGameContext::CreateSoundGlobal(int Sound, int Target) } } +void CGameContext::CallVote(int ClientID, const char *aDesc, const char *aCmd, const char *pReason, const char *aChatmsg) +{ + // check if a vote is already running + if(m_VoteCloseTime) + return; + + int64 Now = Server()->Tick(); + CPlayer *pPlayer = m_apPlayers[ClientID]; + SendChat(-1, CGameContext::CHAT_ALL, aChatmsg); + StartVote(aDesc, aCmd, pReason); + pPlayer->m_Vote = 1; + pPlayer->m_VotePos = m_VotePos = 1; + m_VoteCreator = ClientID; + pPlayer->m_LastVoteCall = Now; +} + void CGameContext::SendChatTarget(int To, const char *pText) { CNetMsg_Sv_Chat Msg; @@ -354,10 +370,6 @@ void CGameContext::SendBroadcast(const char *pText, int ClientID) // void CGameContext::StartVote(const char *pDesc, const char *pCommand, const char *pReason) { - // check if a vote is already running - if(m_VoteCloseTime) - return; - // reset votes m_VoteEnforce = VOTE_ENFORCE_UNKNOWN; m_VoteEnforcer = -1; @@ -1135,14 +1147,7 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } if(aCmd[0] && str_comp(aCmd,"info")) - { - SendChat(-1, CGameContext::CHAT_ALL, aChatmsg); - StartVote(aDesc, aCmd, pReason); - pPlayer->m_Vote = 1; - pPlayer->m_VotePos = m_VotePos = 1; - m_VoteCreator = ClientID; - pPlayer->m_LastVoteCall = Now; - } + CallVote(ClientID, aDesc, aCmd, pReason, aChatmsg); } else if(MsgID == NETMSGTYPE_CL_VOTE) { diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index b60abf304..ac6ce62ce 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -148,6 +148,7 @@ public: }; // network + void CallVote(int ClientID, const char *aDesc, const char *aCmd, const char *pReason, const char *aChatmsg); void SendChatTarget(int To, const char *pText); void SendChat(int ClientID, int Team, const char *pText, int SpamProtectionClientID = -1); void SendEmoticon(int ClientID, int Emoticon); @@ -251,6 +252,7 @@ private: static void ConUTF8(IConsole::IResult *pResult, void *pUserData); static void ConDND(IConsole::IResult *pResult, void *pUserData); static void ConMapPoints(IConsole::IResult *pResult, void *pUserData); + static void ConMap(IConsole::IResult *pResult, void *pUserData); static void ConTeamRank(IConsole::IResult *pResult, void *pUserData); static void ConRank(IConsole::IResult *pResult, void *pUserData); static void ConBroadTime(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/server/score.h b/src/game/server/score.h index 3857ebe7d..fd85ebd9b 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -44,6 +44,7 @@ public: CPlayerData *PlayerData(int ID) { return &m_aPlayerData[ID]; } virtual void MapPoints(int ClientID, const char* MapName) = 0; + virtual void MapVote(int ClientID, const char* MapName) = 0; virtual void LoadScore(int ClientID) = 0; virtual void SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index 323f76fb4..796fe4c99 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -59,6 +59,11 @@ void CFileScore::MapPoints(int ClientID, const char* MapName) // TODO: implement } +void CFileScore::MapVote(int ClientID, const char* MapName) +{ + // TODO: implement +} + void CFileScore::SaveScoreThread(void *pUser) { CFileScore *pSelf = (CFileScore *) pUser; diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index efc2fcbc4..1a60fde50 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -64,6 +64,7 @@ public: virtual void LoadScore(int ClientID); virtual void MapPoints(int ClientID, const char* MapName); + virtual void MapVote(int ClientID, const char* MapName); virtual void SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]); virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 429b786d8..1b8e8816a 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -394,6 +394,74 @@ void CSqlScore::SaveTeamScoreThread(void *pUser) lock_release(gs_SqlLock); } +void CSqlScore::MapVote(int ClientID, const char* MapName) +{ + CSqlMapData *Tmp = new CSqlMapData(); + Tmp->m_ClientID = ClientID; + str_copy(Tmp->m_aMap, MapName, 128); + Tmp->m_pSqlData = this; + + void *VoteThread = thread_create(MapVoteThread, Tmp); +#if defined(CONF_FAMILY_UNIX) + pthread_detach((pthread_t)VoteThread); +#endif +} + +void CSqlScore::MapVoteThread(void *pUser) +{ + lock_wait(gs_SqlLock); + + CSqlMapData *pData = (CSqlMapData *)pUser; + + // Connect to database + if(pData->m_pSqlData->Connect()) + { + char originalMap[128]; + strcpy(originalMap,pData->m_aMap); + pData->m_pSqlData->ClearString(pData->m_aMap); + pData->m_pSqlData->FuzzyString(pData->m_aMap); + + try + { + char aBuf[768]; + str_format(aBuf, sizeof(aBuf), "SELECT Map, Server FROM %s_maps WHERE Map LIKE '%s' COLLATE utf8_general_ci ORDER BY LENGTH(Map), Map LIMIT 1;", pData->m_pSqlData->m_pPrefix, pData->m_aMap); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); + + if(pData->m_pSqlData->m_pResults->rowsCount() != 1) + { + str_format(aBuf, sizeof(aBuf), "No map like \"%s\" found.", originalMap); + } + else + { + pData->m_pSqlData->m_pResults->next(); + char aMap[128]; + strcpy(aMap, pData->m_pSqlData->m_pResults->getString("Map").c_str()); + char aServer[32]; + strcpy(aServer, pData->m_pSqlData->m_pResults->getString("Server").c_str()); + char aCmd[256]; + str_format(aCmd, sizeof(aCmd), "sv_reset_file types/%s/flexreset.cfg; change_map %s", aServer, aMap); + char aChatmsg[512]; + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", pData->m_pSqlData->GameServer()->Server()->ClientName(pData->m_ClientID), aMap, "/map"); + pData->m_pSqlData->GameServer()->CallVote(pData->m_ClientID, aMap, aCmd, "/map", aChatmsg); + } + + delete pData->m_pSqlData->m_pResults; + } + catch (sql::SQLException &e) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); + dbg_msg("SQL", aBuf); + dbg_msg("SQL", "ERROR: Could not update time"); + } + + pData->m_pSqlData->Disconnect(); + } + + delete pData; + lock_release(gs_SqlLock); +} + void CSqlScore::MapPoints(int ClientID, const char* MapName) { CSqlMapData *Tmp = new CSqlMapData(); @@ -401,9 +469,9 @@ void CSqlScore::MapPoints(int ClientID, const char* MapName) str_copy(Tmp->m_aMap, MapName, 128); Tmp->m_pSqlData = this; - void *PointsThread = thread_create(MapPointsThread, Tmp); + void *InfoThread = thread_create(MapPointsThread, Tmp); #if defined(CONF_FAMILY_UNIX) - pthread_detach((pthread_t)PointsThread); + pthread_detach((pthread_t)InfoThread); #endif } @@ -419,22 +487,28 @@ void CSqlScore::MapPointsThread(void *pUser) char originalMap[128]; strcpy(originalMap,pData->m_aMap); pData->m_pSqlData->ClearString(pData->m_aMap); + pData->m_pSqlData->FuzzyString(pData->m_aMap); try { char aBuf[768]; - str_format(aBuf, sizeof(aBuf), "SELECT Points FROM %s_maps WHERE Map ='%s'", pData->m_pSqlData->m_pPrefix, pData->m_aMap); + str_format(aBuf, sizeof(aBuf), "SELECT Map, Server, Points FROM %s_maps WHERE Map LIKE '%s' COLLATE utf8_general_ci ORDER BY LENGTH(Map), Map LIMIT 1;", pData->m_pSqlData->m_pPrefix, pData->m_aMap); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); if(pData->m_pSqlData->m_pResults->rowsCount() != 1) { - str_format(aBuf, sizeof(aBuf), "No map called \"%s\" found.", originalMap); + str_format(aBuf, sizeof(aBuf), "No map like \"%s\" found.", originalMap); } else { pData->m_pSqlData->m_pResults->next(); int points = (int)pData->m_pSqlData->m_pResults->getInt("Points"); - str_format(aBuf, sizeof(aBuf), "Finishing \"%s\" will give you %d points.", originalMap, points); + char aMap[128]; + strcpy(aMap, pData->m_pSqlData->m_pResults->getString("Map").c_str()); + char aServer[32]; + strcpy(aServer, pData->m_pSqlData->m_pResults->getString("Server").c_str()); + aServer[0] = toupper(aServer[0]); + str_format(aBuf, sizeof(aBuf), "\"%s\" on %s (%d points)", aMap, aServer, points); } pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); @@ -1055,8 +1129,25 @@ void CSqlScore::ShowTimes(int ClientID, const char* pName, int Debut) #endif } -// anti SQL injection +void CSqlScore::FuzzyString(char *pString) +{ + char newString[32*4-1]; + int pos = 0; + for(int i=0;i<64;i++) + { + if(!pString[i]) + break; + + newString[pos++] = pString[i]; + newString[pos++] = '%'; + } + + newString[pos] = '\0'; + strcpy(pString, newString); +} + +// anti SQL injection void CSqlScore::ClearString(char *pString) { char newString[32*2-1]; diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index c446e2cfe..8561b0e18 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -41,6 +41,7 @@ class CSqlScore: public IScore } static void MapPointsThread(void *pUser); + static void MapVoteThread(void *pUser); static void LoadScoreThread(void *pUser); static void SaveScoreThread(void *pUser); static void SaveTeamScoreThread(void *pUser); @@ -57,6 +58,7 @@ class CSqlScore: public IScore bool Connect(); void Disconnect(); + void FuzzyString(char *pString); // anti SQL injection void ClearString(char *pString); @@ -69,6 +71,7 @@ public: virtual void LoadScore(int ClientID); virtual void MapPoints(int ClientID, const char* MapName); + virtual void MapVote(int ClientID, const char* MapName); virtual void SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS]); virtual void SaveTeamScore(int* aClientIDs, unsigned int Size, float Time);