Merge pull request #284 from Arkshine/feature/new-pdata-natives

Introduce new natives to read/write on entity's private data based off class/member name using gamedata files
This commit is contained in:
Vincent Herbet 2015-09-19 20:56:31 +02:00
commit 9a4731dd14
10 changed files with 1161 additions and 6 deletions

View File

@ -13,6 +13,7 @@ binary.sources = [
'engfunc.cpp', 'engfunc.cpp',
'fakemeta_amxx.cpp', 'fakemeta_amxx.cpp',
'pdata.cpp', 'pdata.cpp',
'pdata_gc.cpp',
'forward.cpp', 'forward.cpp',
'fm_tr.cpp', 'fm_tr.cpp',
'pev.cpp', 'pev.cpp',

View File

@ -16,6 +16,11 @@
edict_t *g_player_edicts[33]; // Used for INDEXENT() forward. edict_t *g_player_edicts[33]; // Used for INDEXENT() forward.
IGameConfig *CommonConfig;
IGameConfigManager *ConfigManager;
HLTypeConversion TypeConversion;
void OnAmxxAttach() void OnAmxxAttach()
{ {
initialze_offsets(); initialze_offsets();
@ -33,6 +38,25 @@ void OnAmxxAttach()
g_kvd_glb.kvd.szKeyName = const_cast<char *>(g_kvd_glb.key.chars()); g_kvd_glb.kvd.szKeyName = const_cast<char *>(g_kvd_glb.key.chars());
g_kvd_glb.kvd.szValue = const_cast<char *>(g_kvd_glb.val.chars()); g_kvd_glb.kvd.szValue = const_cast<char *>(g_kvd_glb.val.chars());
g_kvd_glb.kvd.fHandled = 0; g_kvd_glb.kvd.fHandled = 0;
ConfigManager = MF_GetConfigManager();
char error[256];
error[0] = '\0';
if (!ConfigManager->LoadGameConfigFile("common.games", &CommonConfig, error, sizeof(error)) && error[0] != '\0')
{
MF_Log("Could not read common.games gamedata: %s", error);
MF_Log("get/set/find_ent_data* natives have been disabled");
return;
}
MF_AddNatives(pdata_gc_natives);
}
void OnPluginsLoaded()
{
TypeConversion.init();
} }
extern CStack<TraceResult *> g_FreeTRs; extern CStack<TraceResult *> g_FreeTRs;

View File

@ -25,6 +25,8 @@
#include "glb.h" #include "glb.h"
#include <am-string.h> #include <am-string.h>
#include <am-vector.h> #include <am-vector.h>
#include <IGameConfigs.h>
#include <HLTypeConversion.h>
extern edict_t *g_player_edicts[33]; extern edict_t *g_player_edicts[33];
@ -55,13 +57,14 @@ inline edict_t* INDEXENT2( int iEdictNum )
} }
#endif #endif
#define CHECK_ENTITY(x) if (x != 0 && (FNullEnt(INDEXENT2(x)) || x < 0 || x > gpGlobals->maxEntities)) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid entity"); return 0; } #define CHECK_ENTITY(x) if (x != 0 && (FNullEnt(TypeConversion.id_to_edict(x)) || x < 0 || x > gpGlobals->maxEntities)) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid entity"); return 0; }
#define CHECK_OFFSET(x) if (x < 0) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid offset"); return 0; } #define CHECK_OFFSET(x) if (x < 0) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid offset"); return 0; }
extern AMX_NATIVE_INFO engfunc_natives[]; extern AMX_NATIVE_INFO engfunc_natives[];
extern AMX_NATIVE_INFO dllfunc_natives[]; extern AMX_NATIVE_INFO dllfunc_natives[];
extern AMX_NATIVE_INFO forward_natives[]; extern AMX_NATIVE_INFO forward_natives[];
extern AMX_NATIVE_INFO pdata_natives[]; extern AMX_NATIVE_INFO pdata_natives[];
extern AMX_NATIVE_INFO pdata_gc_natives[];
extern AMX_NATIVE_INFO tr_Natives[]; extern AMX_NATIVE_INFO tr_Natives[];
extern AMX_NATIVE_INFO pev_natives[]; extern AMX_NATIVE_INFO pev_natives[];
extern AMX_NATIVE_INFO glb_natives[]; extern AMX_NATIVE_INFO glb_natives[];
@ -80,5 +83,10 @@ extern enginefuncs_t *g_pengfuncsTable_Post;
extern NEW_DLL_FUNCTIONS *g_pNewFunctionsTable; extern NEW_DLL_FUNCTIONS *g_pNewFunctionsTable;
extern NEW_DLL_FUNCTIONS *g_pNewFunctionsTable_Post; extern NEW_DLL_FUNCTIONS *g_pNewFunctionsTable_Post;
extern IGameConfig *CommonConfig;
extern IGameConfigManager *ConfigManager;
extern HLTypeConversion TypeConversion;
#endif //_FAKEMETA_INCLUDE_H #endif //_FAKEMETA_INCLUDE_H

View File

@ -68,7 +68,7 @@
/** All plugins loaded /** All plugins loaded
* Do forward functions init here (MF_RegisterForward) * Do forward functions init here (MF_RegisterForward)
*/ */
//#define FN_AMXX_PLUGINSLOADED OnPluginsLoaded #define FN_AMXX_PLUGINSLOADED OnPluginsLoaded
/** All plugins are about to be unloaded */ /** All plugins are about to be unloaded */
//#define FN_AMXX_PLUGINSUNLOADING OnPluginsUnloading //#define FN_AMXX_PLUGINSUNLOADING OnPluginsUnloading

View File

@ -54,7 +54,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\;..\..\..\public;..\..\..\public\sdk; ..\..\..\public\amtl\include;..\..\third_party;..\..\third_party\hashing;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\;..\..\..\public;..\..\..\public\sdk;..\..\..\public\amtl\include;..\..\third_party;..\..\third_party\hashing;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;FAKEMETA_EXPORTS;HAVE_STDINT_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;FAKEMETA_EXPORTS;HAVE_STDINT_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild> <MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
@ -77,7 +77,7 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
<AdditionalIncludeDirectories>..\;..\..\..\public;..\..\..\public\sdk; ..\..\..\public\amtl\include;..\..\third_party;..\..\third_party\hashing;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\;..\..\..\public;..\..\..\public\sdk;..\..\..\public\amtl\include;..\..\third_party;..\..\third_party\hashing;$(METAMOD)\metamod;$(HLSDK)\common;$(HLSDK)\engine;$(HLSDK)\dlls;$(HLSDK)\pm_shared;$(HLSDK)\public;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;FAKEMETA_EXPORTS;HAVE_STDINT_H;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;FAKEMETA_EXPORTS;HAVE_STDINT_H;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary> <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<RuntimeTypeInfo>false</RuntimeTypeInfo> <RuntimeTypeInfo>false</RuntimeTypeInfo>
@ -103,12 +103,14 @@
<ClCompile Include="..\pdata.cpp" /> <ClCompile Include="..\pdata.cpp" />
<ClCompile Include="..\dllfunc.cpp" /> <ClCompile Include="..\dllfunc.cpp" />
<ClCompile Include="..\engfunc.cpp" /> <ClCompile Include="..\engfunc.cpp" />
<ClCompile Include="..\pdata_gc.cpp" />
<ClCompile Include="..\pev.cpp" /> <ClCompile Include="..\pev.cpp" />
<ClCompile Include="..\forward.cpp" /> <ClCompile Include="..\forward.cpp" />
<ClCompile Include="..\glb.cpp" /> <ClCompile Include="..\glb.cpp" />
<ClCompile Include="..\..\..\public\sdk\amxxmodule.cpp" /> <ClCompile Include="..\..\..\public\sdk\amxxmodule.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\..\public\HLTypeConversion.h" />
<ClInclude Include="..\fakemeta_amxx.h" /> <ClInclude Include="..\fakemeta_amxx.h" />
<ClInclude Include="..\fm_tr.h" /> <ClInclude Include="..\fm_tr.h" />
<ClInclude Include="..\dllfunc.h" /> <ClInclude Include="..\dllfunc.h" />

View File

@ -68,6 +68,9 @@
<ClCompile Include="..\..\..\public\sdk\amxxmodule.cpp"> <ClCompile Include="..\..\..\public\sdk\amxxmodule.cpp">
<Filter>Module SDK\SDK Base</Filter> <Filter>Module SDK\SDK Base</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\pdata_gc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\fakemeta_amxx.h"> <ClInclude Include="..\fakemeta_amxx.h">
@ -103,6 +106,9 @@
<ClInclude Include="..\..\..\public\sdk\amxxmodule.h"> <ClInclude Include="..\..\..\public\sdk\amxxmodule.h">
<Filter>Module SDK\SDK Base</Filter> <Filter>Module SDK\SDK Base</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\..\public\HLTypeConversion.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\..\plugins\include\fakemeta.inc"> <None Include="..\..\..\plugins\include\fakemeta.inc">

View File

@ -0,0 +1,537 @@
// vim: set ts=4 sw=4 tw=99 noet:
//
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
// Copyright (C) The AMX Mod X Development Team.
//
// This software is licensed under the GNU General Public License, version 3 or higher.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://alliedmods.net/amxmodx-license
//
// Fakemeta Module
//
#include "fakemeta_amxx.h"
#include <am-algorithm.h>
enum class BaseFieldType
{
None,
Integer,
Float,
Vector,
Entity,
String,
};
static const char *BaseFieldTypeName[] =
{
"none",
"integer",
"float",
"vector",
"entity",
"string",
};
static BaseFieldType GetBaseDataType(TypeDescription &data)
{
switch (data.fieldType)
{
case FieldType::FIELD_INTEGER:
case FieldType::FIELD_STRINGINT:
case FieldType::FIELD_SHORT:
case FieldType::FIELD_CHARACTER:
case FieldType::FIELD_CLASS:
case FieldType::FIELD_STRUCTURE:
case FieldType::FIELD_POINTER:
case FieldType::FIELD_FUNCTION:
case FieldType::FIELD_BOOLEAN:
{
return BaseFieldType::Integer;
}
case FieldType::FIELD_FLOAT:
{
return BaseFieldType::Float;
}
case FieldType::FIELD_VECTOR:
{
return BaseFieldType::Vector;
}
case FieldType::FIELD_CLASSPTR:
case FieldType::FIELD_ENTVARS:
case FieldType::FIELD_EDICT:
case FieldType::FIELD_EHANDLE:
{
return BaseFieldType::Entity;
}
case FieldType::FIELD_STRINGPTR:
case FieldType::FIELD_STRING:
{
return BaseFieldType::String;
}
}
return BaseFieldType::None;
}
#define GET_TYPE_DESCRIPTION(position, data, baseType) \
int classLength, memberLength; \
const char *className = MF_GetAmxString(amx, params[position], 0, &classLength); \
const char *memberName = MF_GetAmxString(amx, params[position + 1], 1, &memberLength); \
if (!classLength || !memberLength) \
{ \
MF_LogError(amx, AMX_ERR_NATIVE, "Either class (\"%s\") or member (\"%s\") is empty", className, memberName); \
return 0; \
} \
else if (!CommonConfig->GetOffsetByClass(className, memberName, &data)) \
{ \
MF_LogError(amx, AMX_ERR_NATIVE, "Could not find class \"%s\" and/or member \"%s\" in gamedata", className, memberName); \
return 0; \
} \
else if (data.fieldOffset < 0) \
{ \
MF_LogError(amx, AMX_ERR_NATIVE, "Invalid offset %d retrieved from \"%s\" member", data.fieldOffset, memberName); \
return 0; \
} \
else if (baseType > BaseFieldType::None && baseType != GetBaseDataType(data)) \
{ \
MF_LogError(amx, AMX_ERR_NATIVE, "Data field is not %s-based", BaseFieldTypeName[static_cast<int>(baseType)]); \
return 0; \
}
#define CHECK_ELEMENT(element) \
if (element < 0 || (element > 0 && element >= data.fieldSize)) \
{ \
MF_LogError(amx, AMX_ERR_NATIVE, "Invalid element index %d, value must be between 0 and %d", element, data.fieldSize); \
return 0; \
} \
else if (element > 0 && !data.fieldSize) \
{ \
MF_LogError(amx, AMX_ERR_NATIVE, "Member \"%s\" is not an array. Element %d is invalid.", memberName, element);\
return 0; \
}
// native any:get_ent_data(entity, const class[], const member[], element = 0);
static cell AMX_NATIVE_CALL get_ent_data(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::Integer);
int element = params[4];
CHECK_ELEMENT(element);
auto pEntity = TypeConversion.id_to_edict(entity);
switch (data.fieldType)
{
case FieldType::FIELD_INTEGER:
case FieldType::FIELD_STRINGINT:
{
return get_pdata<int32>(pEntity, data.fieldOffset, element);
}
case FieldType::FIELD_CLASS:
case FieldType::FIELD_STRUCTURE:
{
return reinterpret_cast<cell>(reinterpret_cast<int8*>(pEntity->pvPrivateData) + data.fieldOffset);
}
case FieldType::FIELD_POINTER:
case FieldType::FIELD_FUNCTION:
{
return reinterpret_cast<cell>(get_pdata<void*>(pEntity, data.fieldOffset, element));
}
case FieldType::FIELD_SHORT:
{
if (data.fieldUnsigned)
{
return get_pdata<uint16>(pEntity, data.fieldOffset, element);
}
else
{
return get_pdata<int16>(pEntity, data.fieldOffset, element);
}
}
case FieldType::FIELD_CHARACTER:
{
if (data.fieldUnsigned)
{
return get_pdata<uint8>(pEntity, data.fieldOffset, element);
}
else
{
return get_pdata<int8>(pEntity, data.fieldOffset, element);
}
}
case FieldType::FIELD_BOOLEAN:
{
return get_pdata<bool>(pEntity, data.fieldOffset, element) ? 1 : 0;
}
}
return 0;
}
// native set_ent_data(entity, const class[], const member[], any:value, element = 0);
static cell AMX_NATIVE_CALL set_ent_data(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::Integer);
int element = params[5];
CHECK_ELEMENT(element);
auto pEntity = TypeConversion.id_to_edict(entity);
auto value = params[4];
switch (data.fieldType)
{
case FieldType::FIELD_INTEGER:
case FieldType::FIELD_STRINGINT:
{
set_pdata<int32>(pEntity, data.fieldOffset, static_cast<int32>(value), element);
break;
}
case FieldType::FIELD_CLASS:
case FieldType::FIELD_STRUCTURE:
{
MF_LogError(amx, AMX_ERR_NATIVE, "Setting directly to a class or structure address is not available");
return 0;
}
case FieldType::FIELD_POINTER:
case FieldType::FIELD_FUNCTION:
{
set_pdata<void*>(pEntity, data.fieldOffset, reinterpret_cast<void*>(value), element);
break;
}
case FieldType::FIELD_SHORT:
{
if (data.fieldUnsigned)
{
set_pdata<uint16>(pEntity, data.fieldOffset, static_cast<uint16>(value), element);
}
else
{
set_pdata<int16>(pEntity, data.fieldOffset, static_cast<uint16>(value), element);
}
break;
}
case FieldType::FIELD_CHARACTER:
{
if (data.fieldUnsigned)
{
set_pdata<uint8>(pEntity, data.fieldOffset, static_cast<uint8>(value), element);
}
else
{
set_pdata<int8>(pEntity, data.fieldOffset, static_cast<uint8>(value), element);
}
break;
}
case FieldType::FIELD_BOOLEAN:
{
set_pdata<bool>(pEntity, data.fieldOffset, value != 0, element);
break;
}
}
return 0;
}
// native Float:get_ent_data_float(entity, const class[], const member[], element = 0);
static cell AMX_NATIVE_CALL get_ent_data_float(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::Float);
int element = params[4];
CHECK_ELEMENT(element);
return amx_ftoc(get_pdata<float>(TypeConversion.id_to_edict(entity), data.fieldOffset, element));
}
// native set_ent_data_float(entity, const classname[], const member[], Float:value, element = 0);
static cell AMX_NATIVE_CALL set_ent_data_float(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::Float);
int element = params[5];
CHECK_ELEMENT(element);
set_pdata<float>(TypeConversion.id_to_edict(entity), data.fieldOffset, amx_ctof(params[4]), element);
return 1;
}
// native get_ent_data_vector(entity, const class[], const member[], Float:value[3], element = 0);
static cell AMX_NATIVE_CALL get_ent_data_vector(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::Vector);
int element = params[5];
CHECK_ELEMENT(element);
auto refvec = MF_GetAmxAddr(amx, params[4]);
auto vector = get_pdata<Vector>(TypeConversion.id_to_edict(entity), data.fieldOffset, element);
refvec[0] = amx_ftoc(vector.x);
refvec[1] = amx_ftoc(vector.y);
refvec[2] = amx_ftoc(vector.z);
return 1;
}
// native set_ent_data_vector(entity, const class[], const member[], Float:value[3], element = 0);
static cell AMX_NATIVE_CALL set_ent_data_vector(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::Vector);
int element = params[5];
CHECK_ELEMENT(element);
auto refvec = MF_GetAmxAddr(amx, params[4]);
Vector vector(amx_ctof(refvec[0]), amx_ctof(refvec[1]), amx_ctof(refvec[2]));
set_pdata<Vector>(TypeConversion.id_to_edict(entity), data.fieldOffset, vector, element);
return 1;
}
// native get_ent_data_entity(entity, const class[], const member[], element = 0);
static cell AMX_NATIVE_CALL get_ent_data_entity(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::Entity);
int element = params[4];
CHECK_ELEMENT(element);
auto pEntity = TypeConversion.id_to_edict(entity);
switch (data.fieldType)
{
case FieldType::FIELD_CLASSPTR:
{
return TypeConversion.cbase_to_id(get_pdata<void*>(pEntity, data.fieldOffset, element));
}
case FieldType::FIELD_ENTVARS:
{
return TypeConversion.entvars_to_id(get_pdata<entvars_t*>(pEntity, data.fieldOffset, element));
}
case FieldType::FIELD_EDICT:
{
return TypeConversion.edict_to_id(get_pdata<edict_t*>(pEntity, data.fieldOffset, element));
}
case FieldType::FIELD_EHANDLE:
{
return TypeConversion.edict_to_id(get_pdata<EHANDLE>(pEntity, data.fieldOffset, element).Get());
}
}
return 0;
}
// native set_ent_data_entity(entity, const class[], const member[], value, element = 0);
static cell AMX_NATIVE_CALL set_ent_data_entity(AMX *amx, cell *params)
{
int entity = params[1];
int value = params[4];
CHECK_ENTITY(entity);
if (value != -1)
{
CHECK_ENTITY(value);
}
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::Entity);
int element = params[5];
CHECK_ELEMENT(element);
auto pEntity = TypeConversion.id_to_edict(entity);
switch (data.fieldType)
{
case FieldType::FIELD_CLASSPTR:
{
set_pdata<void*>(pEntity, data.fieldOffset, value != -1 ? TypeConversion.id_to_cbase(value) : nullptr, element);
break;
}
case FieldType::FIELD_ENTVARS:
{
set_pdata<entvars_t*>(pEntity, data.fieldOffset, value != -1 ? TypeConversion.id_to_entvars(value) : nullptr, element);
break;
}
case FieldType::FIELD_EDICT:
{
set_pdata<edict_t*>(pEntity, data.fieldOffset, value != -1 ? TypeConversion.id_to_edict(value) : nullptr, element);
break;
}
case FieldType::FIELD_EHANDLE:
{
get_pdata<EHANDLE>(pEntity, data.fieldOffset, element).Set(value != -1 ? TypeConversion.id_to_edict(value) : nullptr);
break;
}
}
return 0;
}
// native get_ent_data_string(entity, const class[], const member[], value[], maxlen, element = 0);
static cell AMX_NATIVE_CALL get_ent_data_string(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::String);
int element = params[6];
CHECK_ELEMENT(element);
auto pEntity = TypeConversion.id_to_edict(entity);
cell buffer = params[4];
int maxlen = params[5];
switch (data.fieldType)
{
case FieldType::FIELD_STRING:
{
maxlen = ke::Min<int>(maxlen, static_cast<int>(data.fieldSize));
char *string = get_pdata_direct<char*>(pEntity, data.fieldOffset, element, data.fieldSize);
return MF_SetAmxStringUTF8Char(amx, buffer, string ? string : "", string ? strlen(string) : 0, maxlen);
}
case FieldType::FIELD_STRINGPTR:
{
char *string = get_pdata<char*>(pEntity, data.fieldOffset, element);
return MF_SetAmxStringUTF8Char(amx, buffer, string ? string : "", string ? strlen(string) : 0, maxlen);
}
}
return 0;
}
// native set_ent_data_string(entity, const class[], const member[], const value[], element = 0);
static cell AMX_NATIVE_CALL set_ent_data_string(AMX *amx, cell *params)
{
int entity = params[1];
CHECK_ENTITY(entity);
TypeDescription data;
GET_TYPE_DESCRIPTION(2, data, BaseFieldType::String);
int element = params[5];
CHECK_ELEMENT(element);
auto pEntity = TypeConversion.id_to_edict(entity);
int length;
const char *value = MF_GetAmxString(amx, params[4], 0, &length);
switch (data.fieldType)
{
case FieldType::FIELD_STRING:
{
auto buffer = reinterpret_cast<char*>(pEntity->pvPrivateData) + data.fieldOffset;
return strncopy(buffer, value, ke::Min<int>(length + 1, data.fieldSize));
}
case FieldType::FIELD_STRINGPTR:
{
auto buffer = get_pdata<char*>(pEntity, data.fieldOffset, element);
if (!buffer || length > static_cast<int>(strlen(buffer)))
{
if (buffer)
{
free(buffer);
}
buffer = reinterpret_cast<char*>(malloc(length + 1));
set_pdata<char*>(pEntity, data.fieldOffset, buffer, element);
}
return strncopy(buffer, value, length + 1);
}
}
return 0;
}
// native get_ent_data_size(const class[], const member[]);
static cell AMX_NATIVE_CALL get_ent_data_size(AMX *amx, cell *params)
{
TypeDescription data;
GET_TYPE_DESCRIPTION(0, data, BaseFieldType::None);
return data.fieldSize;
}
// native find_ent_data_info(const class[], const member[], &FieldType:type = FIELD_NONE, &arraysize = 0, &bool:unsigned = false);
static cell AMX_NATIVE_CALL find_ent_data_info(AMX *amx, cell *params)
{
TypeDescription data;
GET_TYPE_DESCRIPTION(1, data, BaseFieldType::None);
*MF_GetAmxAddr(amx, params[3]) = static_cast<cell>(data.fieldType);
*MF_GetAmxAddr(amx, params[4]) = ke::Max<int>(0, data.fieldSize);
*MF_GetAmxAddr(amx, params[5]) = data.fieldUnsigned != 0;
return data.fieldOffset;
}
AMX_NATIVE_INFO pdata_gc_natives[] =
{
{ "get_ent_data" , get_ent_data },
{ "set_ent_data" , set_ent_data },
{ "get_ent_data_float" , get_ent_data_float },
{ "set_ent_data_float" , set_ent_data_float },
{ "get_ent_data_vector", get_ent_data_vector },
{ "set_ent_data_vector", set_ent_data_vector },
{ "get_ent_data_entity", get_ent_data_entity },
{ "set_ent_data_entity", set_ent_data_entity },
{ "get_ent_data_string", get_ent_data_string },
{ "set_ent_data_string", set_ent_data_string },
{ "get_ent_data_size" , get_ent_data_size },
{ "find_ent_data_info" , find_ent_data_info },
{ nullptr , nullptr }
};

View File

@ -557,11 +557,350 @@ native lookup_sequence(entity, const name[], &Float:framerate = 0.0, &bool:loops
*/ */
native Float:set_controller(entity, controller, Float:value); native Float:set_controller(entity, controller, Float:value);
/**
* Retrieves an integer value from an entity's private data based off a class
* and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
* @note This native is used to access the following (C++/engine) data types:
* integer, boolean, short, character, pointer, structure, class,
* stringint and function. Unsigned variants (if applicable) are supported
* and will be converted automatically.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param element Element to retrieve (starting from 0) if member is an array
*
* @return Integer value
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native any:get_ent_data(entity, const class[], const member[], element = 0);
/**
* Sets an integer value to an entity's private data based off a class
* and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
* @note This native is used to access the following (C++/engine) data types:
* integer, boolean, short, character, pointer, stringint and function.
* Unsigned variants (if applicable) are supported and will be converted
* automatically.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param value Value to set
* @param element Element to set (starting from 0) if member is an array
*
* @noreturn
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native set_ent_data(entity, const class[], const member[], any:value, element = 0);
/**
* Retrieves a float value from an entity's private data based off a class
* and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param element Element to retrieve (starting from 0) if member is an array
*
* @return Float value
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native Float:get_ent_data_float(entity, const class[], const member[], element = 0);
/**
* Sets a float value to an entity's private data based off a class
* and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param value Value to set
* @param element Element to set (starting from 0) if member is an array
*
* @noreturn
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native set_ent_data_float(entity, const class[], const member[], Float:value, element = 0);
/**
* Retrieves a vector from an entity's private data based off a class and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param value Vector buffer to store data in
* @param element Element to retrieve (starting from 0) if member is an array
*
* @noreturn
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native get_ent_data_vector(entity, const class[], const member[], Float:value[3], element = 0);
/**
* Sets a vector to an entity's private data based off a class and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param value Vector to set
* @param element Element to set (starting from 0) if member is an array
*
* @noreturn
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native set_ent_data_vector(entity, const class[], const member[], Float:value[3], element = 0);
/**
* Retrieves an entity index from an entity's private data based off a class
* and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
* @note This native is used to access the following (C++/engine) data types:
* classptr, entvars, edict and ehandle.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param element Element to retrieve (starting from 0) if member is an array
*
* @return Entity index if found, -1 otherwise
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native get_ent_data_entity(entity, const class[], const member[], element = 0);
/**
* Sets an entity index to an entity's private data based off a class
* and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
* @note This native is used to access the following (C++/engine) data types:
* classptr, entvars, edict and ehandle.
* @note Pass -1 as value to act as C++ NULL.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param value Entity index to set
* @param element Element to set (starting from 0) if member is an array
*
* @noreturn
* @error If an invalid entity or value is provided, either class or member
* is empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native set_ent_data_entity(entity, const class[], const member[], value, element = 0);
/**
* Retrieves a string from an entity's private data based off a class and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
* @note This native is used to access the following (C++/engine) data types:
* string, stringptr.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param value Buffer to store data in
* @param maxlen Maximum size of the buffer
* @param element Element to retrieve (starting from 0) if member is an array
*
* @return Number of cells written to buffer
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native get_ent_data_string(entity, const class[], const member[], value[], maxlen, element = 0);
/**
* Sets a string to an entity's private data based off a class and member name.
*
* @note Unlike the [get|set]_pdata_* natives that require compiling the class
* member offset into the plugin, this native instead retrieves the
* necessary offset from the AMXX gamedata files at runtime, based on the
* provided class and member name.
* @note This native is safer than [get|set]_pdata_* as it can perform stricter
* offset and typing checks.
* @note This native is used to access the following (C++/engine) data types:
* string, stringptr.
*
* @param entity Entity index
* @param class Class name
* @param member Member name
* @param value String to set
* @param element Element to set (starting from 0) if member is an array
*
* @return Number of cells written to buffer
* @error If an invalid entity is provided, either class or member is
* empty, no offset is found or an invalid offset is retrieved,
* or the data type does not match, an error will be thrown.
*/
native set_ent_data_string(entity, const class[], const member[], const value[], element = 0);
/**
* Retrieves the size of array of a class member.
*
* @param class Class name
* @param member Member name
*
* @return Size of array (in elements), otherwise 1 if member is not an array
* @error If either class or member is empty, no offset is found or an invalid
* offset is retrieved, an error will be thrown.
*/
native get_ent_data_size(const class[], const member[]);
/**
* Finds an offset based off a class and member name.
*
* @param class Class name
* @param member Member name
* @param type Optional variable to store member type in (FIELD_* constants)
* @param arraysize Optional variable to store array size in, if member is an array
* @param unsigned Optional variable to store whether member is unsigned (short and char types only)
*
* @return Class member offset
* @error If either class or member is empty, no offset is found or an invalid
* offset is retrieved, an error will be thrown.
*/
native find_ent_data_info(const class[], const member[], &FieldType:type = FIELD_NONE, &arraysize = 0, &bool:unsigned = false);
/**
* Returns the data field base type based off a specific field type.
*
* @note From an AMXX plugin perspective, the (C++/engine) data types can be grouped
* in five base types: integer, float, vector, entity and string. This stock is
* essentially for convenience and debug purpose.
*
* @param type Class member type (FIELD_* constants)
* @param type_name Optional buffer to store base type name in
* @param maxlen Maximum size of the buffer
*
* @return Base field type (BASEFIELD_* constants)
*/
stock BaseFieldType:get_ent_data_basetype(FieldType:type, type_name[] = "", maxlen = 0)
{
static baseFieldTypeNames[BaseFieldType][] =
{
"none",
"integer",
"float",
"vector",
"entity",
"string",
};
new BaseFieldType:baseType = BASEFIELD_NONE;
switch (type)
{
case FIELD_INTEGER, FIELD_STRINGINT, FIELD_SHORT , FIELD_CHARACTER,
FIELD_CLASS , FIELD_STRUCTURE, FIELD_POINTER, FIELD_FUNCTION,
FIELD_BOOLEAN:
{
baseType = BASEFIELD_INTEGER;
}
case FIELD_FLOAT:
{
baseType = BASEFIELD_FLOAT;
}
case FIELD_VECTOR:
{
baseType = BASEFIELD_VECTOR;
}
case FIELD_CLASSPTR, FIELD_ENTVARS, FIELD_EDICT, FIELD_EHANDLE:
{
baseType = BASEFIELD_ENTITY;
}
case FIELD_STRINGPTR, FIELD_STRING:
{
baseType = BASEFIELD_STRING;
}
}
if (maxlen > 0)
{
copy(type_name, maxlen, baseFieldTypeNames[baseType]);
}
return baseType;
}
enum enum
{ {
Model_DefaultSize = -2, Model_DefaultSize = -2,
Model_CurrentSequence = -1, Model_CurrentSequence = -1,
}; };
/** /**

View File

@ -753,3 +753,41 @@ enum AlertType
at_error, at_error,
at_logged // Server print to console (only in multiplayer games) at_logged // Server print to console (only in multiplayer games)
}; };
/**
* Data field types for use with find_ent_data_info().
*/
enum FieldType
{
FIELD_NONE,
FIELD_FLOAT, // Floating point value
FIELD_STRINGINT, // String ID (return from ALLOC_STRING)
FIELD_STRINGPTR, // String, pointer-to-char
FIELD_STRING, // String, fixed size
FIELD_CLASSPTR, // Classes pointer derived of CBaseEntity
FIELD_CLASS, // Arbitrary classes, direct
FIELD_STRUCTURE, // Arbitrary structures, direct
FIELD_EHANDLE, // Entity handle
FIELD_ENTVARS, // entvars_t*
FIELD_EDICT, // edict_t*
FIELD_VECTOR, // Vector
FIELD_POINTER, // Arbitrary data pointer
FIELD_INTEGER, // Integer or enum
FIELD_FUNCTION, // Class function pointer (Think, Use, etc)
FIELD_BOOLEAN, // Boolean
FIELD_SHORT, // 2 bytes integer
FIELD_CHARACTER, // 1 byte
};
/**
* Base data field types for use with get_ent_data_basetype().
*/
enum BaseFieldType
{
BASEFIELD_NONE,
BASEFIELD_INTEGER,
BASEFIELD_FLOAT,
BASEFIELD_VECTOR,
BASEFIELD_ENTITY,
BASEFIELD_STRING,
};

200
public/HLTypeConversion.h Normal file
View File

@ -0,0 +1,200 @@
//
// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO").
// Copyright (C) The AMX Mod X Development Team.
//
// This software is licensed under the GNU General Public License, version 3 or higher.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://alliedmods.net/amxmodx-license
#ifndef _HL_CONVERSION_TYPE_H_
#define _HL_CONVERSION_TYPE_H_
#include "amxxmodule.h"
template <typename T> static inline T& ref_pdata(void *pPrivateData, int offset, int element = 0)
{
return *reinterpret_cast<T*>((reinterpret_cast<int8*>(pPrivateData) + offset + (element * sizeof(T))));
}
template <typename T> inline T get_pdata_direct(edict_t *pEntity, int offset, int element = 0, int size = 0)
{
return reinterpret_cast<T>((reinterpret_cast<int8*>(pEntity->pvPrivateData) + offset + (element * size)));
}
template <typename T> inline T& get_pdata(void *pPrivateData, int offset, int element = 0)
{
return ref_pdata<T>(pPrivateData, offset, element);
}
template <typename T> inline T& get_pdata(edict_t *pEntity, int offset, int element = 0)
{
return get_pdata<T>(pEntity->pvPrivateData, offset, element);
}
template <typename T> inline void set_pdata(void *pPrivateData, int offset, T value, int element = 0)
{
ref_pdata<T>(pPrivateData, offset, element) = value;
}
template <typename T>inline void set_pdata(edict_t *pEntity, int offset, T value, int element = 0)
{
set_pdata<T>(pEntity->pvPrivateData, offset, value, element);
}
class HLTypeConversion
{
public:
HLTypeConversion() : m_FirstEdict(nullptr), m_PevOffset(0)
{}
public:
void init()
{
if (!m_FirstEdict)
{
m_FirstEdict = INDEXENT(0);
search_pev();
}
}
public: // Edict -> Index
int edict_to_id(edict_t *pEdict)
{
if (FNullEnt(pEdict))
{
return -1;
}
return static_cast<int>(pEdict - m_FirstEdict);
}
public: // Entvars -> Edict/Index
edict_t* entvar_to_edict(entvars_t *pev)
{
if (!pev || FNullEnt(pev->pContainingEntity))
{
return nullptr;
}
return pev->pContainingEntity;
}
int entvars_to_id(entvars_t *pev)
{
return edict_to_id(entvar_to_edict(pev));
}
public: // Index -> CBase*/Edict/Entvars
void* id_to_cbase(int index)
{
edict_t *pEdict = id_to_edict(index);
return pEdict ? pEdict->pvPrivateData : nullptr;
}
edict_t* id_to_edict(int index)
{
return static_cast<edict_t*>(m_FirstEdict + index);
}
entvars_t* id_to_entvars(int index)
{
edict_t *pEdict = id_to_edict(index);
return pEdict ? VARS(pEdict) : nullptr;
}
public: // CBase* -> Entvars/Index
entvars_t* cbase_to_entvar(void *cbase)
{
if (!cbase)
{
return nullptr;
}
return get_pdata<entvars_t*>(cbase, m_PevOffset);
}
int cbase_to_id(void *cbase)
{
return entvars_to_id(cbase_to_entvar(cbase));
}
private:
void search_pev()
{
entvars_t *pev = VARS(m_FirstEdict);
byte *privateData = reinterpret_cast<byte*>(m_FirstEdict->pvPrivateData);
for (size_t i = 0; i < 0xFFF; ++i)
{
entvars_t *val = *(reinterpret_cast<entvars_t**>(privateData + i));
if (val == pev)
{
m_PevOffset = i;
return;
}
}
m_PevOffset = 0; // This should not happen.
}
private:
edict_t* m_FirstEdict;
size_t m_PevOffset;
};
class EHANDLE
{
private:
edict_t* m_pent;
int m_serialnumber;
public:
edict_t* Get(void)
{
if (!FNullEnt(m_pent))
{
if (m_pent->serialnumber == m_serialnumber)
{
return m_pent;
}
return nullptr;
}
return nullptr;
};
edict_t* Set(edict_t *pent)
{
if (!FNullEnt(pent))
{
m_pent = pent;
m_serialnumber = m_pent->serialnumber;
}
else
{
m_pent = nullptr;
m_serialnumber = 0;
}
return pent;
};
};
#endif // _HL_CONVERSION_TYPE_H_