Expose entity and client flags to GSC

This commit is contained in:
FutureRave 2022-04-28 13:03:22 +01:00
parent 1f5253ad2a
commit ab06edf44d
No known key found for this signature in database
GPG Key ID: E883E2BC9657D955
5 changed files with 241 additions and 1 deletions

View File

@ -4,6 +4,115 @@ namespace Components
{
const char* ScriptExtension::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
std::unordered_map<std::uint16_t, Game::ent_field_t> ScriptExtension::CustomEntityFields;
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
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);
ScriptExtension::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
ScriptExtension::CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter};
++fieldOffsetStart;
}
void ScriptExtension::GScr_AddFieldsForEntityStub()
{
for (const auto& [offset, field] : ScriptExtension::CustomEntityFields)
{
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
}
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
for (const auto& [offset, field] : ScriptExtension::CustomClientFields)
{
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);
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
if (got != ScriptExtension::CustomEntityFields.end())
{
got->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 & 0xFFFF1FFF
void ScriptExtension::Scr_SetClientFieldStub(Game::gclient_s* client, int offset)
{
const auto client_offset = static_cast<std::uint16_t>(offset);
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
if (got != ScriptExtension::CustomClientFields.end())
{
got->second.setter(client, &got->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 & 0xFFFF1FFF);
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
if (got != ScriptExtension::CustomClientFields.end())
{
// Game functions probably don't ever need to use the reference to client_fields_s...
got->second.getter(Game::g_entities[entnum].client, &got->second);
return;
}
}
}
// Regular entity offsets can be searched directly in our custom handler
const auto entity_offset = static_cast<std::uint16_t>(offset);
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
if (got != ScriptExtension::CustomEntityFields.end())
{
got->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 ScriptExtension::AddFunctions()
{
// File functions
@ -247,11 +356,46 @@ namespace Components
Game::Scr_AddIString(value);
}
void ScriptExtension::AddEntityFields()
{
ScriptExtension::AddEntityField("entityflags", Game::fieldtype_t::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()
{
ScriptExtension::AddClientField("clientflags", Game::fieldtype_t::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::AddFunctions();
ScriptExtension::AddMethods();
ScriptExtension::AddEntityFields();
ScriptExtension::AddClientFields();
// Correct builtin function pointer
Utils::Hook::Set<void(*)()>(0x79A90C, ScriptExtension::Scr_TableLookupIStringByRow);
Utils::Hook(0x4EC721, ScriptExtension::GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
Utils::Hook(0x41BED2, ScriptExtension::Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
Utils::Hook(0x5FBF01, ScriptExtension::Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
Utils::Hook(0x4FF413, ScriptExtension::Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
}
}

View File

@ -7,11 +7,28 @@ namespace Components
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);
private:
static const char* QueryStrings[];
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 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 AddFunctions();
static void AddMethods();
static void AddEntityFields();
static void AddClientFields();
static void Scr_TableLookupIStringByRow();
};
}

View File

@ -296,6 +296,12 @@ namespace Game
Scr_ClearOutParams_t Scr_ClearOutParams = Scr_ClearOutParams_t(0x4386E0);
Scr_GetObjectField_t Scr_GetObjectField = Scr_GetObjectField_t(0x4FF3D0);
Scr_SetObjectField_t Scr_SetObjectField = Scr_SetObjectField_t(0x4F20F0);
Scr_GetEntityField_t Scr_GetEntityField = Scr_GetEntityField_t(0x4E8390);
Scr_SetClientField_t Scr_SetClientField = Scr_SetClientField_t(0x4A6DF0);
Scr_AddClassField_t Scr_AddClassField = Scr_AddClassField_t(0x4C0E70);
GetEntity_t GetEntity = GetEntity_t(0x4BC270);
GetPlayerEntity_t GetPlayerEntity = GetPlayerEntity_t(0x49C4A0);

View File

@ -747,6 +747,21 @@ namespace Game
typedef void(__cdecl * Scr_ParamError_t)(unsigned int paramIndex, const char*);
extern Scr_ParamError_t Scr_ParamError;
typedef void(__cdecl * Scr_GetObjectField_t)(unsigned int classnum, int entnum, int offset);
extern Scr_GetObjectField_t Scr_GetObjectField;
typedef int(__cdecl * Scr_SetObjectField_t)(unsigned int classnum, int entnum, int offset);
extern Scr_SetObjectField_t Scr_SetObjectField;
typedef void(__cdecl * Scr_SetClientField_t)(gclient_s* client, int offset);
extern Scr_SetClientField_t Scr_SetClientField;
typedef void(__cdecl * Scr_GetEntityField_t)(int entnum, int offset);
extern Scr_GetEntityField_t Scr_GetEntityField;
typedef void(__cdecl * Scr_AddClassField_t)(unsigned int classnum, const char* name, unsigned int offset);
extern Scr_AddClassField_t Scr_AddClassField;
typedef gentity_s*(__cdecl * GetPlayerEntity_t)(scr_entref_t entref);
extern GetPlayerEntity_t GetPlayerEntity;

View File

@ -231,6 +231,17 @@ namespace Game
FL_MOVER_SLIDE = 0x8000000
};
enum ClassNum : unsigned int
{
CLASS_NUM_ENTITY = 0x0,
CLASS_NUM_HUDELEM = 0x1,
CLASS_NUM_PATHNODE = 0x2,
CLASS_NUM_VEHICLENODE = 0x3,
CLASS_NUM_VEHTRACK_SEGMENT = 0x4,
CLASS_NUM_FXENTITY = 0x5,
CLASS_NUM_COUNT = 0x6,
};
typedef enum
{
HITLOC_NONE,
@ -5705,6 +5716,53 @@ namespace Game
static_assert(sizeof(gentity_s) == 0x274);
enum $1C4253065710F064DA9E4D59ED6EC544
{
ENTFIELD_ENTITY = 0x0,
ENTFIELD_SENTIENT = 0x2000,
ENTFIELD_ACTOR = 0x4000,
ENTFIELD_CLIENT = 0x6000,
ENTFIELD_VEHICLE = 0x8000,
ENTFIELD_MASK = 0xE000,
};
enum fieldtype_t
{
F_INT = 0x0,
F_SHORT = 0x1,
F_BYTE = 0x2,
F_FLOAT = 0x3,
F_CSTRING = 0x4,
F_STRING = 0x5,
F_VECTOR = 0x6,
F_ENTITY = 0x7,
F_ENTHANDLE = 0x8,
F_ANGLES_YAW = 0x9,
F_OBJECT = 0xA,
F_MODEL = 0xB,
};
struct ent_field_t
{
const char* name;
int ofs;
fieldtype_t type;
void(__cdecl * setter)(gentity_s*, int);
void(__cdecl * getter)(gentity_s*, int);
};
struct client_fields_s
{
const char* name;
int ofs;
fieldtype_t type;
void(__cdecl * setter)(gclient_s*, const client_fields_s*);
void(__cdecl * getter)(gclient_s*, const client_fields_s*);
};
typedef void(__cdecl * ScriptCallbackEnt)(gentity_s*, int);
typedef void(__cdecl * ScriptCallbackClient)(gclient_s*, const client_fields_s*);
struct lockonFireParms
{
bool lockon;
@ -6966,7 +7024,7 @@ namespace Game
SHELLSHOCK_VIEWTYPE_NONE = 0x2,
};
struct shellshock_parms_t
struct shellshock_parms_t
{
struct
{