Files
GTASource/game/network/Chat/NetworkTextChat.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

867 lines
24 KiB
C++

#if __WIN32PC
//
// name: NetworkTextChat.h
//
// game headers
#include "fwnet/netchannel.h"
#include "Network/Chat/NetworkTextChat.h"
#include "frontend/ProfileSettings.h"
#include "Network/NetworkInterface.h"
#include "Network/Voice/NetworkVoice.h"
#include "Network/Objects/Entities/NetObjPlayer.h"
//#include "Network/Objects/Entities/NetObjPlayer.h"
#include "Network/Players/NetworkPlayerMgr.h"
#include "Peds/PlayerPed.h"
NETWORK_OPTIMISATIONS()
RAGE_DEFINE_SUBCHANNEL(net, textchat)
#undef __net_channel
#define __net_channel net_textchat
PARAM(netTextChatEnableFilter, "[TextChat] Enable text filtering");
static const unsigned DEFAULT_CHAT_FILTER_HOLDING_PEN_MAX_REQUESTS = 10;
static const unsigned DEFAULT_CHAT_FILTER_HOLDING_PEN_INTERVAL_MS = 500;
static const unsigned DEFAULT_CHAT_FILTER_SPAM_MAX_REQUESTS = 30;
static const unsigned DEFAULT_CHAT_FILTER_SPAM_INTERVAL_MS = 10000;
bool s_IsTextFilterEnabled = false;
unsigned s_FilterHoldingMaxRequests = DEFAULT_CHAT_FILTER_HOLDING_PEN_MAX_REQUESTS;
unsigned s_FilterHoldingPenIntervalMs = DEFAULT_CHAT_FILTER_HOLDING_PEN_INTERVAL_MS;
unsigned s_FilterSpamMaxRequests = DEFAULT_CHAT_FILTER_SPAM_MAX_REQUESTS;
unsigned s_FilterSpamIntervalMs = DEFAULT_CHAT_FILTER_SPAM_INTERVAL_MS;
bool IsTextChatFilteringEnabled()
{
return s_IsTextFilterEnabled || PARAM_netTextChatEnableFilter.Get();
}
struct CMsgTextChatStatus
{
NET_MESSAGE_DECL(CMsgTextChatStatus, CMSG_TEXT_CHAT_STATUS);
CMsgTextChatStatus()
: m_TextChatFlags(0)
{
}
NET_MESSAGE_SER(bb, msg)
{
return bb.SerUns(msg.m_TextChatFlags, 32)
//Don't send remotely set flags.
&& AssertVerify(0 == (msg.m_TextChatFlags & CNetGamePlayer::TEXT_LOCAL_MASK));
}
int GetMessageDataBitSize() const
{
return 32;
}
unsigned m_TextChatFlags;
};
NET_MESSAGE_IMPL(CMsgTextChatStatus);
static const unsigned DEFAULT_TEXT_CHAT_VALID_SEND_GROUPS = SentFromGroup::Groups_CloseContacts | SentFromGroup::Group_SameTeam;
CNetworkTextChat::CNetworkTextChat()
: m_TeamOnlyChat(false)
, m_IsInitialized(false)
, m_IsSpectatorToNonSpectatorChatEnabled(false)
, m_IsTransitioning(false)
, m_IsScriptControllingTeams(false)
, m_AllowedSentFromGroups(DEFAULT_TEXT_CHAT_VALID_SEND_GROUPS)
{
// Initialize our message handler. This will be registered with
// the network in Init().
m_Dlgt.Bind(this, &CNetworkTextChat::OnNetEvent);
m_TextChatDlgt.Bind(this, &CNetworkTextChat::OnTextChatEvent);
}
CNetworkTextChat::~CNetworkTextChat()
{
this->Shutdown();
}
bool
CNetworkTextChat::Init(netConnectionManager* cxnMgr)
{
bool success = false;
if(AssertVerify(!m_IsInitialized))
{
netInterface::AddDelegate(&m_Dlgt);
m_TextChat.AddDelegate(&m_TextChatDlgt);
m_TextChat.Init(TextChat::MAX_LOCAL_TYPERS,
TextChat::MAX_REMOTE_TYPERS,
cxnMgr,
NETWORK_GAME_CHANNEL_ID,
NETWORK_SESSION_ACTIVITY_CHANNEL_ID);
SetTeamOnlyChat(false);
m_IsScriptControllingTeams = false;
m_IsInitialized = true;
success = true;
}
return success;
}
void CNetworkTextChat::Shutdown()
{
if(m_IsInitialized)
{
gnetDebug3("CNetworkTextChat::Shutdown()");
m_TextChat.RemoveDelegate(&m_TextChatDlgt);
m_TextChat.Shutdown();
netInterface::RemoveDelegate(&m_Dlgt);
m_IsInitialized = false;
m_ChatHistory.Reset();
}
}
bool
CNetworkTextChat::IsInitialized() const
{
return m_IsInitialized;
}
void CNetworkTextChat::Update()
{
if(m_IsInitialized)
{
//Update the collection of typers who can send and receive text chat
this->UpdateTypers();
if(m_TextChat.IsInitialized())
{
m_TextChat.Update();
}
if(IsTextChatFilteringEnabled())
{
const int numChatHistories = m_ChatHistory.GetNumSlots();
for(int i = 0; i < numChatHistories; ++i)
{
ChatHistoryMap::Entry* entry = m_ChatHistory.GetEntry(i);
if(entry)
{
PlayerChatHistory& history = entry->data;
history.Update();
}
}
}
}
}
void
CNetworkTextChat::AddDelegate(Delegate* dlgt)
{
m_Delegator.AddDelegate(dlgt);
}
void
CNetworkTextChat::RemoveDelegate(Delegate* dlgt)
{
m_Delegator.RemoveDelegate(dlgt);
}
bool
CNetworkTextChat::AddTyper(const rlGamerInfo& gamerInfo,
const EndpointId endpointId)
{
bool success = false;
if(gnetVerifyf(m_TextChat.IsInitialized(), "Text chat not initialized"))
{
success = m_TextChat.AddTyper(gamerInfo, endpointId);
if(success)
{
RockstarId rockstarId = gamerInfo.GetGamerHandle().GetRockstarId();
if(m_ChatHistory.Access(rockstarId))
{
gnetDebug3("AddTyper :: %d removed from chat history", (int)rockstarId);
m_ChatHistory.Delete(rockstarId);
}
gnetAssertf(m_ChatHistory.GetNumUsed() < TextChat::MAX_TYPERS, "Too many players in the chat history (%d)", m_ChatHistory.GetNumUsed());
gnetDebug3("AddTyper :: %d added to chat history", (int)rockstarId);
m_ChatHistory.Insert(rockstarId, PlayerChatHistory(rockstarId, MakeFunctor(*this, &CNetworkTextChat::OnTextFiltered)));
}
else
{
gnetDebug3("AddTyper :: Failed for gamer %s", gamerInfo.GetName());
}
}
return success;
}
void
CNetworkTextChat::RemoveTyper(const rlGamerInfo& gamerInfo)
{
if(gnetVerifyf(m_TextChat.IsInitialized(), "Text chat not initialized"))
{
bool success = m_TextChat.RemoveTyper(gamerInfo.GetGamerId());
if(success)
{
RockstarId rockstarId = gamerInfo.GetGamerHandle().GetRockstarId();
if(m_ChatHistory.Access(rockstarId))
{
gnetDebug3("RemoveTyper :: %d removed from chat history", (int)rockstarId);
m_ChatHistory.Delete(rockstarId);
}
else
{
gnetDebug3("RemoveTyper :: %d was already previously removed from chat history", (int)rockstarId);
}
}
else
{
gnetDebug3("RemoveTyper :: Failed for gamer %s - already removed", gamerInfo.GetName());
}
}
}
void
CNetworkTextChat::RemoveAllRemoteTypers()
{
if(gnetVerifyf(m_TextChat.IsInitialized(), "Text chat not initialized"))
{
gnetDebug3("CNetworkTextChat::RemoveAllRemoteTypers()");
m_TextChat.RemoveAllRemoteTypers();
CNetGamePlayer* localPlayer = NetworkInterface::GetLocalPlayer();
bool isLocalPlayerInChatHistory = false;
RockstarId rockstarId = InvalidRockstarId;
if(localPlayer)
{
rockstarId = localPlayer->GetGamerInfo().GetGamerHandle().GetRockstarId();
isLocalPlayerInChatHistory = m_ChatHistory.Access(rockstarId) != NULL;
}
m_ChatHistory.Reset();
if(isLocalPlayerInChatHistory)
{
// Re-add the local player to the chat history
gnetAssertf(m_ChatHistory.GetNumUsed() < TextChat::MAX_TYPERS, "Too many players in the chat history (%d)", m_ChatHistory.GetNumUsed());
m_ChatHistory.Insert(rockstarId, PlayerChatHistory(rockstarId, MakeFunctor(*this, &CNetworkTextChat::OnTextFiltered)));
}
}
}
void
CNetworkTextChat::RemoveAllTypers()
{
if(gnetVerifyf(m_TextChat.IsInitialized(), "Text chat not initialized"))
{
m_TextChat.RemoveAllTypers();
m_ChatHistory.Reset();
}
}
bool
CNetworkTextChat::IsPlayerTyping(const rlGamerId& gamerId) const
{
return m_TextChat.IsInitialized()
&& m_TextChat.IsTyping(gamerId);
}
bool
CNetworkTextChat::IsAnyPlayerTyping() const
{
return m_TextChat.IsInitialized()
&& m_TextChat.IsAnyTyping();
}
bool
CNetworkTextChat::PlayerHasKeyboard(const rlGamerId& gamerId) const
{
const CNetGamePlayer* player =
NetworkInterface::GetPlayerFromGamerId(gamerId);
return player && player->HasKeyboard();
}
bool
CNetworkTextChat::SetPlayerMuted(const rlGamerId& gamerId, const bool muted)
{
bool success = false;
CNetGamePlayer* player =
NetworkInterface::GetPlayerFromGamerId(gamerId);
if(AssertVerify(player))
{
player->SetLocalTextChatFlag(CNetGamePlayer::TEXT_LOCAL_FORCE_MUTED, muted);
success = true;
}
return success;
}
bool
CNetworkTextChat::IsPlayerMutedByMe(const rlGamerId& gamerId) const
{
const VoiceGamerSettings* pSettings = FindVoiceGamerSettingsForGamerId(gamerId);
return pSettings && pSettings->IsMutedByMe();
}
bool
CNetworkTextChat::AmIMutedByPlayer(const rlGamerId& gamerId) const
{
const VoiceGamerSettings* pSettings = FindVoiceGamerSettingsForGamerId(gamerId);
return pSettings && pSettings->IsMutedByHim();
}
bool
CNetworkTextChat::IsPlayerBlockedByMe(const rlGamerId& gamerId) const
{
const VoiceGamerSettings* pSettings = FindVoiceGamerSettingsForGamerId(gamerId);
return pSettings && pSettings->IsBlockedByMe();
}
bool
CNetworkTextChat::AmIBlockedByPlayer(const rlGamerId& gamerId) const
{
const VoiceGamerSettings* pSettings = FindVoiceGamerSettingsForGamerId(gamerId);
return pSettings && pSettings->IsBlockedByHim();
}
bool
CNetworkTextChat::CanIHearPlayer(const rlGamerId& gamerId) const
{
const VoiceGamerSettings* pSettings = FindVoiceGamerSettingsForGamerId(gamerId);
return pSettings && pSettings->CanCommunicateTextWith();
}
void
CNetworkTextChat::SetTeamOnlyChat(const bool teamOnlyChat)
{
m_TeamOnlyChat = teamOnlyChat;
m_TextChat.SetTextChatRecipients(m_TeamOnlyChat ? TextChat::SEND_TO_TEAM : TextChat::SEND_TO_EVERYONE);
}
void
CNetworkTextChat::SetSpectatorToNonSpectatorChat(const bool enable)
{
m_IsSpectatorToNonSpectatorChatEnabled = enable;
}
bool
CNetworkTextChat::SubmitText(const rlGamerInfo& gamerInfo, const char* text, bool bTeamChat)
{
//@@: location AHOOK_0067_CHK_LOCATION_C
// We might not have the typer during a session transition
if(m_TextChat.HaveTyper(gamerInfo.GetGamerId()))
{
CNetworkTelemetry::RecordGameChatMessage(gamerInfo.GetGamerId(), text, bTeamChat);
AppendChatLog(gamerInfo.GetGamerHandle(), text);
return m_TextChat.SubmitText(gamerInfo.GetGamerId(), text, bTeamChat);
}
return false;
}
bool
CNetworkTextChat::SetLocalPlayerIsTyping(const rlGamerId& gamerId)
{
return m_TextChat.SetLocalPlayerIsTyping(gamerId);
}
//private:
void CNetworkTextChat::HandleTextStatus(const ReceivedMessageData &messageData)
{
CMsgTextChatStatus msg;
netPlayer* pPlayer = NULL;
AssertVerify(msg.Import(messageData.m_MessageData, messageData.m_MessageDataSize));
pPlayer = messageData.m_ToPlayer;
if (pPlayer)
{
ActivePlayerIndex idx = pPlayer->GetActivePlayerIndex();
CNetGamePlayer* pNetPlayer = NetworkInterface::GetPlayerMgr().GetActivePlayerFromIndex(idx);
if (pNetPlayer)
{
pNetPlayer->SetRemoteTextChatFlags(msg.m_TextChatFlags);
}
}
}
void CNetworkTextChat::OnNetEvent(const ReceivedMessageData &messageData)
{
unsigned msgId = 0;
if (gnetVerify(messageData.IsValid()))
{
if(netMessage::GetId(&msgId, messageData.m_MessageData, messageData.m_MessageDataSize))
{
if (msgId == CMsgTextChatStatus::MSG_ID())
{
this->HandleTextStatus(messageData);
}
}
}
}
/*
void
CNetworkTextChat::OnNetEvent(CNetGamePlayer* player,
const netEvent* evt)
{
const unsigned evtId = evt->GetId();
if(NET_EVENT_FRAME_RECEIVED == evtId)
{
const netEventFrameReceived* fr = evt->m_FrameReceived;
unsigned msgId;
if(netMessage::GetId(&msgId, fr->m_Payload, fr->m_SizeofPayload))
{
if(msgId == CMsgTextChatStatus::MSG_ID())
{
this->HandleTextStatus(player, fr);
}
}
}
else if(NET_EVENT_BANDWIDTH_EXCEEDED == evtId)
{
//FIXME (KB) - make an attempt to prioritize typers.
}
}
*/
void
CNetworkTextChat::OnTextChatEvent(rage::TextChat* UNUSED_PARAM(textChat),
const rlGamerHandle& gamerHandle,
const char* text,
bool teamOnly)
{
// check if we are allowed to receive from this player
if(CLiveManager::CanReceiveFrom(gamerHandle, RL_INVALID_CLAN_ID, m_AllowedSentFromGroups OUTPUT_ONLY(, "NetworkTextChat")))
{
// Append to file
AppendChatLog(gamerHandle, text);
// todo: spam check here
// todo: periodic profanity check here
m_Delegator.Dispatch(this, gamerHandle, text, teamOnly);
}
}
void
CNetworkTextChat::OnTextFiltered(const rlGamerHandle& sender, const char* text)
{
PlayerChatHistory* history = m_ChatHistory.Access(sender.GetRockstarId());
if(history)
{
history->AppendFilteredText(text);
}
}
void
CNetworkTextChat::UpdateTypers()
{
if(m_TextChat.IsInitialized())
{
CNetGamePlayer* myPlayer = NetworkInterface::GetLocalPlayer();
const int localGamerIdx = myPlayer->GetGamerInfo().GetLocalIndex();
const bool haveKeyboard = m_TextChat.HasKeyboard(localGamerIdx);
myPlayer->SetLocalTextChatFlag(CNetGamePlayer::TEXT_LOCAL_HAS_KEYBOARD, haveKeyboard);
if (m_IsTransitioning)
{
if(!CNetwork::GetNetworkSession().IsTransitionEstablished() || CNetwork::GetNetworkSession().IsTransitionLeaving())
{
m_IsTransitioning = false;
m_TextChat.SetInTransition(false);
RemoveAllRemoteTypers();
}
}
else
{
if(CNetwork::GetNetworkSession().IsTransitionEstablished() && !CNetwork::GetNetworkSession().IsTransitionLeaving())
{
m_IsTransitioning = true;
m_TextChat.SetInTransition(true);
RemoveAllRemoteTypers();
}
}
if (m_IsTransitioning)
{
rlGamerInfo aGamers[RL_MAX_GAMERS_PER_SESSION];
unsigned nGamers = CNetwork::GetNetworkSession().GetTransitionMembers(aGamers, RL_MAX_GAMERS_PER_SESSION);
for(unsigned i = 0; i < nGamers; i++)
{
// find their voice gamer entry
VoiceGamerSettings* pGamer = NetworkInterface::GetVoice().FindGamer(aGamers[i].GetGamerHandle());
if(!gnetVerify(pGamer))
{
gnetError("UpdateTypers :: No settings for %s!", aGamers[i].GetName());
continue;
}
const bool bHaveTyper = m_TextChat.HaveTyper(aGamers[i].GetGamerId());
bool bCanChat = pGamer->CanCommunicateTextWith();
if(bCanChat)
{
if(!bHaveTyper)
{
// get net endpointId
EndpointId endpointId;
CNetwork::GetNetworkSession().GetTransitionNetEndpointId(aGamers[i], &endpointId);
// only add with a valid net endpointId
if(!pGamer->IsLocal() && NET_IS_VALID_ENDPOINT_ID(endpointId))
{
gnetDebug1("Add Typer \"%s\"", aGamers[i].GetName());
m_TextChat.AddTyper(aGamers[i], endpointId);
}
}
}
else if(!bCanChat && bHaveTyper)
{
gnetDebug1("UpdateTypers() - !bCanChat && bHaveTyper - Remove Typer \"%s\"", aGamers[i].GetName());
m_TextChat.RemoveTyper(aGamers[i].GetGamerId());
}
}
}
else
{
//The message that we send to players to signal changes in our text chat status.
CMsgTextChatStatus msgTs;
CNetGamePlayer* player = NULL;
for (ActivePlayerIndex i = 0; i < MAX_NUM_ACTIVE_PLAYERS; i++)
{
player = NetworkInterface::GetPlayerMgr().GetActivePlayerFromIndex(i);
if(!player)
{
continue;
}
//Determine if we've locally blocked or muted the player.
//Also let the player know if we have a keyboard.
const rlGamerInfo& gamerInfo = player->GetGamerInfo();
bool haveTyper = m_TextChat.HaveTyper(gamerInfo.GetGamerId());
const bool isMuted = m_TextChat.IsMuted(gamerInfo.GetGamerHandle());
const bool isBlocked = !m_TextChat.HasTextChatPrivileges(localGamerIdx);
// NOTE: These flags are unused. Muting and blocking is done at a higher level.
player->SetLocalTextChatFlag(CNetGamePlayer::TEXT_LOCAL_HAS_KEYBOARD, haveKeyboard);
player->SetLocalTextChatFlag(CNetGamePlayer::TEXT_LOCAL_MUTED, isMuted);
player->SetLocalTextChatFlag(CNetGamePlayer::TEXT_LOCAL_BLOCKED, isBlocked);
bool canChat = (player->GetConnectionId() >= 0) || player->IsMyPlayer();
if(canChat && !haveTyper)
{
haveTyper = AddTyper(gamerInfo, player->GetEndpointId());
}
if(canChat && !player->IsMyPlayer())
{
if(player->AreTextChatFlagsDirty())
{
msgTs.m_TextChatFlags =
CNetGamePlayer::LocalToRemoteTextChatFlags(player->GetLocalTextChatFlags());
NetworkInterface::SendReliableMessage(player, msgTs);
player->CleanDirtyTextChatFlags();
}
if(player->GetPlayerPed()
&& (NetworkInterface::IsSessionActive() || NetworkInterface::IsInSpectatorMode()))
{
const bool sameTeam = (myPlayer->GetTeam() >= 0 && myPlayer->GetTeam() == player->GetTeam());
// Set the Same Team state only if the players can chat. Otherwise, the typer won't exist in this
// player's typer list. This can occur if one teammate is spectating and one is still alive, and for some reason adding the player failed above.
if(haveTyper && !m_IsScriptControllingTeams)
{
m_TextChat.SetOnSameTeam(player->GetEndpointId(), sameTeam);
}
CNetObjPlayer* pOtherPlayerObj = player->GetPlayerPed() ? static_cast<CNetObjPlayer*>(player->GetPlayerPed()->GetNetworkObject()) : NULL;
const bool otherPlayerSpectating = (pOtherPlayerObj && pOtherPlayerObj->IsSpectating());
if (NetworkInterface::IsInSpectatorMode() && canChat)
{
canChat = otherPlayerSpectating || m_IsSpectatorToNonSpectatorChatEnabled;
}
}
VoiceGamerSettings* pSettings = NULL;
if (player)
{
pSettings = NetworkInterface::GetVoice().FindGamer(player->GetGamerInfo().GetGamerHandle());
}
if (pSettings)
{
// we just use the voice mute/block states for text chat as well
canChat = canChat && pSettings->CanCommunicateTextWith();
}
}
if(!canChat && haveTyper && !m_IsScriptControllingTeams)
{
RemoveTyper(gamerInfo);
}
}
}
}
}
const VoiceGamerSettings* CNetworkTextChat::FindVoiceGamerSettingsForGamerId(const rlGamerId& gamerId) const
{
VoiceGamerSettings* pVoiceGamerSettings = NULL;
if(m_IsTransitioning)
{
rlGamerInfo aGamers[RL_MAX_GAMERS_PER_SESSION];
unsigned nGamers = CNetwork::GetNetworkSession().GetTransitionMembers(aGamers, RL_MAX_GAMERS_PER_SESSION);
for(unsigned i = 0; i < nGamers; i++)
{
if(aGamers[i].GetGamerId() == gamerId)
{
// find their voice gamer entry
pVoiceGamerSettings = NetworkInterface::GetVoice().FindGamer(aGamers[i].GetGamerHandle());
break;
}
}
}
else
{
const CNetGamePlayer* player = NetworkInterface::GetPlayerFromGamerId(gamerId);
if (player)
{
pVoiceGamerSettings = NetworkInterface::GetVoice().FindGamer(player->GetGamerInfo().GetGamerHandle());
}
}
return pVoiceGamerSettings;
}
void CNetworkTextChat::AppendChatLog(const rlGamerHandle& gamerHandle, const char* text)
{
RockstarId rockstarId = NULL;
if(m_IsTransitioning)
{
rlGamerInfo aGamers[RL_MAX_GAMERS_PER_SESSION];
unsigned nGamers = CNetwork::GetNetworkSession().GetTransitionMembers(aGamers, RL_MAX_GAMERS_PER_SESSION);
for(unsigned i = 0; i < nGamers; i++)
{
if(aGamers[i].GetGamerHandle() == gamerHandle)
{
rockstarId = aGamers[i].GetGamerHandle().GetRockstarId();
break;
}
}
}
else
{
CNetGamePlayer* player = NetworkInterface::GetPlayerFromGamerHandle(gamerHandle);
if(!gnetVerifyf(player, "Could not get player from gamerHandle"))
return;
rockstarId = player->GetGamerInfo().GetGamerHandle().GetRockstarId();
}
PlayerChatHistory* history = m_ChatHistory.Access(rockstarId);
if(!history && m_IsTransitioning)
{
if(rockstarId != InvalidRockstarId)
{
gnetAssertf(m_ChatHistory.GetNumUsed() < TextChat::MAX_TYPERS, "Too many players in the chat history (%d)", m_ChatHistory.GetNumUsed());
m_ChatHistory.Insert(rockstarId, PlayerChatHistory(rockstarId, MakeFunctor(*this, &CNetworkTextChat::OnTextFiltered)));
history = m_ChatHistory.Access(rockstarId);
}
}
if(gnetVerifyf(history, "Could not get a history for player %d", rockstarId))
{
history->AppendText(text);
}
}
bool CNetworkTextChat::GetChatHistoryWithPlayer(const RockstarId& rockstarId, RsonWriter* rw)
{
bool result = false;
PlayerChatHistory* reportedPlayerHistory = m_ChatHistory.Access(rockstarId);
PlayerChatHistory* localPlayerHistory = m_ChatHistory.Access(NetworkInterface::GetLocalGamerHandle().GetRockstarId());
if(gnetVerifyf(reportedPlayerHistory, "Could not get a history for player %d", rockstarId) && gnetVerifyf(localPlayerHistory, "Could not get a history for local player"))
{
result = rw->BeginArray("chatlog", NULL);
// We need to write the history in chronological order, so we'll loop over the two history and write the oldest line
// until we've read all the lines for both players
int reportedPlayerIndex = reportedPlayerHistory->index;
int localPlayerIndex = localPlayerHistory->index;
int reportedPlayerConsumed = 0;
int localPlayerConsumed = 0;
while(reportedPlayerConsumed!=MAX_CHATLOG_SIZE || localPlayerConsumed!=MAX_CHATLOG_SIZE)
{
ChatHistoryLine* line = NULL;
RockstarId playerWhoWroteTheLine=0;
// if we're the oldest next message and not finished already (in which case we would be the oldest message)
// Or the other is finished
if(reportedPlayerConsumed!=MAX_CHATLOG_SIZE &&
(reportedPlayerHistory->history[reportedPlayerIndex].timestamp <= localPlayerHistory->history[localPlayerIndex].timestamp
|| localPlayerConsumed==MAX_CHATLOG_SIZE) )
{
playerWhoWroteTheLine=rockstarId;
line = & reportedPlayerHistory->history[reportedPlayerIndex];
reportedPlayerConsumed++;
reportedPlayerIndex++;
if(reportedPlayerIndex>=MAX_CHATLOG_SIZE)
reportedPlayerIndex=0;
}
if(!playerWhoWroteTheLine && localPlayerConsumed!=MAX_CHATLOG_SIZE)
{
playerWhoWroteTheLine=NetworkInterface::GetLocalGamerHandle().GetRockstarId();
line = & localPlayerHistory->history[localPlayerIndex];
localPlayerConsumed++;
localPlayerIndex++;
if(localPlayerIndex>=MAX_CHATLOG_SIZE)
localPlayerIndex=0;
}
// Append the line
if(gnetVerifyf(line, "No next line to append") && strlen(line->message)>0 && playerWhoWroteTheLine)
{
rw->Begin(NULL, NULL);
result = result && rw->WriteInt64("t", line->timestamp);
result = result && rw->WriteInt64("id", playerWhoWroteTheLine);
result = result && rw->WriteString("m", line->message);
rw->End();
}
}
result = result && rw->End();
}
return result;
}
bool CNetworkTextChat::IsTyperValid(const EndpointId endpointId) const
{
return m_TextChat.HaveTyper(endpointId);
}
void CNetworkTextChat::PlayerHasLeft(const rlGamerInfo& gamerInfo)
{
if(!m_IsInitialized)
{
gnetDebug1("PlayerHasLeft :: Not removing %s, Textchat not Initialised", gamerInfo.GetName());
return;
}
gnetAssertf(gamerInfo.IsValid(), "PlayerHasLeft :: Invalid gamer info");
gnetDebug1("PlayerHasLeft :: Removing typer %s", gamerInfo.GetName());
RemoveTyper(gamerInfo);
}
void CNetworkTextChat::OnTunablesRead()
{
m_AllowedSentFromGroups = CLiveManager::GetValidSentFromGroupsFromTunables(DEFAULT_TEXT_CHAT_VALID_SEND_GROUPS, "TEXT_CHAT");
s_IsTextFilterEnabled = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_CHAT_FILTER_ENABLED", 0x31c99855), s_IsTextFilterEnabled);
s_FilterHoldingMaxRequests = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_CHAT_FILTER_HOLDING_PEN_MAX_REQUESTS", 0x197ba46b), s_FilterHoldingMaxRequests);
s_FilterHoldingPenIntervalMs = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_CHAT_FILTER_HOLDING_PEN_INTERVAL_MS", 0xa233de30), s_FilterHoldingPenIntervalMs);
s_FilterSpamMaxRequests = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_CHAT_FILTER_SPAM_MAX_REQUESTS", 0xa5417a51), s_FilterSpamMaxRequests);
s_FilterSpamIntervalMs = Tunables::GetInstance().TryAccess(CD_GLOBAL_HASH, ATSTRINGHASH("NET_CHAT_FILTER_SPAM_INTERVAL_MS", 0x80c6f91c), s_FilterSpamIntervalMs);
}
CNetworkTextChat::PlayerChatHistory::PlayerChatHistory(RockstarId pid, NetworkTextFilter::OnReceivedTextProcessedCB callback)
: playerid(pid)
, index (0)
{
// build a handle so we don't need to keep doing this
gamerHandle.ResetSc(playerid);
if(IsTextChatFilteringEnabled())
{
filter.Init(
callback,
gamerHandle,
s_FilterHoldingMaxRequests,
s_FilterHoldingPenIntervalMs,
s_FilterSpamMaxRequests,
s_FilterSpamIntervalMs
OUTPUT_ONLY(, "TextChat"));
}
}
void CNetworkTextChat::PlayerChatHistory::Update()
{
if(IsTextChatFilteringEnabled())
{
filter.Update();
}
}
void CNetworkTextChat::PlayerChatHistory::AppendText(const char* text)
{
if(IsTextChatFilteringEnabled() && filter.IsInitialised())
{
// if filtering is enabled, throw it at that first
filter.AddReceivedText(gamerHandle, text);
}
else
{
// otherwise, we can treat this as filtered text
AppendFilteredText(text);
}
}
void CNetworkTextChat::PlayerChatHistory::AppendFilteredText(const char* textMessage)
{
formatf(history[index].message, "%s", textMessage);
history[index].timestamp = rlGetPosixTime();
index++;
if (index >= MAX_CHATLOG_SIZE)
index = 0;
}
void CNetworkTextChat::PlayerChatHistory::Reset()
{
for (int i = 0; i < MAX_CHATLOG_SIZE; i++)
history[i].Reset();
index = 0;
filter.Shutdown();
}
#endif