CS2-Kit
C++23 library for CS2 Metamod:Source plugin development
Loading...
Searching...
No Matches
UserMessage.cpp
Go to the documentation of this file.
1#include "Sdk/SigScanner.hpp"
2
3#include <igameevents.h>
4
5#include <CS2Kit/Sdk/GameData.hpp>
6#include <CS2Kit/Sdk/GameInterfaces.hpp>
7#include <CS2Kit/Sdk/UserMessage.hpp>
8#include <CS2Kit/Utils/Log.hpp>
9#include <engine/igameeventsystem.h>
10#include <irecipientfilter.h>
11#include <networksystem/inetworkmessages.h>
12#include <networksystem/netmessage.h>
13#include <usermessages.pb.h>
14
15namespace CS2Kit::Sdk
16{
17
18using namespace CS2Kit::Utils;
19
20bool MessageSystem::Initialize()
21{
22 auto& interfaces = GameInterfaces::Instance();
23
24 if (!interfaces.GameEventSystem)
25 {
26 Log::Error("IGameEventSystem not available.");
27 return false;
28 }
29
30 if (!interfaces.NetworkMessages)
31 {
32 Log::Error("INetworkMessages not available.");
33 return false;
34 }
35
36 Log::Info("Message system initialized.");
37 return true;
38}
39
40bool MessageSystem::InitGameEventManager()
41{
42 auto& interfaces = GameInterfaces::Instance();
43 auto& gameData = GameData::Instance();
44
45 void* eventManagerAddr = gameData.ResolveSignature("GameEventManager");
46 if (eventManagerAddr)
47 {
48 interfaces.GameEventManager =
49 *reinterpret_cast<IGameEventManager2**>(reinterpret_cast<uintptr_t>(eventManagerAddr));
50
51 if (interfaces.GameEventManager)
52 {
53 Log::Info("Game event manager resolved at {:#x}.",
54 reinterpret_cast<uintptr_t>(interfaces.GameEventManager));
55 }
56 else
57 {
58 Log::Warn("Game event manager pointer is null after resolve.");
59 }
60 }
61 else
62 {
63 Log::Warn("GameEventManager signature not found.");
64 }
65
66 void* legacyListenerAddr = gameData.FindSignature("LegacyGameEventListener");
67 if (legacyListenerAddr)
68 {
69 _getLegacyListener = reinterpret_cast<GetLegacyGameEventListenerFn>(legacyListenerAddr);
70 Log::Info("LegacyGameEventListener resolved.");
71 }
72 else
73 {
74 Log::Warn("LegacyGameEventListener signature not found (will use broadcast fallback).");
75 }
76
77 return interfaces.GameEventManager != nullptr;
78}
79
80void MessageSystem::SendCenterHtml(int slot, const std::string& html)
81{
82 auto* gameEventManager = GameInterfaces::Instance().GameEventManager;
83 if (!gameEventManager || slot < 0 || slot >= 64)
84 return;
85
86 IGameEvent* pEvent = gameEventManager->CreateEvent("show_survival_respawn_status");
87 if (!pEvent)
88 return;
89
90 pEvent->SetString("loc_token", html.c_str());
91 pEvent->SetInt("userid", slot);
92 pEvent->SetInt("duration", 5);
93
94 if (_getLegacyListener)
95 {
96 IGameEventListener2* pListener = _getLegacyListener(CPlayerSlot(slot));
97 if (pListener)
98 {
99 pListener->FireGameEvent(pEvent);
100 gameEventManager->FreeEvent(pEvent);
101 return;
102 }
103 }
104
105 gameEventManager->FireEvent(pEvent);
106}
107
108void MessageSystem::SendChatMessage(int slot, const std::string& message)
109{
110 auto& interfaces = GameInterfaces::Instance();
111 if (!interfaces.GameEventSystem || !interfaces.NetworkMessages || slot < 0 || slot >= 64)
112 return;
113
114 // CS2 routes server-originated chat through TextMsg with dest=HUD_PRINTTALK rather than
115 // SayText2. SayText2 requires a real source player and silently drops messages whose
116 // entityindex doesn't resolve to a connected client.
117 if (!_textMsgInternal)
118 {
119 _textMsgInternal = interfaces.NetworkMessages->FindNetworkMessage("CUserMessageTextMsg");
120 if (!_textMsgInternal)
121 _textMsgInternal = interfaces.NetworkMessages->FindNetworkMessagePartial("TextMsg");
122 }
123
124 if (!_textMsgInternal)
125 return;
126
127 CNetMessage* pMsg = _textMsgInternal->AllocateMessage();
128 if (!pMsg)
129 return;
130
131 auto* pTextMsg = pMsg->ToPB<CUserMessageTextMsg>();
132 if (!pTextMsg)
133 {
134 interfaces.NetworkMessages->DeallocateNetMessageAbstract(_textMsgInternal, pMsg);
135 return;
136 }
137 pTextMsg->set_dest(HudPrintTalk);
138 pTextMsg->add_param(message.c_str());
139
140 uint64_t clients = (1ULL << slot);
141
142 interfaces.GameEventSystem->PostEventAbstract(-1, false, 1, &clients, _textMsgInternal, pMsg, 0,
143 NetChannelBufType_t::BUF_RELIABLE);
144
145 interfaces.NetworkMessages->DeallocateNetMessageAbstract(_textMsgInternal, pMsg);
146}
147
148void MessageSystem::ClearCenterHtml(int slot)
149{
150 SendCenterHtml(slot, " ");
151}
152
153} // namespace CS2Kit::Sdk