Merge pull request #1004 from XLabsProject/develop

Release r4251
This commit is contained in:
Edo 2023-05-03 18:12:56 +01:00 committed by GitHub
commit 6d117a96a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 300 additions and 222 deletions

View File

@ -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/). 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 ## r4246 - 2023-05-03
### Added ### 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 ### Fixed
@ -336,6 +352,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
### Changed ### Changed
- `sv_mapRotationCurrent` is not being used anymore (#302) - `sv_mapRotationCurrent` is not being used anymore (#302)
- `sv_mapRotation` is parsed once on startup (#283)
### Security ### Security

View File

@ -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; SteamID guid;
guid.bits = cl->steamID; 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); Game::SV_DropClient(cl, reason.data(), true);
} }

View File

@ -9,7 +9,7 @@ namespace Components
Bans(); 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(SteamID id);
static void UnbanClient(Game::netIP_t ip); static void UnbanClient(Game::netIP_t ip);

View File

@ -274,7 +274,7 @@ namespace Components
}); });
} }
void Bots::BotAiAction(Game::client_t* cl) void Bots::BotAiAction(Game::client_s* cl)
{ {
if (!cl->gentity) if (!cl->gentity)
{ {
@ -409,9 +409,9 @@ namespace Components
Bots::Bots() Bots::Bots()
{ {
AssertOffset(Game::client_t, bIsTestClient, 0x41AF0); AssertOffset(Game::client_s, bIsTestClient, 0x41AF0);
AssertOffset(Game::client_t, ping, 0x212C8); AssertOffset(Game::client_s, ping, 0x212C8);
AssertOffset(Game::client_t, gentity, 0x212A0); AssertOffset(Game::client_s, gentity, 0x212A0);
// Replace connect string // 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\""); 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\"");

View File

@ -25,7 +25,7 @@ namespace Components
static void GScr_isTestClient(Game::scr_entref_t entref); static void GScr_isTestClient(Game::scr_entref_t entref);
static void AddScriptMethods(); static void AddScriptMethods();
static void BotAiAction(Game::client_t* cl); static void BotAiAction(Game::client_s* cl);
static void SV_BotUserMove_Hk(); static void SV_BotUserMove_Hk();
static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex); static void G_SelectWeaponIndex(int clientNum, int iWeaponIndex);

View File

@ -29,7 +29,7 @@ namespace Components
return lock; 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; SendChat = true;
@ -274,7 +274,7 @@ namespace Components
return result; 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 clientNum = cl - Game::svs_clients;
const auto xuid = Game::svs_clients[clientNum].steamID; const auto xuid = Game::svs_clients[clientNum].steamID;
@ -287,7 +287,7 @@ namespace Components
return result; return result;
} }
void Chat::MuteClient(const Game::client_t* client) void Chat::MuteClient(const Game::client_s* client)
{ {
const auto xuid = client->steamID; const auto xuid = client->steamID;
MutedList.access([&](muteList& clients) 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)); 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); UnmuteInternal(client->steamID);
@ -606,7 +606,7 @@ namespace Components
Chat::Chat() 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"); 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"); sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients");

View File

@ -9,7 +9,7 @@ namespace Components
Chat(); Chat();
static bool IsMuted(const Game::gentity_s* ent); 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: private:
static Dvar::Var cg_chatWidth; static Dvar::Var cg_chatWidth;
@ -27,7 +27,7 @@ namespace Components
static std::unique_lock<Utils::NamedMutex> Lock(); 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 PreSayStub();
static void PostSayStub(); static void PostSayStub();
@ -36,8 +36,8 @@ namespace Components
static void CG_AddToTeamChat(const char* text); static void CG_AddToTeamChat(const char* text);
static void CG_AddToTeamChat_Stub(); static void CG_AddToTeamChat_Stub();
static void MuteClient(const Game::client_t* client); static void MuteClient(const Game::client_s* client);
static void UnmuteClient(const Game::client_t* client); static void UnmuteClient(const Game::client_s* client);
static void UnmuteInternal(std::uint64_t id, bool everyone = false); static void UnmuteInternal(std::uint64_t id, bool everyone = false);
static void SaveMutedList(const muteList& list); static void SaveMutedList(const muteList& list);
static void LoadMutedList(); static void LoadMutedList();

View File

@ -298,8 +298,7 @@ namespace Components
const auto* name = params->get(1); const auto* name = params->get(1);
ent->client->visionDuration[visMode] = duration; ent->client->visionDuration[visMode] = duration;
strncpy_s(ent->client->visionName[visMode], strncpy_s(ent->client->visionName[visMode], sizeof(Game::gclient_s::visionName[0]) / sizeof(char), name, _TRUNCATE);
sizeof(Game::gclient_t::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)); 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); const auto* name = params->get(1);
ent->client->visionDuration[visMode] = duration; ent->client->visionDuration[visMode] = duration;
strncpy_s(ent->client->visionName[visMode], strncpy_s(ent->client->visionName[visMode], sizeof(Game::gclient_s::visionName[0]) / sizeof(char), name, _TRUNCATE);
sizeof(Game::gclient_t::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)); Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
}); });

View File

@ -56,11 +56,11 @@ namespace Components
Utils::Hook::Call<void(int)>(0x4AA430)(clientNum); // ClientDisconnect 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); 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() void Events::SteamDisconnect_Hk()

View File

@ -6,7 +6,7 @@ namespace Components
{ {
public: public:
typedef void(ClientCallback)(int clientNum); typedef void(ClientCallback)(int clientNum);
typedef void(ClientConnectCallback)(Game::client_t* cl); typedef void(ClientConnectCallback)(Game::client_s* cl);
typedef void(Callback)(); typedef void(Callback)();
Events(); Events();
@ -40,7 +40,7 @@ namespace Components
static Utils::Signal<Callback> DvarInitSignal; static Utils::Signal<Callback> DvarInitSignal;
static void ClientDisconnect_Hk(int clientNum); 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 SteamDisconnect_Hk();
static void Scr_ShutdownSystem_Hk(unsigned char sys); static void Scr_ShutdownSystem_Hk(unsigned char sys);
static void CL_InitOnceForAllClients_HK(); static void CL_InitOnceForAllClients_HK();

View 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
}
}

View 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();
};
}

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "Field.hpp"
#include "Int64.hpp" #include "Int64.hpp"
#include "IO.hpp" #include "IO.hpp"
#include "Script.hpp" #include "Script.hpp"
@ -14,6 +15,7 @@ namespace Components::GSC
{ {
GSC::GSC() GSC::GSC()
{ {
Loader::Register(new Field());
Loader::Register(new Int64()); Loader::Register(new Int64());
Loader::Register(new IO()); Loader::Register(new IO());
Loader::Register(new Script()); Loader::Register(new Script());

View File

@ -230,7 +230,7 @@ namespace Components::GSC
return Game::Scr_GetNumParam(); 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); assert(ent);

View File

@ -14,7 +14,7 @@ namespace Components::GSC
static void AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames); static void AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames);
static void AddMethMultiple(Game::BuiltinMethod 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 // Probably a macro 'originally' but this is fine
static Game::gentity_s* Scr_GetPlayerEntity(Game::scr_entref_t entref); static Game::gentity_s* Scr_GetPlayerEntity(Game::scr_entref_t entref);

View File

@ -4,111 +4,9 @@
namespace Components::GSC 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; std::unordered_map<const char*, const char*> ScriptExtension::ReplacedFunctions;
const char* ScriptExtension::ReplacedPos = nullptr; 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) const char* ScriptExtension::GetCodePosForParam(int index)
{ {
if (static_cast<unsigned int>(index) >= Game::scrVmPub->outparamcount) 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() ScriptExtension::ScriptExtension()
{ {
AddFunctions(); AddFunctions();
AddMethods(); 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(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick();
Utils::Hook::Nop(0x61E933, 1); Utils::Hook::Nop(0x61E933, 1);

View File

@ -7,34 +7,17 @@ namespace Components::GSC
public: public:
ScriptExtension(); 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); static const char* GetCodePosForParam(int index);
private: 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 std::unordered_map<const char*, const char*> ReplacedFunctions;
static const char* ReplacedPos; 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 GetReplacedPos(const char* pos);
static void SetReplacedPos(const char* what, const char* with); static void SetReplacedPos(const char* what, const char* with);
static void VMExecuteInternalStub(); static void VMExecuteInternalStub();
static void AddFunctions(); static void AddFunctions();
static void AddMethods(); static void AddMethods();
static void AddEntityFields();
static void AddClientFields();
}; };
} }

View File

@ -104,7 +104,7 @@ namespace Components
return true; return true;
} }
void PlayerName::DropClient(Game::client_t* drop) void PlayerName::DropClient(Game::client_s* drop)
{ {
const auto* reason = "Invalid name detected"; const auto* reason = "Invalid name detected";
Network::SendCommand(drop->header.netchan.remoteAddress, "error", reason); Network::SendCommand(drop->header.netchan.remoteAddress, "error", reason);

View File

@ -19,7 +19,7 @@ namespace Components
static bool IsBadChar(int c); static bool IsBadChar(int c);
static bool CopyClientNameCheck(char* dest, const char* source, int size); 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(); static void SV_UserinfoChangedStub();
}; };
} }

View File

@ -29,7 +29,7 @@ namespace Components
return size; 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 // This is a fix copied from V2. As I don't have time to investigate, let's simply trust them
return -1; return -1;
@ -121,7 +121,7 @@ namespace Components
InterlockedIncrement(&Game::deferredQueue->send); 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) if ((client->reliableSequence - client->reliableAcknowledge) < 0)
{ {
@ -132,7 +132,7 @@ namespace Components
return; 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() Security::Security()

View File

@ -11,7 +11,7 @@ namespace Components
static int Msg_ReadBitsCompressCheckCL(const unsigned char* from, unsigned char* to, int size); static int Msg_ReadBitsCompressCheckCL(const unsigned char* from, unsigned char* to, int size);
private: 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); 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 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);
}; };
} }

View File

@ -36,7 +36,7 @@ namespace Components
assert(!msg->overflowed); assert(!msg->overflowed);
} }
void Voice::SV_SendClientVoiceData(Game::client_t* client) void Voice::SV_SendClientVoiceData(Game::client_s* client)
{ {
Game::msg_t msg{}; Game::msg_t msg{};
const auto clientNum = client - Game::svs_clients; 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 // 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); 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{}; 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{}; Game::VoicePacket_t voicePacket{};
@ -390,7 +390,7 @@ namespace Components
Events::OnSteamDisconnect(CL_ClearMutedList); Events::OnSteamDisconnect(CL_ClearMutedList);
Events::OnClientDisconnect(SV_UnmuteClient); Events::OnClientDisconnect(SV_UnmuteClient);
Events::OnClientConnect([](Game::client_t* cl) -> void Events::OnClientConnect([](Game::client_s* cl) -> void
{ {
if (Chat::IsMuted(cl)) if (Chat::IsMuted(cl))
{ {

View File

@ -26,16 +26,16 @@ namespace Components
static const Game::dvar_t* sv_voice; static const Game::dvar_t* sv_voice;
static void SV_WriteVoiceDataToClient(int clientNum, Game::msg_t* msg); static void SV_WriteVoiceDataToClient(int clientNum, Game::msg_t* msg);
static void SV_SendClientVoiceData(Game::client_t* client); static void SV_SendClientVoiceData(Game::client_s* client);
static void SV_SendClientMessages_Stub(Game::client_t* client, Game::msg_t* msg, unsigned char* snapshotMsgBuf); static void SV_SendClientMessages_Stub(Game::client_s* client, Game::msg_t* msg, unsigned char* snapshotMsgBuf);
static bool SV_ServerHasClientMuted(int talker); static bool SV_ServerHasClientMuted(int talker);
static bool OnSameTeam(const Game::gentity_s* ent1, const Game::gentity_s* ent2); 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 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 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_UserVoice(Game::client_s* cl, Game::msg_t* msg);
static void SV_PreGameUserVoice(Game::client_t* 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 SV_VoicePacket(Game::netadr_t from, Game::msg_t* msg);
static void CL_ClearMutedList(); static void CL_ClearMutedList();

View File

@ -419,7 +419,7 @@ namespace Game
typedef void(*Steam_JoinLobby_t)(SteamID, char); typedef void(*Steam_JoinLobby_t)(SteamID, char);
extern Steam_JoinLobby_t Steam_JoinLobby; 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; extern TeleportPlayer_t TeleportPlayer;
typedef void(*UI_AddMenuList_t)(UiContext* dc, MenuList* menuList, int close); typedef void(*UI_AddMenuList_t)(UiContext* dc, MenuList* menuList, int close);

View File

@ -16,7 +16,7 @@ namespace Game
G_DebugLineWithDuration_t G_DebugLineWithDuration = G_DebugLineWithDuration_t(0x4C3280); 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); const char* origErrorMsg = reinterpret_cast<const char*>(0x79B124);

View File

@ -51,7 +51,7 @@ namespace Game
constexpr std::size_t MAX_GENTITIES = 2048; constexpr std::size_t MAX_GENTITIES = 2048;
constexpr std::size_t ENTITYNUM_NONE = MAX_GENTITIES - 1; constexpr std::size_t ENTITYNUM_NONE = MAX_GENTITIES - 1;
extern gentity_t* g_entities; extern gentity_s* g_entities;
extern const char* origErrorMsg; extern const char* origErrorMsg;

View File

@ -125,7 +125,7 @@ namespace Game
typedef void(*Scr_FreeThread_t)(unsigned __int16 handle); typedef void(*Scr_FreeThread_t)(unsigned __int16 handle);
extern Scr_FreeThread_t Scr_FreeThread; 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; extern Scr_Notify_t Scr_Notify;
typedef void(*Scr_NotifyLevel_t)(unsigned __int16 stringValue, unsigned int paramcount); typedef void(*Scr_NotifyLevel_t)(unsigned __int16 stringValue, unsigned int paramcount);

View File

@ -27,7 +27,7 @@ namespace Game
int* sv_timeResidual = reinterpret_cast<int*>(0x2089E14); int* sv_timeResidual = reinterpret_cast<int*>(0x2089E14);
int* sv_serverId_value = reinterpret_cast<int*>(0x2089DC0); int* sv_serverId_value = reinterpret_cast<int*>(0x2089DC0);
int* svs_clientCount = reinterpret_cast<int*>(0x31D938C); 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_sconfigstrings = reinterpret_cast<unsigned short*>(0x208A632);
unsigned short* sv_emptyConfigString = reinterpret_cast<unsigned short*>(0x208A630); 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); 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; static DWORD SV_BotUserMove_t = 0x626E50;

View File

@ -2,7 +2,7 @@
namespace Game namespace Game
{ {
typedef gentity_t*(*SV_AddTestClient_t)(); typedef gentity_s*(*SV_AddTestClient_t)();
extern SV_AddTestClient_t SV_AddTestClient; extern SV_AddTestClient_t SV_AddTestClient;
typedef int(*SV_IsTestClient_t)(int clientNum); 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); typedef void(*SV_GameSendServerCommand_t)(int clientNum, svscmd_type type, const char* text);
extern SV_GameSendServerCommand_t SV_GameSendServerCommand; 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; extern SV_SendServerCommand_t SV_SendServerCommand;
typedef void(*SV_Cmd_TokenizeString_t)(const char* string); typedef void(*SV_Cmd_TokenizeString_t)(const char* string);
@ -38,19 +38,19 @@ namespace Game
typedef bool(*SV_Loaded_t)(); typedef bool(*SV_Loaded_t)();
extern SV_Loaded_t SV_Loaded; 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; 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; extern SV_DropClient_t SV_DropClient;
typedef client_t*(*SV_GetPlayerByName_t)(); typedef client_s*(*SV_GetPlayerByName_t)();
extern SV_GetPlayerByName_t SV_GetPlayerByName; extern SV_GetPlayerByName_t SV_GetPlayerByName;
typedef client_t*(*SV_GetPlayerByNum_t)(); typedef client_s*(*SV_GetPlayerByNum_t)();
extern SV_GetPlayerByNum_t SV_GetPlayerByNum; 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; extern SV_FindClientByAddress_t SV_FindClientByAddress;
typedef void(*SV_WaitServer_t)(); typedef void(*SV_WaitServer_t)();
@ -68,7 +68,7 @@ namespace Game
extern int* sv_timeResidual; extern int* sv_timeResidual;
extern int* sv_serverId_value; extern int* sv_serverId_value;
extern int* svs_clientCount; extern int* svs_clientCount;
extern client_t* svs_clients; extern client_s* svs_clients;
extern unsigned short* sv_sconfigstrings; extern unsigned short* sv_sconfigstrings;
extern unsigned short* sv_emptyConfigString; extern unsigned short* sv_emptyConfigString;
@ -80,5 +80,5 @@ namespace Game
extern void SV_DropAllBots(); extern void SV_DropAllBots();
extern int SV_GetClientStat(int clientNum, int index); extern int SV_GetClientStat(int clientNum, int index);
extern void SV_SetClientStat(int clientNum, int index, int value); extern void SV_SetClientStat(int clientNum, int index, int value);
extern void SV_BotUserMove(client_t* client); extern void SV_BotUserMove(client_s* client);
} }

View File

@ -7226,7 +7226,7 @@ namespace Game
float spectateDefaultAngles[3]; float spectateDefaultAngles[3];
}; };
typedef struct gclient_s struct gclient_s
{ {
playerState_s ps; playerState_s ps;
clientSession_t sess; clientSession_t sess;
@ -7247,9 +7247,9 @@ namespace Game
unsigned __int16 attachShieldTagName; unsigned __int16 attachShieldTagName;
hintType_t hintForcedType; hintType_t hintForcedType;
int hintForcedString; int hintForcedString;
} gclient_t; };
static_assert(sizeof(gclient_t) == 13932); static_assert(sizeof(gclient_s) == 0x366C);
struct EntHandle struct EntHandle
{ {
@ -7300,11 +7300,11 @@ namespace Game
ENT_HANDLER_COUNT ENT_HANDLER_COUNT
}; };
typedef struct gentity_s struct gentity_s
{ {
entityState_s s; entityState_s s;
entityShared_t r; entityShared_t r;
gclient_t* client; // 344 gclient_s* client; // 344
void /*Turret*/* turret; void /*Turret*/* turret;
void /*Vehicle*/* vehicle; void /*Vehicle*/* vehicle;
int physObjId; int physObjId;
@ -7342,7 +7342,7 @@ namespace Game
gentity_s* nextFree; gentity_s* nextFree;
int birthTime; int birthTime;
char pad[100]; char pad[100];
} gentity_t; };
static_assert(sizeof(gentity_s) == 0x274); static_assert(sizeof(gentity_s) == 0x274);
@ -7439,7 +7439,7 @@ namespace Game
int baselineSnap; int baselineSnap;
}; };
struct client_t struct client_s
{ {
clientHeader_t header; clientHeader_t header;
const char* dropReason; // 1624 const char* dropReason; // 1624
@ -7454,7 +7454,7 @@ namespace Game
usercmd_s lastUsercmd; // 134772 usercmd_s lastUsercmd; // 134772
int lastClientCommand; // 134812 int lastClientCommand; // 134812
char lastClientCommandString[1024]; // 134816 char lastClientCommandString[1024]; // 134816
gentity_t* gentity; // 135840 gentity_s* gentity; // 135840
char name[16]; // 135844 char name[16]; // 135844
int nextReliableTime; // 135860 int nextReliableTime; // 135860
int lastPacketTime; // 135864 int lastPacketTime; // 135864
@ -7493,7 +7493,7 @@ namespace Game
clientSnapshot_t frames[32]; clientSnapshot_t frames[32];
}; };
static_assert(sizeof(client_t) == 0xA6790); static_assert(sizeof(client_s) == 0xA6790);
enum CompassType enum CompassType
{ {