diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 8bf57f51c..b53796473 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -934,6 +934,22 @@ void CGameContext::OnClientEnter(int ClientID) void CGameContext::OnClientConnected(int ClientID) { + { + bool Empty = true; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_apPlayers[i]) + { + Empty = false; + break; + } + } + if(Empty) + { + m_NonEmptySince = Server()->Tick(); + } + } + // Check which team the player should be on const int StartTeam = g_Config.m_SvTournamentMode ? TEAM_SPECTATORS : m_pController->GetAutoTeam(ClientID); @@ -1119,12 +1135,13 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } else if(MsgID == NETMSGTYPE_CL_CALLVOTE) { - if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry+Server()->TickSpeed()*3 > Server()->Tick()) + int64 Now = Server()->Tick(); + int64 TickSpeed = Server()->TickSpeed(); + + if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry + TickSpeed * 3 > Now) return; - int64 Now = Server()->Tick(); pPlayer->m_LastVoteTry = Now; - //if(pPlayer->GetTeam() == TEAM_SPECTATORS) if(g_Config.m_SvSpectatorVotes == 0 && pPlayer->GetTeam() == TEAM_SPECTATORS) { SendChatTarget(ClientID, "Spectators aren't allowed to start a vote."); @@ -1137,11 +1154,19 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) return; } - int Timeleft = pPlayer->m_LastVoteCall + Server()->TickSpeed()*g_Config.m_SvVoteDelay - Now; - if(pPlayer->m_LastVoteCall && Timeleft > 0) + if(g_Config.m_SvJoinVoteDelay && Now < pPlayer->m_FirstVoteTick) { - char aChatmsg[512] = {0}; - str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote", (Timeleft/Server()->TickSpeed())+1); + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "You must wait %d seconds before making your first vote", ((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", (TimeLeft/TickSpeed)+1); SendChatTarget(ClientID, aChatmsg); return; } diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index a306fafdd..a5f72ec96 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -223,6 +223,8 @@ public: int ProcessSpamProtection(int ClientID); int GetDDRaceTeam(int ClientID); + // Describes the time when the first player joined the server. + int64 m_NonEmptySince; int64 m_LastMapVote; private: diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index b524a1a73..fe03a5c03 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -125,6 +125,22 @@ void CPlayer::Reset() #if defined(CONF_SQL) m_LastSQLQuery = 0; #endif + + int64 Now = Server()->Tick(); + int64 TickSpeed = Server()->TickSpeed(); + // If the player joins within ten seconds of the server becoming + // 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 for 60 seconds after joining. + if(Now > GameServer()->m_NonEmptySince + 10 * TickSpeed) + { + m_FirstVoteTick = Now + 60 * TickSpeed; + } + else + { + m_FirstVoteTick = Now; + } } void CPlayer::Tick() diff --git a/src/game/server/player.h b/src/game/server/player.h index 7e3763c9a..4b490d302 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -138,6 +138,7 @@ public: int m_Paused; bool m_DND; + int64 m_FirstVoteTick; int64 m_NextPauseTick; char m_TimeoutCode[64]; diff --git a/src/game/variables.h b/src/game/variables.h index b7904c214..90fb3b190 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -148,7 +148,8 @@ MACRO_CONFIG_INT(SvVoteSpectate, sv_vote_spectate, 1, 0, 1, CFGFLAG_SERVER, "All MACRO_CONFIG_INT(SvVoteSpectateRejoindelay, sv_vote_spectate_rejoindelay, 3, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before a player can rejoin after being moved to spectators by vote") MACRO_CONFIG_INT(SvVoteKick, sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players") MACRO_CONFIG_INT(SvVoteKickMin, sv_vote_kick_min, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Minimum number of players required to start a kick vote") -MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time to ban a player if kicked by vote. 0 makes it just use kick") +MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time in seconds to ban a player if kicked by vote. 0 makes it just use kick") +MACRO_CONFIG_INT(SvJoinVoteDelay, sv_join_vote_delay, 1, 0, 1, CFGFLAG_SERVER, "Add a delay before recently joined players can vote") MACRO_CONFIG_INT(SvOldTeleportWeapons, sv_old_teleport_weapons, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Teleporting of all weapons (deprecated, use special entities instead)"); MACRO_CONFIG_INT(SvOldTeleportHook, sv_old_teleport_hook, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Hook through teleporter (deprecated, use special entities instead)"); MACRO_CONFIG_INT(SvTeleportHoldHook, sv_teleport_hold_hook, 0, 0, 1, CFGFLAG_SERVER|CFGFLAG_GAME, "Hold hook when teleported");