Thread safe /map

This commit is contained in:
Zwelf 2020-06-14 10:44:14 +02:00
parent ea31171873
commit 7f4dc2bb4e
6 changed files with 125 additions and 78 deletions

View file

@ -518,6 +518,9 @@ 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())

View file

@ -920,12 +920,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;
@ -939,8 +933,6 @@ void CGameContext::OnTick()
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;
}
*/
@ -1424,68 +1416,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 +1448,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;
}
@ -3581,3 +3514,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;
}

View file

@ -259,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;

View file

@ -818,7 +818,20 @@ void CPlayer::ProcessSqlResult(CSqlPlayerResult &Result)
GameServer()->SendBroadcast(Result.m_Data.m_Broadcast, -1);
break;
case CSqlPlayerResult::MAP_VOTE:
// TODO: start 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(

View file

@ -43,6 +43,9 @@ void CSqlPlayerResult::SetVariant(Variant v)
m_Data.m_Broadcast[0] = 0;
break;
case MAP_VOTE:
m_Data.m_MapVote.m_Map[0] = '\0';
m_Data.m_MapVote.m_Reason[0] = '\0';
m_Data.m_MapVote.m_Server[0] = '\0';
break;
case PLAYER_INFO:
m_Data.m_Info.m_Score = -9999;
@ -373,13 +376,16 @@ bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData<CSqlPlayerR
}
else
{
pSqlServer->GetResults()->first();
auto Server = pSqlServer->GetResults()->getString("Server");
auto Map = pSqlServer->GetResults()->getString("Map");
strcpy(paMessages[0], "/map"); // reason
str_copy(paMessages[1], Server.c_str(), sizeof(paMessages[0]));
str_copy(paMessages[2], Map.c_str(), sizeof(paMessages[0]));
pData->m_pResult->SetVariant(CSqlPlayerResult::MAP_VOTE);
auto MapVote = &pData->m_pResult->m_Data.m_MapVote;
strcpy(MapVote->m_Reason, "/map");
str_copy(MapVote->m_Server, Server.c_str(), sizeof(MapVote->m_Server));
str_copy(MapVote->m_Map, Map.c_str(), sizeof(MapVote->m_Map));
for(char *p = paMessages[1]; *p; p++) // lower case server
for(char *p = MapVote->m_Server; *p; p++) // lower case server
*p = tolower(*p);
}
pData->m_pResult->m_Done = true;

View file

@ -6,6 +6,7 @@
#include <engine/server/sql_string_helpers.h>
#include <game/prng.h>
#include <game/voting.h>
#include "../score.h"
@ -34,6 +35,12 @@ struct CSqlPlayerResult
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);