CS2-Kit
C++23 library for CS2 Metamod:Source plugin development
Loading...
Searching...
No Matches
PlayerController.cpp
Go to the documentation of this file.
1#include "Sdk/Schema.hpp"
2#include "Sdk/VirtualCall.hpp"
3
4#include <CS2Kit/Sdk/Entity.hpp>
5#include <CS2Kit/Sdk/EntityRender.hpp>
6#include <CS2Kit/Sdk/GameData.hpp>
7#include <CS2Kit/Sdk/GameInterfaces.hpp>
8#include <CS2Kit/Sdk/PlayerController.hpp>
9#include <CS2Kit/Utils/Log.hpp>
10#include <cstring>
11#include <eiface.h>
12#include <entity2/entityinstance.h>
13#include <mathlib/vector.h>
14
15namespace CS2Kit::Sdk
16{
17
18using namespace CS2Kit::Utils;
19
20PlayerController::PlayerController(int slot) : _slot(slot)
21{
22 _controller = EntitySystem::Instance().GetPlayerController(slot);
23}
24
25bool PlayerController::IsValid() const
26{
27 return _controller != nullptr;
28}
29
30CEntityInstance* PlayerController::GetEntity() const
31{
32 return _controller;
33}
34
35CEntityInstance* PlayerController::GetPawn() const
36{
37 if (!_controller)
38 return nullptr;
39
40 int offset = SchemaService::Instance().GetOffset("CCSPlayerController", "m_hPlayerPawn");
41 if (offset < 0)
42 return nullptr;
43
44 auto hPawn = *reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(_controller) + offset);
45 return EntitySystem::Instance().ResolveEntityHandle(hPawn);
46}
47
48void PlayerController::Kick(const char* reason) const
49{
50 if (!IsValid())
51 return;
52
53 auto* engine = GameInterfaces::Instance().Engine;
54 if (!engine)
55 {
56 Log::Warn("PlayerController::Kick: IVEngineServer2 not available.");
57 return;
58 }
59
60 engine->DisconnectClient(CPlayerSlot(_slot), NETWORK_DISCONNECT_KICKED, reason);
61}
62
63template <typename T>
64T PlayerController::GetField(const char* className, const char* fieldName) const
65{
66 if (!_controller)
67 return T{};
68
69 int offset = SchemaService::Instance().GetOffset(className, fieldName);
70 if (offset < 0)
71 return T{};
72
73 return *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(_controller) + offset);
74}
75
76template <typename T>
77T PlayerController::GetPawnField(const char* className, const char* fieldName) const
78{
79 auto* pawn = GetPawn();
80 if (!pawn)
81 return T{};
82
83 int offset = SchemaService::Instance().GetOffset(className, fieldName);
84 if (offset < 0)
85 return T{};
86
87 return *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(pawn) + offset);
88}
89
90template <typename T>
91void PlayerController::SetField(const char* className, const char* fieldName, const T& value) const
92{
93 if (!_controller)
94 return;
95
96 int offset = SchemaService::Instance().GetOffset(className, fieldName);
97 if (offset < 0)
98 return;
99
100 *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(_controller) + offset) = value;
101}
102
103template <typename T>
104void PlayerController::SetPawnField(const char* className, const char* fieldName, const T& value) const
105{
106 auto* pawn = GetPawn();
107 if (!pawn)
108 return;
109
110 int offset = SchemaService::Instance().GetOffset(className, fieldName);
111 if (offset < 0)
112 return;
113
114 *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(pawn) + offset) = value;
115}
116
117int PlayerController::GetHealth() const
118{
119 return GetPawnField<int>("CBaseEntity", "m_iHealth");
120}
121
122int PlayerController::GetTeam() const
123{
124 return GetPawnField<int>("CBaseEntity", "m_iTeamNum");
125}
126
127int PlayerController::GetLifeState() const
128{
129 return GetPawnField<uint8_t>("CBaseEntity", "m_lifeState");
130}
131
132bool PlayerController::IsAlive() const
133{
134 return GetPawn() != nullptr && GetLifeState() == 0;
135}
136
137uint64_t PlayerController::GetButtons() const
138{
139 return EntitySystem::Instance().GetPlayerButtons(_slot);
140}
141
142int PlayerController::GetArmor() const
143{
144 return GetPawnField<int>("CCSPlayerPawn", "m_ArmorValue");
145}
146
147Vector PlayerController::GetAbsOrigin() const
148{
149 return GetPawnField<Vector>("CBaseEntity", "m_vecAbsOrigin");
150}
151
152QAngle PlayerController::GetAbsAngles() const
153{
154 return GetPawnField<QAngle>("CBaseEntity", "m_angRotation");
155}
156
157QAngle PlayerController::GetEyeAngles() const
158{
159 return GetPawnField<QAngle>("CCSPlayerPawnBase", "m_angEyeAngles");
160}
161
162void PlayerController::Slay() const
163{
164 auto* pawn = GetPawn();
165 if (!pawn)
166 return;
167
168 int vtableIndex = GameData::Instance().GetOffset("CommitSuicide");
169 if (vtableIndex < 0)
170 {
171 Log::Warn("PlayerController::Slay: CommitSuicide vtable offset not found.");
172 return;
173 }
174
175 CallVirtual<void>(vtableIndex, pawn, false, true);
176}
177
178void PlayerController::ChangeTeam(int team) const
179{
180 if (!_controller)
181 return;
182
183 int vtableIndex = GameData::Instance().GetOffset("ChangeTeam");
184 if (vtableIndex < 0)
185 {
186 Log::Warn("PlayerController::ChangeTeam: ChangeTeam vtable offset not found.");
187 return;
188 }
189
190 CallVirtual<void>(vtableIndex, _controller, team);
191}
192
193void PlayerController::Respawn() const
194{
195 if (!_controller)
196 return;
197
198 int vtableIndex = GameData::Instance().GetOffset("Respawn");
199 if (vtableIndex < 0)
200 {
201 Log::Warn("PlayerController::Respawn: Respawn vtable offset not found.");
202 return;
203 }
204
205 CallVirtual<void>(vtableIndex, _controller);
206}
207
208void PlayerController::Teleport(const Vector* origin, const QAngle* angles, const Vector* velocity) const
209{
210 auto* pawn = GetPawn();
211 if (!pawn)
212 return;
213
214 int vtableIndex = GameData::Instance().GetOffset("Teleport");
215 if (vtableIndex < 0)
216 {
217 Log::Warn("PlayerController::Teleport: Teleport vtable offset not found.");
218 return;
219 }
220
221 CallVirtual<void>(vtableIndex, pawn, origin, angles, velocity);
222}
223
224namespace
225{
226// Player name is stored as a 128-byte fixed buffer on CBasePlayerController.
227constexpr size_t PlayerNameBufferSize = 128;
228} // namespace
229
230int PlayerController::GetObserverMode() const
231{
232 return GetPawnField<uint8_t>("CPlayer_ObserverServices", "m_iObserverMode");
233}
234
235void PlayerController::SetObserverMode(uint8_t mode) const
236{
237 SetPawnField<uint8_t>("CPlayer_ObserverServices", "m_iObserverMode", mode);
238}
239
240std::string PlayerController::GetPlayerName() const
241{
242 if (!_controller)
243 return {};
244
245 int offset = SchemaService::Instance().GetOffset("CBasePlayerController", "m_iszPlayerName");
246 if (offset < 0)
247 return {};
248
249 auto* p = reinterpret_cast<const char*>(reinterpret_cast<uint8_t*>(_controller) + offset);
250 size_t len = 0;
251 while (len < PlayerNameBufferSize && p[len] != '\0')
252 ++len;
253 return std::string(p, len);
254}
255
256void PlayerController::SetPlayerName(const std::string& name) const
257{
258 if (!_controller)
259 return;
260
261 int offset = SchemaService::Instance().GetOffset("CBasePlayerController", "m_iszPlayerName");
262 if (offset < 0)
263 return;
264
265 auto* dst = reinterpret_cast<char*>(reinterpret_cast<uint8_t*>(_controller) + offset);
266 std::memset(dst, 0, PlayerNameBufferSize);
267 size_t copyLen = name.size();
268 if (copyLen >= PlayerNameBufferSize)
269 copyLen = PlayerNameBufferSize - 1;
270 if (copyLen > 0)
271 std::memcpy(dst, name.data(), copyLen);
272}
273
274void PlayerController::SetVisible(bool visible, uint8_t alpha) const
275{
276 auto* pawn = GetPawn();
277 if (!pawn)
278 return;
279
280 RenderMode_t mode = visible ? RenderMode_t::Normal : RenderMode_t::TransTexture;
281 uint32_t color = visible ? ColorOpaqueWhite : ((static_cast<uint32_t>(alpha) << 24) | 0x00FFFFFFu);
282
283 SetEntityRender(pawn, mode, color);
284}
285
286// Explicit template instantiations for common types
287template int PlayerController::GetField<int>(const char*, const char*) const;
288template uint8_t PlayerController::GetField<uint8_t>(const char*, const char*) const;
289template uint32_t PlayerController::GetField<uint32_t>(const char*, const char*) const;
290template float PlayerController::GetField<float>(const char*, const char*) const;
291template Vector PlayerController::GetField<Vector>(const char*, const char*) const;
292template QAngle PlayerController::GetField<QAngle>(const char*, const char*) const;
293
294template int PlayerController::GetPawnField<int>(const char*, const char*) const;
295template uint8_t PlayerController::GetPawnField<uint8_t>(const char*, const char*) const;
296template uint32_t PlayerController::GetPawnField<uint32_t>(const char*, const char*) const;
297template float PlayerController::GetPawnField<float>(const char*, const char*) const;
298template Vector PlayerController::GetPawnField<Vector>(const char*, const char*) const;
299template QAngle PlayerController::GetPawnField<QAngle>(const char*, const char*) const;
300
301template void PlayerController::SetField<int>(const char*, const char*, const int&) const;
302template void PlayerController::SetField<uint8_t>(const char*, const char*, const uint8_t&) const;
303template void PlayerController::SetField<uint32_t>(const char*, const char*, const uint32_t&) const;
304template void PlayerController::SetField<float>(const char*, const char*, const float&) const;
305template void PlayerController::SetField<Vector>(const char*, const char*, const Vector&) const;
306template void PlayerController::SetField<QAngle>(const char*, const char*, const QAngle&) const;
307
308template void PlayerController::SetPawnField<int>(const char*, const char*, const int&) const;
309template void PlayerController::SetPawnField<uint8_t>(const char*, const char*, const uint8_t&) const;
310template void PlayerController::SetPawnField<uint32_t>(const char*, const char*, const uint32_t&) const;
311template void PlayerController::SetPawnField<float>(const char*, const char*, const float&) const;
312template void PlayerController::SetPawnField<Vector>(const char*, const char*, const Vector&) const;
313template void PlayerController::SetPawnField<QAngle>(const char*, const char*, const QAngle&) const;
314
315} // namespace CS2Kit::Sdk
void SetEntityRender(CEntityInstance *entity, RenderMode_t mode, uint32_t color)