commit
6d117a96a3
19
CHANGELOG.md
19
CHANGELOG.md
@ -4,11 +4,27 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/).
|
||||
|
||||
## r4251 - 2023-05-03
|
||||
|
||||
### Added
|
||||
|
||||
- Add GSC client field `ping` as a read-only field (#1002)
|
||||
- Add GSC client field `address` as a read-only field (#1003)
|
||||
- Add to the iw4x-rawfiles common_scripts\utility GSC script `getIP` function.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `getPing` function in `common_scripts\utility` now works.
|
||||
|
||||
### Known issues
|
||||
|
||||
- Sound issue fix is experimental as the bug is not fully understood.
|
||||
|
||||
## r4246 - 2023-05-03
|
||||
|
||||
### Added
|
||||
|
||||
- Add to the iw4x-rawfiles common_scripts\utility GSC script `setPing` & `getPing` for backward compatibility.
|
||||
- Add to the iw4x-rawfiles `common_scripts\utility` GSC script `setPing` & `getPing` functions for backward compatibility.
|
||||
|
||||
### Fixed
|
||||
|
||||
@ -336,6 +352,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
|
||||
### Changed
|
||||
|
||||
- `sv_mapRotationCurrent` is not being used anymore (#302)
|
||||
- `sv_mapRotation` is parsed once on startup (#283)
|
||||
|
||||
### Security
|
||||
|
||||
|
@ -186,12 +186,12 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Bans::BanClient(Game::client_t* cl, const std::string& reason)
|
||||
void Bans::BanClient(Game::client_s* cl, const std::string& reason)
|
||||
{
|
||||
SteamID guid;
|
||||
guid.bits = cl->steamID;
|
||||
|
||||
InsertBan({guid, cl->header.netchan.remoteAddress.ip});
|
||||
InsertBan({ guid, cl->header.netchan.remoteAddress.ip });
|
||||
|
||||
Game::SV_DropClient(cl, reason.data(), true);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace Components
|
||||
|
||||
Bans();
|
||||
|
||||
static void BanClient(Game::client_t* cl, const std::string& reason);
|
||||
static void BanClient(Game::client_s* cl, const std::string& reason);
|
||||
static void UnbanClient(SteamID id);
|
||||
static void UnbanClient(Game::netIP_t ip);
|
||||
|
||||
|
@ -274,7 +274,7 @@ namespace Components
|
||||
});
|
||||
}
|
||||
|
||||
void Bots::BotAiAction(Game::client_t* cl)
|
||||
void Bots::BotAiAction(Game::client_s* cl)
|
||||
{
|
||||
if (!cl->gentity)
|
||||
{
|
||||
@ -409,9 +409,9 @@ namespace Components
|
||||
|
||||
Bots::Bots()
|
||||
{
|
||||
AssertOffset(Game::client_t, bIsTestClient, 0x41AF0);
|
||||
AssertOffset(Game::client_t, ping, 0x212C8);
|
||||
AssertOffset(Game::client_t, gentity, 0x212A0);
|
||||
AssertOffset(Game::client_s, bIsTestClient, 0x41AF0);
|
||||
AssertOffset(Game::client_s, ping, 0x212C8);
|
||||
AssertOffset(Game::client_s, gentity, 0x212A0);
|
||||
|
||||
// Replace connect string
|
||||
Utils::Hook::Set<const char*>(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\clanAbbrev\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\"");
|
||||
|
@ -25,7 +25,7 @@ namespace Components
|
||||
static void GScr_isTestClient(Game::scr_entref_t entref);
|
||||
static void AddScriptMethods();
|
||||
|
||||
static void BotAiAction(Game::client_t* cl);
|
||||
static void BotAiAction(Game::client_s* cl);
|
||||
static void SV_BotUserMove_Hk();
|
||||
|
||||
static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex);
|
||||
|
@ -29,7 +29,7 @@ namespace Components
|
||||
return lock;
|
||||
}
|
||||
|
||||
const char* Chat::EvaluateSay(char* text, Game::gentity_t* player, int mode)
|
||||
const char* Chat::EvaluateSay(char* text, Game::gentity_s* player, int mode)
|
||||
{
|
||||
SendChat = true;
|
||||
|
||||
@ -274,7 +274,7 @@ namespace Components
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Chat::IsMuted(const Game::client_t* cl)
|
||||
bool Chat::IsMuted(const Game::client_s* cl)
|
||||
{
|
||||
const auto clientNum = cl - Game::svs_clients;
|
||||
const auto xuid = Game::svs_clients[clientNum].steamID;
|
||||
@ -287,7 +287,7 @@ namespace Components
|
||||
return result;
|
||||
}
|
||||
|
||||
void Chat::MuteClient(const Game::client_t* client)
|
||||
void Chat::MuteClient(const Game::client_s* client)
|
||||
{
|
||||
const auto xuid = client->steamID;
|
||||
MutedList.access([&](muteList& clients)
|
||||
@ -300,7 +300,7 @@ namespace Components
|
||||
Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You were muted\"", 0x65));
|
||||
}
|
||||
|
||||
void Chat::UnmuteClient(const Game::client_t* client)
|
||||
void Chat::UnmuteClient(const Game::client_s* client)
|
||||
{
|
||||
UnmuteInternal(client->steamID);
|
||||
|
||||
@ -606,7 +606,7 @@ namespace Components
|
||||
|
||||
Chat::Chat()
|
||||
{
|
||||
AssertOffset(Game::client_t, steamID, 0x43F00);
|
||||
AssertOffset(Game::client_s, steamID, 0x43F00);
|
||||
|
||||
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
|
||||
sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients");
|
||||
|
@ -9,7 +9,7 @@ namespace Components
|
||||
Chat();
|
||||
|
||||
static bool IsMuted(const Game::gentity_s* ent);
|
||||
static bool IsMuted(const Game::client_t* cl);
|
||||
static bool IsMuted(const Game::client_s* cl);
|
||||
|
||||
private:
|
||||
static Dvar::Var cg_chatWidth;
|
||||
@ -27,7 +27,7 @@ namespace Components
|
||||
|
||||
static std::unique_lock<Utils::NamedMutex> Lock();
|
||||
|
||||
static const char* EvaluateSay(char* text, Game::gentity_t* player, int mode);
|
||||
static const char* EvaluateSay(char* text, Game::gentity_s* player, int mode);
|
||||
|
||||
static void PreSayStub();
|
||||
static void PostSayStub();
|
||||
@ -36,8 +36,8 @@ namespace Components
|
||||
static void CG_AddToTeamChat(const char* text);
|
||||
static void CG_AddToTeamChat_Stub();
|
||||
|
||||
static void MuteClient(const Game::client_t* client);
|
||||
static void UnmuteClient(const Game::client_t* client);
|
||||
static void MuteClient(const Game::client_s* client);
|
||||
static void UnmuteClient(const Game::client_s* client);
|
||||
static void UnmuteInternal(std::uint64_t id, bool everyone = false);
|
||||
static void SaveMutedList(const muteList& list);
|
||||
static void LoadMutedList();
|
||||
|
@ -298,8 +298,7 @@ namespace Components
|
||||
const auto* name = params->get(1);
|
||||
|
||||
ent->client->visionDuration[visMode] = duration;
|
||||
strncpy_s(ent->client->visionName[visMode],
|
||||
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||
strncpy_s(ent->client->visionName[visMode], sizeof(Game::gclient_s::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||
|
||||
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||
});
|
||||
@ -325,8 +324,7 @@ namespace Components
|
||||
const auto* name = params->get(1);
|
||||
|
||||
ent->client->visionDuration[visMode] = duration;
|
||||
strncpy_s(ent->client->visionName[visMode],
|
||||
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||
strncpy_s(ent->client->visionName[visMode], sizeof(Game::gclient_s::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||
|
||||
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||
});
|
||||
|
@ -56,11 +56,11 @@ namespace Components
|
||||
Utils::Hook::Call<void(int)>(0x4AA430)(clientNum); // ClientDisconnect
|
||||
}
|
||||
|
||||
void Events::SV_UserinfoChanged_Hk(Game::client_t* cl)
|
||||
void Events::SV_UserinfoChanged_Hk(Game::client_s* cl)
|
||||
{
|
||||
ClientConnectSignal(cl);
|
||||
|
||||
Utils::Hook::Call<void(Game::client_t*)>(0x401950)(cl); // SV_UserinfoChanged
|
||||
Utils::Hook::Call<void(Game::client_s*)>(0x401950)(cl); // SV_UserinfoChanged
|
||||
}
|
||||
|
||||
void Events::SteamDisconnect_Hk()
|
||||
|
@ -6,7 +6,7 @@ namespace Components
|
||||
{
|
||||
public:
|
||||
typedef void(ClientCallback)(int clientNum);
|
||||
typedef void(ClientConnectCallback)(Game::client_t* cl);
|
||||
typedef void(ClientConnectCallback)(Game::client_s* cl);
|
||||
typedef void(Callback)();
|
||||
|
||||
Events();
|
||||
@ -40,7 +40,7 @@ namespace Components
|
||||
static Utils::Signal<Callback> DvarInitSignal;
|
||||
|
||||
static void ClientDisconnect_Hk(int clientNum);
|
||||
static void SV_UserinfoChanged_Hk(Game::client_t* cl);
|
||||
static void SV_UserinfoChanged_Hk(Game::client_s* cl);
|
||||
static void SteamDisconnect_Hk();
|
||||
static void Scr_ShutdownSystem_Hk(unsigned char sys);
|
||||
static void CL_InitOnceForAllClients_HK();
|
||||
|
166
src/Components/Modules/GSC/Field.cpp
Normal file
166
src/Components/Modules/GSC/Field.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "Field.hpp"
|
||||
|
||||
namespace Components::GSC
|
||||
{
|
||||
std::unordered_map<std::uint16_t, Field::EntField> Field::CustomEntityFields;
|
||||
std::unordered_map<std::uint16_t, Field::ClientFields> Field::CustomClientFields;
|
||||
|
||||
void Field::AddEntityField(const char* name, const ScriptCallbackEnt& setter, const ScriptCallbackEnt& getter)
|
||||
{
|
||||
static std::uint16_t fieldOffsetStart = 15; // fields count
|
||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||
|
||||
CustomEntityFields[fieldOffsetStart] = { name, fieldOffsetStart, setter, getter };
|
||||
++fieldOffsetStart;
|
||||
}
|
||||
|
||||
void Field::AddClientField(const char* name, const ScriptCallbackClient& setter, const ScriptCallbackClient& getter)
|
||||
{
|
||||
static std::uint16_t fieldOffsetStart = 21; // fields count
|
||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||
|
||||
const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated
|
||||
|
||||
// Use 'index' in 'array' as map key. It will be used later in Scr_SetObjectFieldStub
|
||||
CustomClientFields[fieldOffsetStart] = { name, offset, setter, getter };
|
||||
++fieldOffsetStart;
|
||||
}
|
||||
|
||||
void Field::GScr_AddFieldsForEntityStub()
|
||||
{
|
||||
for (const auto& field : CustomEntityFields | std::views::values)
|
||||
{
|
||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
|
||||
|
||||
for (const auto& field : CustomClientFields | std::views::values)
|
||||
{
|
||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||
}
|
||||
}
|
||||
|
||||
// Because some functions are inlined we have to hook this function instead of Scr_SetEntityField
|
||||
int Field::Scr_SetObjectFieldStub(const unsigned int classnum, const int entnum, const int offset)
|
||||
{
|
||||
if (classnum == Game::ClassNum::CLASS_NUM_ENTITY)
|
||||
{
|
||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||
if (const auto itr = CustomEntityFields.find(entity_offset); itr != CustomEntityFields.end())
|
||||
{
|
||||
itr->second.setter(&Game::g_entities[entnum], offset);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// No custom generic field was found, let the game handle it
|
||||
return Game::Scr_SetObjectField(classnum, entnum, offset);
|
||||
}
|
||||
|
||||
// Offset was already converted to array 'index' following binop offset & ~Game::ENTFIELD_MASK
|
||||
void Field::Scr_SetClientFieldStub(Game::gclient_s* client, const int offset)
|
||||
{
|
||||
const auto client_offset = static_cast<std::uint16_t>(offset);
|
||||
if (const auto itr = CustomClientFields.find(client_offset); itr != CustomClientFields.end())
|
||||
{
|
||||
itr->second.setter(nullptr, client, &itr->second);
|
||||
return;
|
||||
}
|
||||
|
||||
// No custom field client was found, let the game handle it
|
||||
Game::Scr_SetClientField(client, offset);
|
||||
}
|
||||
|
||||
void Field::Scr_GetEntityFieldStub(const int entnum, int offset)
|
||||
{
|
||||
if ((offset & Game::ENTFIELD_MASK) == Game::ENTFIELD_CLIENT)
|
||||
{
|
||||
// If we have a ENTFIELD_CLIENT offset we need to check g_entity is actually a fully connected client
|
||||
if (Game::g_entities[entnum].client != nullptr)
|
||||
{
|
||||
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
||||
if (const auto itr = CustomClientFields.find(client_offset); itr != CustomClientFields.end())
|
||||
{
|
||||
// Game functions probably don't ever need to use the reference to client_fields_s...
|
||||
itr->second.getter(&Game::svs_clients[entnum], Game::g_entities[entnum].client, &itr->second);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regular entity offsets can be searched directly in our custom handler
|
||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||
|
||||
if (const auto itr = CustomEntityFields.find(entity_offset); itr != CustomEntityFields.end())
|
||||
{
|
||||
itr->second.getter(&Game::g_entities[entnum], offset);
|
||||
return;
|
||||
}
|
||||
|
||||
// No custom generic field was found, let the game handle it
|
||||
Game::Scr_GetEntityField(entnum, offset);
|
||||
}
|
||||
|
||||
|
||||
void Field::AddEntityFields()
|
||||
{
|
||||
AddEntityField("entityflags",
|
||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||
{
|
||||
ent->flags = Game::Scr_GetInt(0);
|
||||
},
|
||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||
{
|
||||
Game::Scr_AddInt(ent->flags);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Field::AddClientFields()
|
||||
{
|
||||
AddClientField("clientflags",
|
||||
[]([[maybe_unused]] Game::client_s* client, Game::gclient_s* pSelf, [[maybe_unused]] const ClientFields* pField)
|
||||
{
|
||||
pSelf->flags = Game::Scr_GetInt(0);
|
||||
},
|
||||
[]([[maybe_unused]] Game::client_s* client, Game::gclient_s* pSelf, [[maybe_unused]] const ClientFields* pField)
|
||||
{
|
||||
Game::Scr_AddInt(pSelf->flags);
|
||||
}
|
||||
);
|
||||
|
||||
AddClientField("ping",
|
||||
[]([[maybe_unused]] Game::client_s* client, [[maybe_unused]] Game::gclient_s* pSelf, [[maybe_unused]] const ClientFields* pField)
|
||||
{
|
||||
},
|
||||
[]([[maybe_unused]] Game::client_s* client, [[maybe_unused]] Game::gclient_s* pSelf, [[maybe_unused]] const ClientFields* pField)
|
||||
{
|
||||
Game::Scr_AddInt(client->ping);
|
||||
}
|
||||
);
|
||||
|
||||
AddClientField("address",
|
||||
[]([[maybe_unused]] Game::client_s* client, [[maybe_unused]] Game::gclient_s* pSelf, [[maybe_unused]] const ClientFields* pField)
|
||||
{
|
||||
},
|
||||
[]([[maybe_unused]] Game::client_s* client, [[maybe_unused]] Game::gclient_s* pSelf, [[maybe_unused]] const ClientFields* pField)
|
||||
{
|
||||
Game::Scr_AddString(Game::NET_AdrToString(client->header.netchan.remoteAddress));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Field::Field()
|
||||
{
|
||||
AddEntityFields();
|
||||
AddClientFields();
|
||||
|
||||
Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
||||
|
||||
Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
||||
Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
||||
Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
||||
}
|
||||
}
|
48
src/Components/Modules/GSC/Field.hpp
Normal file
48
src/Components/Modules/GSC/Field.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components::GSC
|
||||
{
|
||||
class Field : public Component
|
||||
{
|
||||
public:
|
||||
Field();
|
||||
|
||||
private:
|
||||
struct EntField
|
||||
{
|
||||
const char* name;
|
||||
int ofs;
|
||||
void(*setter)(Game::gentity_s*, int);
|
||||
void(*getter)(Game::gentity_s*, int);
|
||||
};
|
||||
|
||||
struct ClientFields
|
||||
{
|
||||
const char* name;
|
||||
int ofs;
|
||||
void(*setter)(Game::client_s*, Game::gclient_s*, const ClientFields*);
|
||||
void(*getter)(Game::client_s*, Game::gclient_s*, const ClientFields*);
|
||||
};
|
||||
|
||||
typedef void(*ScriptCallbackEnt)(Game::gentity_s*, int);
|
||||
typedef void(*ScriptCallbackClient)(Game::client_s*, Game::gclient_s*, const ClientFields*);
|
||||
|
||||
static std::unordered_map<std::uint16_t, EntField> CustomEntityFields;
|
||||
static std::unordered_map<std::uint16_t, ClientFields> CustomClientFields;
|
||||
|
||||
static void AddEntityField(const char* name, const ScriptCallbackEnt& setter, const ScriptCallbackEnt& getter);
|
||||
static void AddClientField(const char* name, const ScriptCallbackClient& setter, const ScriptCallbackClient& getter);
|
||||
|
||||
static void GScr_AddFieldsForEntityStub();
|
||||
|
||||
// Two hooks because it makes our code cleaner (luckily functions were not inlined)
|
||||
static int Scr_SetObjectFieldStub(unsigned int classnum, int entnum, int offset);
|
||||
static void Scr_SetClientFieldStub(Game::gclient_s* client, int offset);
|
||||
|
||||
// One hook because functions were inlined
|
||||
static void Scr_GetEntityFieldStub(int entnum, int offset);
|
||||
|
||||
static void AddEntityFields();
|
||||
static void AddClientFields();
|
||||
};
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#include "Field.hpp"
|
||||
#include "Int64.hpp"
|
||||
#include "IO.hpp"
|
||||
#include "Script.hpp"
|
||||
@ -14,6 +15,7 @@ namespace Components::GSC
|
||||
{
|
||||
GSC::GSC()
|
||||
{
|
||||
Loader::Register(new Field());
|
||||
Loader::Register(new Int64());
|
||||
Loader::Register(new IO());
|
||||
Loader::Register(new Script());
|
||||
|
@ -230,7 +230,7 @@ namespace Components::GSC
|
||||
return Game::Scr_GetNumParam();
|
||||
}
|
||||
|
||||
Game::client_t* Script::GetClient(const Game::gentity_t* ent)
|
||||
Game::client_s* Script::GetClient(const Game::gentity_s* ent)
|
||||
{
|
||||
assert(ent);
|
||||
|
||||
|
@ -14,7 +14,7 @@ namespace Components::GSC
|
||||
static void AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames);
|
||||
static void AddMethMultiple(Game::BuiltinMethod func, bool type, scriptNames);
|
||||
|
||||
static Game::client_t* GetClient(const Game::gentity_t* gentity);
|
||||
static Game::client_s* GetClient(const Game::gentity_s* gentity);
|
||||
// Probably a macro 'originally' but this is fine
|
||||
static Game::gentity_s* Scr_GetPlayerEntity(Game::scr_entref_t entref);
|
||||
|
||||
|
@ -4,111 +4,9 @@
|
||||
|
||||
namespace Components::GSC
|
||||
{
|
||||
std::unordered_map<std::uint16_t, Game::ent_field_t> ScriptExtension::CustomEntityFields;
|
||||
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
|
||||
|
||||
std::unordered_map<const char*, const char*> ScriptExtension::ReplacedFunctions;
|
||||
const char* ScriptExtension::ReplacedPos = nullptr;
|
||||
|
||||
void ScriptExtension::AddEntityField(const char* name, Game::fieldtype_t type,
|
||||
const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter)
|
||||
{
|
||||
static std::uint16_t fieldOffsetStart = 15; // fields count
|
||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||
|
||||
CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
||||
++fieldOffsetStart;
|
||||
}
|
||||
|
||||
void ScriptExtension::AddClientField(const char* name, Game::fieldtype_t type,
|
||||
const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter)
|
||||
{
|
||||
static std::uint16_t fieldOffsetStart = 21; // fields count
|
||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||
|
||||
const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated
|
||||
|
||||
// Use 'index' in 'array' as map key. It will be used later in Scr_SetObjectFieldStub
|
||||
CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter};
|
||||
++fieldOffsetStart;
|
||||
}
|
||||
|
||||
void ScriptExtension::GScr_AddFieldsForEntityStub()
|
||||
{
|
||||
for (const auto& field : CustomEntityFields | std::views::values)
|
||||
{
|
||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
|
||||
|
||||
for (const auto& field : CustomClientFields | std::views::values)
|
||||
{
|
||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||
}
|
||||
}
|
||||
|
||||
// Because some functions are inlined we have to hook this function instead of Scr_SetEntityField
|
||||
int ScriptExtension::Scr_SetObjectFieldStub(unsigned int classnum, int entnum, int offset)
|
||||
{
|
||||
if (classnum == Game::ClassNum::CLASS_NUM_ENTITY)
|
||||
{
|
||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||
if (const auto itr = CustomEntityFields.find(entity_offset); itr != CustomEntityFields.end())
|
||||
{
|
||||
itr->second.setter(&Game::g_entities[entnum], offset);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// No custom generic field was found, let the game handle it
|
||||
return Game::Scr_SetObjectField(classnum, entnum, offset);
|
||||
}
|
||||
|
||||
// Offset was already converted to array 'index' following binop offset & ~Game::ENTFIELD_MASK
|
||||
void ScriptExtension::Scr_SetClientFieldStub(Game::gclient_s* client, int offset)
|
||||
{
|
||||
const auto client_offset = static_cast<std::uint16_t>(offset);
|
||||
if (const auto itr = CustomClientFields.find(client_offset); itr != CustomClientFields.end())
|
||||
{
|
||||
itr->second.setter(client, &itr->second);
|
||||
return;
|
||||
}
|
||||
|
||||
// No custom field client was found, let the game handle it
|
||||
Game::Scr_SetClientField(client, offset);
|
||||
}
|
||||
|
||||
void ScriptExtension::Scr_GetEntityFieldStub(int entnum, int offset)
|
||||
{
|
||||
if ((offset & Game::ENTFIELD_MASK) == Game::ENTFIELD_CLIENT)
|
||||
{
|
||||
// If we have a ENTFIELD_CLIENT offset we need to check g_entity is actually a fully connected client
|
||||
if (Game::g_entities[entnum].client != nullptr)
|
||||
{
|
||||
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
||||
if (const auto itr = CustomClientFields.find(client_offset); itr != CustomClientFields.end())
|
||||
{
|
||||
// Game functions probably don't ever need to use the reference to client_fields_s...
|
||||
itr->second.getter(Game::g_entities[entnum].client, &itr->second);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regular entity offsets can be searched directly in our custom handler
|
||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||
|
||||
if (const auto itr = CustomEntityFields.find(entity_offset); itr != CustomEntityFields.end())
|
||||
{
|
||||
itr->second.getter(&Game::g_entities[entnum], offset);
|
||||
return;
|
||||
}
|
||||
|
||||
// No custom generic field was found, let the game handle it
|
||||
Game::Scr_GetEntityField(entnum, offset);
|
||||
}
|
||||
|
||||
const char* ScriptExtension::GetCodePosForParam(int index)
|
||||
{
|
||||
if (static_cast<unsigned int>(index) >= Game::scrVmPub->outparamcount)
|
||||
@ -281,44 +179,10 @@ namespace Components::GSC
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptExtension::AddEntityFields()
|
||||
{
|
||||
AddEntityField("entityflags", Game::F_INT,
|
||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||
{
|
||||
ent->flags = Game::Scr_GetInt(0);
|
||||
},
|
||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||
{
|
||||
Game::Scr_AddInt(ent->flags);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptExtension::AddClientFields()
|
||||
{
|
||||
AddClientField("clientflags", Game::F_INT,
|
||||
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||
{
|
||||
pSelf->flags = Game::Scr_GetInt(0);
|
||||
},
|
||||
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||
{
|
||||
Game::Scr_AddInt(pSelf->flags);
|
||||
});
|
||||
}
|
||||
|
||||
ScriptExtension::ScriptExtension()
|
||||
{
|
||||
AddFunctions();
|
||||
AddMethods();
|
||||
AddEntityFields();
|
||||
AddClientFields();
|
||||
|
||||
Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
||||
|
||||
Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
||||
Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
||||
Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
||||
|
||||
Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook::Nop(0x61E933, 1);
|
||||
|
@ -7,34 +7,17 @@ namespace Components::GSC
|
||||
public:
|
||||
ScriptExtension();
|
||||
|
||||
static void AddEntityField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackEnt& setter, const Game::ScriptCallbackEnt& getter);
|
||||
static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter);
|
||||
|
||||
static const char* GetCodePosForParam(int index);
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::uint16_t, Game::ent_field_t> CustomEntityFields;
|
||||
static std::unordered_map<std::uint16_t, Game::client_fields_s> CustomClientFields;
|
||||
|
||||
static std::unordered_map<const char*, const char*> ReplacedFunctions;
|
||||
static const char* ReplacedPos;
|
||||
|
||||
static void GScr_AddFieldsForEntityStub();
|
||||
|
||||
// Two hooks because it makes our code cleaner (luckily functions were not inlined)
|
||||
static int Scr_SetObjectFieldStub(unsigned int classnum, int entnum, int offset);
|
||||
static void Scr_SetClientFieldStub(Game::gclient_s* client, int offset);
|
||||
|
||||
// One hook because functions were inlined
|
||||
static void Scr_GetEntityFieldStub(int entnum, int offset);
|
||||
|
||||
static void GetReplacedPos(const char* pos);
|
||||
static void SetReplacedPos(const char* what, const char* with);
|
||||
static void VMExecuteInternalStub();
|
||||
|
||||
static void AddFunctions();
|
||||
static void AddMethods();
|
||||
static void AddEntityFields();
|
||||
static void AddClientFields();
|
||||
};
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ namespace Components
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlayerName::DropClient(Game::client_t* drop)
|
||||
void PlayerName::DropClient(Game::client_s* drop)
|
||||
{
|
||||
const auto* reason = "Invalid name detected";
|
||||
Network::SendCommand(drop->header.netchan.remoteAddress, "error", reason);
|
||||
|
@ -19,7 +19,7 @@ namespace Components
|
||||
|
||||
static bool IsBadChar(int c);
|
||||
static bool CopyClientNameCheck(char* dest, const char* source, int size);
|
||||
static void DropClient(Game::client_t* drop);
|
||||
static void DropClient(Game::client_s* drop);
|
||||
static void SV_UserinfoChangedStub();
|
||||
};
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ namespace Components
|
||||
return size;
|
||||
}
|
||||
|
||||
int Security::SV_CanReplaceServerCommand_Hk([[maybe_unused]] Game::client_t* client, [[maybe_unused]] const char* cmd)
|
||||
int Security::SV_CanReplaceServerCommand_Hk([[maybe_unused]] Game::client_s* client, [[maybe_unused]] const char* cmd)
|
||||
{
|
||||
// This is a fix copied from V2. As I don't have time to investigate, let's simply trust them
|
||||
return -1;
|
||||
@ -121,7 +121,7 @@ namespace Components
|
||||
InterlockedIncrement(&Game::deferredQueue->send);
|
||||
}
|
||||
|
||||
void Security::SV_ExecuteClientMessage_Stub(Game::client_t* client, Game::msg_t* msg)
|
||||
void Security::SV_ExecuteClientMessage_Stub(Game::client_s* client, Game::msg_t* msg)
|
||||
{
|
||||
if ((client->reliableSequence - client->reliableAcknowledge) < 0)
|
||||
{
|
||||
@ -132,7 +132,7 @@ namespace Components
|
||||
return;
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void(Game::client_t*, Game::msg_t*)>(0x414D40)(client, msg);
|
||||
Utils::Hook::Call<void(Game::client_s*, Game::msg_t*)>(0x414D40)(client, msg);
|
||||
}
|
||||
|
||||
Security::Security()
|
||||
|
@ -11,7 +11,7 @@ namespace Components
|
||||
static int Msg_ReadBitsCompressCheckCL(const unsigned char* from, unsigned char* to, int size);
|
||||
|
||||
private:
|
||||
static int SV_CanReplaceServerCommand_Hk(Game::client_t* client, const char* cmd);
|
||||
static int SV_CanReplaceServerCommand_Hk(Game::client_s* client, const char* cmd);
|
||||
|
||||
static long AtolAdjustPlayerLimit(const char* string);
|
||||
|
||||
@ -23,6 +23,6 @@ namespace Components
|
||||
|
||||
static void NET_DeferPacketToClient_Hk(Game::netadr_t* net_from, Game::msg_t* net_message);
|
||||
|
||||
static void SV_ExecuteClientMessage_Stub(Game::client_t* client, Game::msg_t* msg);
|
||||
static void SV_ExecuteClientMessage_Stub(Game::client_s* client, Game::msg_t* msg);
|
||||
};
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace Components
|
||||
assert(!msg->overflowed);
|
||||
}
|
||||
|
||||
void Voice::SV_SendClientVoiceData(Game::client_t* client)
|
||||
void Voice::SV_SendClientVoiceData(Game::client_s* client)
|
||||
{
|
||||
Game::msg_t msg{};
|
||||
const auto clientNum = client - Game::svs_clients;
|
||||
@ -68,10 +68,10 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Voice::SV_SendClientMessages_Stub(Game::client_t* client, Game::msg_t* msg, unsigned char* snapshotMsgBuf)
|
||||
void Voice::SV_SendClientMessages_Stub(Game::client_s* client, Game::msg_t* msg, unsigned char* snapshotMsgBuf)
|
||||
{
|
||||
// SV_EndClientSnapshot
|
||||
Utils::Hook::Call<void(Game::client_t*, Game::msg_t*, unsigned char*)>(0x4F5300)(client, msg, snapshotMsgBuf);
|
||||
Utils::Hook::Call<void(Game::client_s*, Game::msg_t*, unsigned char*)>(0x4F5300)(client, msg, snapshotMsgBuf);
|
||||
|
||||
SV_SendClientVoiceData(client);
|
||||
}
|
||||
@ -148,7 +148,7 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Voice::SV_UserVoice(Game::client_t* cl, Game::msg_t* msg)
|
||||
void Voice::SV_UserVoice(Game::client_s* cl, Game::msg_t* msg)
|
||||
{
|
||||
Game::VoicePacket_t voicePacket{};
|
||||
|
||||
@ -179,7 +179,7 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Voice::SV_PreGameUserVoice(Game::client_t* cl, Game::msg_t* msg)
|
||||
void Voice::SV_PreGameUserVoice(Game::client_s* cl, Game::msg_t* msg)
|
||||
{
|
||||
Game::VoicePacket_t voicePacket{};
|
||||
|
||||
@ -390,7 +390,7 @@ namespace Components
|
||||
|
||||
Events::OnSteamDisconnect(CL_ClearMutedList);
|
||||
Events::OnClientDisconnect(SV_UnmuteClient);
|
||||
Events::OnClientConnect([](Game::client_t* cl) -> void
|
||||
Events::OnClientConnect([](Game::client_s* cl) -> void
|
||||
{
|
||||
if (Chat::IsMuted(cl))
|
||||
{
|
||||
|
@ -26,16 +26,16 @@ namespace Components
|
||||
static const Game::dvar_t* sv_voice;
|
||||
|
||||
static void SV_WriteVoiceDataToClient(int clientNum, Game::msg_t* msg);
|
||||
static void SV_SendClientVoiceData(Game::client_t* client);
|
||||
static void SV_SendClientMessages_Stub(Game::client_t* client, Game::msg_t* msg, unsigned char* snapshotMsgBuf);
|
||||
static void SV_SendClientVoiceData(Game::client_s* client);
|
||||
static void SV_SendClientMessages_Stub(Game::client_s* client, Game::msg_t* msg, unsigned char* snapshotMsgBuf);
|
||||
|
||||
static bool SV_ServerHasClientMuted(int talker);
|
||||
|
||||
static bool OnSameTeam(const Game::gentity_s* ent1, const Game::gentity_s* ent2);
|
||||
static void SV_QueueVoicePacket(int talkerNum, int clientNum, const Game::VoicePacket_t* voicePacket);
|
||||
static void G_BroadcastVoice(Game::gentity_s* talker, const Game::VoicePacket_t* voicePacket);
|
||||
static void SV_UserVoice(Game::client_t* cl, Game::msg_t* msg);
|
||||
static void SV_PreGameUserVoice(Game::client_t* cl, Game::msg_t* msg);
|
||||
static void SV_UserVoice(Game::client_s* cl, Game::msg_t* msg);
|
||||
static void SV_PreGameUserVoice(Game::client_s* cl, Game::msg_t* msg);
|
||||
static void SV_VoicePacket(Game::netadr_t from, Game::msg_t* msg);
|
||||
|
||||
static void CL_ClearMutedList();
|
||||
|
@ -419,7 +419,7 @@ namespace Game
|
||||
typedef void(*Steam_JoinLobby_t)(SteamID, char);
|
||||
extern Steam_JoinLobby_t Steam_JoinLobby;
|
||||
|
||||
typedef void(*TeleportPlayer_t)(gentity_t* entity, float* pos, float* orientation);
|
||||
typedef void(*TeleportPlayer_t)(gentity_s* entity, float* pos, float* orientation);
|
||||
extern TeleportPlayer_t TeleportPlayer;
|
||||
|
||||
typedef void(*UI_AddMenuList_t)(UiContext* dc, MenuList* menuList, int close);
|
||||
|
@ -16,7 +16,7 @@ namespace Game
|
||||
|
||||
G_DebugLineWithDuration_t G_DebugLineWithDuration = G_DebugLineWithDuration_t(0x4C3280);
|
||||
|
||||
gentity_t* g_entities = reinterpret_cast<gentity_t*>(0x18835D8);
|
||||
gentity_s* g_entities = reinterpret_cast<gentity_s*>(0x18835D8);
|
||||
|
||||
const char* origErrorMsg = reinterpret_cast<const char*>(0x79B124);
|
||||
|
||||
|
@ -51,7 +51,7 @@ namespace Game
|
||||
|
||||
constexpr std::size_t MAX_GENTITIES = 2048;
|
||||
constexpr std::size_t ENTITYNUM_NONE = MAX_GENTITIES - 1;
|
||||
extern gentity_t* g_entities;
|
||||
extern gentity_s* g_entities;
|
||||
|
||||
extern const char* origErrorMsg;
|
||||
|
||||
|
@ -125,7 +125,7 @@ namespace Game
|
||||
typedef void(*Scr_FreeThread_t)(unsigned __int16 handle);
|
||||
extern Scr_FreeThread_t Scr_FreeThread;
|
||||
|
||||
typedef void(*Scr_Notify_t)(gentity_t* ent, unsigned __int16 stringValue, unsigned int paramcount);
|
||||
typedef void(*Scr_Notify_t)(gentity_s* ent, unsigned __int16 stringValue, unsigned int paramcount);
|
||||
extern Scr_Notify_t Scr_Notify;
|
||||
|
||||
typedef void(*Scr_NotifyLevel_t)(unsigned __int16 stringValue, unsigned int paramcount);
|
||||
|
@ -27,7 +27,7 @@ namespace Game
|
||||
int* sv_timeResidual = reinterpret_cast<int*>(0x2089E14);
|
||||
int* sv_serverId_value = reinterpret_cast<int*>(0x2089DC0);
|
||||
int* svs_clientCount = reinterpret_cast<int*>(0x31D938C);
|
||||
client_t* svs_clients = reinterpret_cast<client_t*>(0x31D9390);
|
||||
client_s* svs_clients = reinterpret_cast<client_s*>(0x31D9390);
|
||||
|
||||
unsigned short* sv_sconfigstrings = reinterpret_cast<unsigned short*>(0x208A632);
|
||||
unsigned short* sv_emptyConfigString = reinterpret_cast<unsigned short*>(0x208A630);
|
||||
@ -115,7 +115,7 @@ namespace Game
|
||||
SV_SendServerCommand(&svs_clients[clientNum], SV_CMD_RELIABLE, "%c %i %i", 'M', index, value);
|
||||
}
|
||||
|
||||
void SV_BotUserMove(client_t* client)
|
||||
void SV_BotUserMove(client_s* client)
|
||||
{
|
||||
static DWORD SV_BotUserMove_t = 0x626E50;
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Game
|
||||
{
|
||||
typedef gentity_t*(*SV_AddTestClient_t)();
|
||||
typedef gentity_s*(*SV_AddTestClient_t)();
|
||||
extern SV_AddTestClient_t SV_AddTestClient;
|
||||
|
||||
typedef int(*SV_IsTestClient_t)(int clientNum);
|
||||
@ -14,7 +14,7 @@ namespace Game
|
||||
typedef void(*SV_GameSendServerCommand_t)(int clientNum, svscmd_type type, const char* text);
|
||||
extern SV_GameSendServerCommand_t SV_GameSendServerCommand;
|
||||
|
||||
typedef void(*SV_SendServerCommand_t)(client_t* cl, svscmd_type type, const char* fmt, ...);
|
||||
typedef void(*SV_SendServerCommand_t)(client_s* cl, svscmd_type type, const char* fmt, ...);
|
||||
extern SV_SendServerCommand_t SV_SendServerCommand;
|
||||
|
||||
typedef void(*SV_Cmd_TokenizeString_t)(const char* string);
|
||||
@ -38,19 +38,19 @@ namespace Game
|
||||
typedef bool(*SV_Loaded_t)();
|
||||
extern SV_Loaded_t SV_Loaded;
|
||||
|
||||
typedef void(*SV_ClientThink_t)(client_t* cl, usercmd_s* cmd);
|
||||
typedef void(*SV_ClientThink_t)(client_s* cl, usercmd_s* cmd);
|
||||
extern SV_ClientThink_t SV_ClientThink;
|
||||
|
||||
typedef void(*SV_DropClient_t)(client_t* drop, const char* reason, bool tellThem);
|
||||
typedef void(*SV_DropClient_t)(client_s* drop, const char* reason, bool tellThem);
|
||||
extern SV_DropClient_t SV_DropClient;
|
||||
|
||||
typedef client_t*(*SV_GetPlayerByName_t)();
|
||||
typedef client_s*(*SV_GetPlayerByName_t)();
|
||||
extern SV_GetPlayerByName_t SV_GetPlayerByName;
|
||||
|
||||
typedef client_t*(*SV_GetPlayerByNum_t)();
|
||||
typedef client_s*(*SV_GetPlayerByNum_t)();
|
||||
extern SV_GetPlayerByNum_t SV_GetPlayerByNum;
|
||||
|
||||
typedef client_t*(*SV_FindClientByAddress_t)(netadr_t from, int qport, int remoteClientIndex);
|
||||
typedef client_s*(*SV_FindClientByAddress_t)(netadr_t from, int qport, int remoteClientIndex);
|
||||
extern SV_FindClientByAddress_t SV_FindClientByAddress;
|
||||
|
||||
typedef void(*SV_WaitServer_t)();
|
||||
@ -68,7 +68,7 @@ namespace Game
|
||||
extern int* sv_timeResidual;
|
||||
extern int* sv_serverId_value;
|
||||
extern int* svs_clientCount;
|
||||
extern client_t* svs_clients;
|
||||
extern client_s* svs_clients;
|
||||
|
||||
extern unsigned short* sv_sconfigstrings;
|
||||
extern unsigned short* sv_emptyConfigString;
|
||||
@ -80,5 +80,5 @@ namespace Game
|
||||
extern void SV_DropAllBots();
|
||||
extern int SV_GetClientStat(int clientNum, int index);
|
||||
extern void SV_SetClientStat(int clientNum, int index, int value);
|
||||
extern void SV_BotUserMove(client_t* client);
|
||||
extern void SV_BotUserMove(client_s* client);
|
||||
}
|
||||
|
@ -7226,7 +7226,7 @@ namespace Game
|
||||
float spectateDefaultAngles[3];
|
||||
};
|
||||
|
||||
typedef struct gclient_s
|
||||
struct gclient_s
|
||||
{
|
||||
playerState_s ps;
|
||||
clientSession_t sess;
|
||||
@ -7247,9 +7247,9 @@ namespace Game
|
||||
unsigned __int16 attachShieldTagName;
|
||||
hintType_t hintForcedType;
|
||||
int hintForcedString;
|
||||
} gclient_t;
|
||||
};
|
||||
|
||||
static_assert(sizeof(gclient_t) == 13932);
|
||||
static_assert(sizeof(gclient_s) == 0x366C);
|
||||
|
||||
struct EntHandle
|
||||
{
|
||||
@ -7300,11 +7300,11 @@ namespace Game
|
||||
ENT_HANDLER_COUNT
|
||||
};
|
||||
|
||||
typedef struct gentity_s
|
||||
struct gentity_s
|
||||
{
|
||||
entityState_s s;
|
||||
entityShared_t r;
|
||||
gclient_t* client; // 344
|
||||
gclient_s* client; // 344
|
||||
void /*Turret*/* turret;
|
||||
void /*Vehicle*/* vehicle;
|
||||
int physObjId;
|
||||
@ -7342,7 +7342,7 @@ namespace Game
|
||||
gentity_s* nextFree;
|
||||
int birthTime;
|
||||
char pad[100];
|
||||
} gentity_t;
|
||||
};
|
||||
|
||||
static_assert(sizeof(gentity_s) == 0x274);
|
||||
|
||||
@ -7439,7 +7439,7 @@ namespace Game
|
||||
int baselineSnap;
|
||||
};
|
||||
|
||||
struct client_t
|
||||
struct client_s
|
||||
{
|
||||
clientHeader_t header;
|
||||
const char* dropReason; // 1624
|
||||
@ -7454,7 +7454,7 @@ namespace Game
|
||||
usercmd_s lastUsercmd; // 134772
|
||||
int lastClientCommand; // 134812
|
||||
char lastClientCommandString[1024]; // 134816
|
||||
gentity_t* gentity; // 135840
|
||||
gentity_s* gentity; // 135840
|
||||
char name[16]; // 135844
|
||||
int nextReliableTime; // 135860
|
||||
int lastPacketTime; // 135864
|
||||
@ -7493,7 +7493,7 @@ namespace Game
|
||||
clientSnapshot_t frames[32];
|
||||
};
|
||||
|
||||
static_assert(sizeof(client_t) == 0xA6790);
|
||||
static_assert(sizeof(client_s) == 0xA6790);
|
||||
|
||||
enum CompassType
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user