diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 1f210f870..f671446e3 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -222,7 +222,8 @@ MACRO_CONFIG_INT(SvInviteFrequency, sv_invite_frequency, 1, 0, 9999, CFGFLAG_SER MACRO_CONFIG_INT(SvTeleOthersAuthLevel, sv_tele_others_auth_level, 1, 1, 3, CFGFLAG_SERVER, "The auth level you need to tele others") MACRO_CONFIG_INT(SvEmotionalTees, sv_emotional_tees, 1, -1, 1, CFGFLAG_SERVER, "Whether eye change of tees is enabled with emoticons = 1, not = 0, -1 not at all") -MACRO_CONFIG_INT(SvEmoticonDelay, sv_emoticon_delay, 3, 0, 9999, CFGFLAG_SERVER, "The time in seconds between over-head emoticons") +MACRO_CONFIG_INT(SvEmoticonMsDelay, sv_emoticon_ms_delay, 3000, 20, 999999999, CFGFLAG_SERVER, "The time in ms a player has to wait before allowing the next over-head emoticons") +MACRO_CONFIG_INT(SvGlobalEmoticonMsDelay, sv_global_emoticon_ms_delay, 3000, 20, 999999999, CFGFLAG_SERVER, "The time in ms a player has to wait before allowing the next over-head emoticons that is send to all clients (this config must be higher or equal to sv_emoticon_ms_delay to have an effect)") MACRO_CONFIG_INT(SvEyeEmoteChangeDelay, sv_eye_emote_change_delay, 1, 0, 9999, CFGFLAG_SERVER, "The time in seconds between eye emoticons change") MACRO_CONFIG_INT(SvChatDelay, sv_chat_delay, 1, 0, 9999, CFGFLAG_SERVER, "The time in seconds between chat messages") diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index ce4e44a97..1d40933a7 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1119,6 +1119,35 @@ bool CCharacter::CanSnapCharacter(int SnappingClient) return true; } +bool CCharacter::IsSnappingCharacterInView(int SnappingClientID) +{ + int ID = m_pPlayer->GetCID(); + + // A player may not be clipped away if his hook or a hook attached to him is in the field of view + bool PlayerAndHookNotInView = NetworkClippedLine(SnappingClientID, m_Pos, m_Core.m_HookPos); + bool AttachedHookInView = false; + if(PlayerAndHookNotInView) + { + for(const auto &AttachedPlayerID : m_Core.m_AttachedPlayers) + { + CCharacter *pOtherPlayer = GameServer()->GetPlayerChar(AttachedPlayerID); + if(pOtherPlayer && pOtherPlayer->m_Core.m_HookedPlayer == ID) + { + if(!NetworkClippedLine(SnappingClientID, m_Pos, pOtherPlayer->m_Pos)) + { + AttachedHookInView = true; + break; + } + } + } + } + if(PlayerAndHookNotInView && !AttachedHookInView) + { + return false; + } + return true; +} + void CCharacter::Snap(int SnappingClient) { int ID = m_pPlayer->GetCID(); @@ -1131,28 +1160,8 @@ void CCharacter::Snap(int SnappingClient) return; } - // A player may not be clipped away if his hook or a hook attached to him is in the field of view - bool PlayerAndHookNotInView = NetworkClippedLine(SnappingClient, m_Pos, m_Core.m_HookPos); - bool AttachedHookInView = false; - if(PlayerAndHookNotInView) - { - for(const auto &AttachedPlayerID : m_Core.m_AttachedPlayers) - { - CCharacter *pOtherPlayer = GameServer()->GetPlayerChar(AttachedPlayerID); - if(pOtherPlayer && pOtherPlayer->m_Core.m_HookedPlayer == ID) - { - if(!NetworkClippedLine(SnappingClient, m_Pos, pOtherPlayer->m_Pos)) - { - AttachedHookInView = true; - break; - } - } - } - } - if(PlayerAndHookNotInView && !AttachedHookInView) - { + if(!IsSnappingCharacterInView(SnappingClient)) return; - } SnapCharacter(SnappingClient, ID); diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index c0c5bfdea..89dc031c4 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -41,6 +41,7 @@ public: void SwapClients(int Client1, int Client2) override; bool CanSnapCharacter(int SnappingClient); + bool IsSnappingCharacterInView(int SnappingClientID); bool IsGrounded(); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index a418b1c9d..8f7864c14 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -613,12 +613,12 @@ void CGameContext::SendStartWarning(int ClientID, const char *pMessage) } } -void CGameContext::SendEmoticon(int ClientID, int Emoticon) +void CGameContext::SendEmoticon(int ClientID, int Emoticon, int TargetClientID) { CNetMsg_Sv_Emoticon Msg; Msg.m_ClientID = ClientID; Msg.m_Emoticon = Emoticon; - Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, -1); + Server()->SendPackMsg(&Msg, MSGFLAG_VITAL, TargetClientID); } void CGameContext::SendWeaponPickup(int ClientID, int Weapon) @@ -2526,50 +2526,75 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) { CNetMsg_Cl_Emoticon *pMsg = (CNetMsg_Cl_Emoticon *)pRawMsg; - if(g_Config.m_SvSpamprotection && pPlayer->m_LastEmote && - pPlayer->m_LastEmote + maximum(Server()->TickSpeed() * g_Config.m_SvEmoticonDelay, g_Config.m_SvHighBandwidth ? 1 : 2) > Server()->Tick()) + auto &&CheckPreventEmote = [&](int64_t LastEmote, int64_t DelayInMs) { + return (LastEmote * (int64_t)1000) + (int64_t)Server()->TickSpeed() * DelayInMs > ((int64_t)Server()->Tick() * (int64_t)1000); + }; + + if(g_Config.m_SvSpamprotection && CheckPreventEmote((int64_t)pPlayer->m_LastEmote, (int64_t)g_Config.m_SvEmoticonMsDelay)) return; - pPlayer->m_LastEmote = Server()->Tick(); - pPlayer->UpdatePlaytime(); - - SendEmoticon(ClientID, pMsg->m_Emoticon); CCharacter *pChr = pPlayer->GetCharacter(); - if(pChr && g_Config.m_SvEmotionalTees && pPlayer->m_EyeEmoteEnabled) + // player needs a character to send emotes + if(pChr != nullptr) { - int EmoteType = EMOTE_NORMAL; - switch(pMsg->m_Emoticon) + pPlayer->m_LastEmote = Server()->Tick(); + pPlayer->UpdatePlaytime(); + + // check if the global emoticon is prevented and emotes are only send to nearby players + if(g_Config.m_SvSpamprotection && CheckPreventEmote((int64_t)pPlayer->m_LastEmoteGlobal, (int64_t)g_Config.m_SvGlobalEmoticonMsDelay)) { - case EMOTICON_EXCLAMATION: - case EMOTICON_GHOST: - case EMOTICON_QUESTION: - case EMOTICON_WTF: - EmoteType = EMOTE_SURPRISE; - break; - case EMOTICON_DOTDOT: - case EMOTICON_DROP: - case EMOTICON_ZZZ: - EmoteType = EMOTE_BLINK; - break; - case EMOTICON_EYES: - case EMOTICON_HEARTS: - case EMOTICON_MUSIC: - EmoteType = EMOTE_HAPPY; - break; - case EMOTICON_OOP: - case EMOTICON_SORRY: - case EMOTICON_SUSHI: - EmoteType = EMOTE_PAIN; - break; - case EMOTICON_DEVILTEE: - case EMOTICON_SPLATTEE: - case EMOTICON_ZOMG: - EmoteType = EMOTE_ANGRY; - break; - default: - break; + for(int i = 0; i < MAX_CLIENTS; ++i) + { + if(Server()->Translate(ClientID, i) && + m_apPlayers[i] && pChr->CanSnapCharacter(i) && pChr->IsSnappingCharacterInView(i)) + { + SendEmoticon(ClientID, pMsg->m_Emoticon, i); + } + } + } + else + { + // else send emoticons to all players + pPlayer->m_LastEmoteGlobal = Server()->Tick(); + SendEmoticon(ClientID, pMsg->m_Emoticon, -1); + } + + if(g_Config.m_SvEmotionalTees && pPlayer->m_EyeEmoteEnabled) + { + int EmoteType = EMOTE_NORMAL; + switch(pMsg->m_Emoticon) + { + case EMOTICON_EXCLAMATION: + case EMOTICON_GHOST: + case EMOTICON_QUESTION: + case EMOTICON_WTF: + EmoteType = EMOTE_SURPRISE; + break; + case EMOTICON_DOTDOT: + case EMOTICON_DROP: + case EMOTICON_ZZZ: + EmoteType = EMOTE_BLINK; + break; + case EMOTICON_EYES: + case EMOTICON_HEARTS: + case EMOTICON_MUSIC: + EmoteType = EMOTE_HAPPY; + break; + case EMOTICON_OOP: + case EMOTICON_SORRY: + case EMOTICON_SUSHI: + EmoteType = EMOTE_PAIN; + break; + case EMOTICON_DEVILTEE: + case EMOTICON_SPLATTEE: + case EMOTICON_ZOMG: + EmoteType = EMOTE_ANGRY; + break; + default: + break; + } + pChr->SetEmote(EmoteType, Server()->Tick() + 2 * Server()->TickSpeed()); } - pChr->SetEmote(EmoteType, Server()->Tick() + 2 * Server()->TickSpeed()); } } else if(MsgID == NETMSGTYPE_CL_KILL && !m_World.m_Paused) diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index f3f8ae0a5..77f0298a0 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -250,7 +250,7 @@ public: void SendChatTeam(int Team, const char *pText); void SendChat(int ClientID, int Team, const char *pText, int SpamProtectionClientID = -1, int Flags = CHAT_SIX | CHAT_SIXUP); void SendStartWarning(int ClientID, const char *pMessage); - void SendEmoticon(int ClientID, int Emoticon); + void SendEmoticon(int ClientID, int Emoticon, int TargetClientID); void SendWeaponPickup(int ClientID, int Weapon); void SendMotd(int ClientID); void SendSettings(int ClientID); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 3cfea294f..090ada463 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -278,7 +278,7 @@ void CPlayer::Tick() { if(1200 - ((Server()->Tick() - m_pCharacter->GetLastAction()) % (1200)) < 5) { - GameServer()->SendEmoticon(GetCID(), EMOTICON_GHOST); + GameServer()->SendEmoticon(GetCID(), EMOTICON_GHOST, -1); } } } diff --git a/src/game/server/player.h b/src/game/server/player.h index 2960e26fa..7246079c5 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -93,6 +93,7 @@ public: int m_LastSetSpectatorMode; int m_LastChangeInfo; int m_LastEmote; + int m_LastEmoteGlobal; int m_LastKill; int m_aLastCommands[4]; int m_LastCommandPos;