diff --git a/gradle.properties b/gradle.properties index 107964b..8815c20 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ majorVersion=5 -minorVersion=17 +minorVersion=18 maintenanceVersion=0 diff --git a/reapi/extra/amxmodx/scripting/include/reapi.inc b/reapi/extra/amxmodx/scripting/include/reapi.inc index 2a796c3..86755b8 100644 --- a/reapi/extra/amxmodx/scripting/include/reapi.inc +++ b/reapi/extra/amxmodx/scripting/include/reapi.inc @@ -94,6 +94,13 @@ enum members_tables_e #include // @note: only for ReHLDS #include // @note: only for gamedll Counter-Strike (ReGameDLL_CS) +// If you want to use s/get_member unsafe version, +// then macro MEMBER_UNSAFE must be defined before including header reapi.inc +#if !defined(MEMBER_UNSAFE) + #define set_member set_member_s + #define get_member get_member_s +#endif + // addons #include #include diff --git a/reapi/extra/amxmodx/scripting/include/reapi_gamedll.inc b/reapi/extra/amxmodx/scripting/include/reapi_gamedll.inc index ca48e57..1e8d522 100644 --- a/reapi/extra/amxmodx/scripting/include/reapi_gamedll.inc +++ b/reapi/extra/amxmodx/scripting/include/reapi_gamedll.inc @@ -120,6 +120,28 @@ native set_member(const index, any:member, any:...); */ native any:get_member(const index, any:member, any:...); +/* +* Sets a value to an entity's member. +* Safe version, can guarantee that the present member is refers to derived class of the entity. +* +* @param index Entity index +* @param member The specified member, look at the enums with name *_Members +* +* @return 1 on success. +*/ +native set_member_s(const index, any:member, any:...); + +/* +* Returns a value from an entity's member. +* Safe version, can guarantee that the present member is refers to derived class of the entity. +* +* @param index Entity index +* @param member The specified member, look at the enums with name *_Members +* +* @return If an integer or boolean or one byte, array or everything else is passed via the 3rd argument and more, look at the argument list for the specified member +*/ +native any:get_member_s(const index, any:member, any:...); + /* * Sets playermove var. * diff --git a/reapi/src/member_list.cpp b/reapi/src/member_list.cpp index ab6b63d..0a03a8f 100644 --- a/reapi/src/member_list.cpp +++ b/reapi/src/member_list.cpp @@ -14,9 +14,9 @@ #define decltypefxdot(cx, pref, mx) decltype(cx::pref.mx) #endif -#define CLASS_MEMBERS(cx, mx, postf) ((!(postf & (MAX_REGION_RANGE - 1)) ? regmember::current_cell = 1, true : false) || (postf & (MAX_REGION_RANGE - 1)) == regmember::current_cell++) ? regmember([](member_t* ptr){ decltypefx(cx, , mx) f = {};ptr->size = getTypeSize(f);ptr->max_size = sizeof(f);ptr->offset = offsetof(cx, mx);ptr->type = getMemberType(f);ptr->name = #postf;}) : regmember(#mx) -#define CLASS_MEMBERS_PREF(cx, mx, postf, pref) ((!(postf & (MAX_REGION_RANGE - 1)) ? regmember::current_cell = 1, true : false) || (postf & (MAX_REGION_RANGE - 1)) == regmember::current_cell++) ? regmember([](member_t* ptr){ decltypefx(cx, pref, mx) f = {};ptr->size = getTypeSize(f);ptr->max_size = sizeof(f);ptr->offset = offsetof(cx, pref##mx);ptr->type = getMemberType(f);ptr->name = #postf;}) : regmember(#pref#mx) -#define CLASS_MEMBERS_DOT(cx, mx, postf, pref) ((!(postf & (MAX_REGION_RANGE - 1)) ? regmember::current_cell = 1, true : false) || (postf & (MAX_REGION_RANGE - 1)) == regmember::current_cell++) ? regmember([](member_t* ptr){ decltypefxdot(cx, pref, mx) f = {};ptr->size = getTypeSize(f);ptr->max_size = sizeof(f);ptr->offset = offsetof(cx, pref.mx);ptr->type = getMemberType(f);ptr->name = #postf;}) : regmember(#pref"."#mx) +#define CLASS_MEMBERS(cx, mx, postf) ((!(postf & (MAX_REGION_RANGE - 1)) ? regmember::current_cell = 1, true : false) || (postf & (MAX_REGION_RANGE - 1)) == regmember::current_cell++) ? regmember([](member_t* ptr){ decltypefx(cx, , mx) f = {};ptr->size = getTypeSize(f);ptr->max_size = sizeof(f);ptr->offset = offsetof(cx, mx);ptr->type = getMemberType(f);ptr->name = #postf;ptr->pfnIsRefsToClass = [](CBaseEntity *pEntity){ return dynamic_cast(pEntity) != nullptr;};}) : regmember(#mx) +#define CLASS_MEMBERS_PREF(cx, mx, postf, pref) ((!(postf & (MAX_REGION_RANGE - 1)) ? regmember::current_cell = 1, true : false) || (postf & (MAX_REGION_RANGE - 1)) == regmember::current_cell++) ? regmember([](member_t* ptr){ decltypefx(cx, pref, mx) f = {};ptr->size = getTypeSize(f);ptr->max_size = sizeof(f);ptr->offset = offsetof(cx, pref##mx);ptr->type = getMemberType(f);ptr->name = #postf;ptr->pfnIsRefsToClass = [](CBaseEntity *pEntity){ return dynamic_cast(pEntity) != nullptr;};}) : regmember(#pref#mx) +#define CLASS_MEMBERS_DOT(cx, mx, postf, pref) ((!(postf & (MAX_REGION_RANGE - 1)) ? regmember::current_cell = 1, true : false) || (postf & (MAX_REGION_RANGE - 1)) == regmember::current_cell++) ? regmember([](member_t* ptr){ decltypefxdot(cx, pref, mx) f = {};ptr->size = getTypeSize(f);ptr->max_size = sizeof(f);ptr->offset = offsetof(cx, pref.mx);ptr->type = getMemberType(f);ptr->name = #postf;ptr->pfnIsRefsToClass = [](CBaseEntity *pEntity){ return dynamic_cast(pEntity) != nullptr;};}) : regmember(#pref"."#mx) #define GM_MEMBERS(mx) CLASS_MEMBERS(CHalfLifeMultiplay, mx, mx) #define GM_VOICE_MEMBERS(mx) CLASS_MEMBERS_DOT(CHalfLifeMultiplay, mx, mx, m_VoiceGameMgr) @@ -616,7 +616,7 @@ member_t memberlist_entvars[] = { EVAR_MEMBERS(euser1), EVAR_MEMBERS(euser2), EVAR_MEMBERS(euser3), - EVAR_MEMBERS(euser4) + EVAR_MEMBERS(euser4), }; member_t memberlist_playermove[] = { @@ -723,7 +723,7 @@ member_t memberlist_usercmd[] = { UCMD_MEMBERS(impulse), UCMD_MEMBERS(weaponselect), UCMD_MEMBERS(impact_index), - UCMD_MEMBERS(impact_position) + UCMD_MEMBERS(impact_position), }; member_t memberlist_pmtrace[] = { @@ -735,7 +735,7 @@ member_t memberlist_pmtrace[] = { PMTRACE_MEMBERS(endpos), PMTRACE_MEMBERS(ent), PMTRACE_MEMBERS(deltavelocity), - PMTRACE_MEMBERS(hitgroup) + PMTRACE_MEMBERS(hitgroup), }; member_t memberlist_csplayer[] = { @@ -790,7 +790,7 @@ member_t memberlist_baseweapon[] = { BASEWPN_MEMBERS(usFireGlock18), BASEWPN_MEMBERS(usFireFamas), BASEWPN_MEMBERS(flPrevPrimaryAttack), - BASEWPN_MEMBERS(flLastFireTime) + BASEWPN_MEMBERS(flLastFireTime), }; member_t memberlist_weaponbox[] = { diff --git a/reapi/src/member_list.h b/reapi/src/member_list.h index e7de49e..50e1f47 100644 --- a/reapi/src/member_list.h +++ b/reapi/src/member_list.h @@ -95,6 +95,7 @@ struct member_t size_t offset; const char *name; MType type; + bool (*pfnIsRefsToClass)(class CBaseEntity *pEntity); }; inline bool member_t::isTypeReturnable() const diff --git a/reapi/src/natives/natives_members.cpp b/reapi/src/natives/natives_members.cpp index 925ffd4..6b075b6 100644 --- a/reapi/src/natives/natives_members.cpp +++ b/reapi/src/natives/natives_members.cpp @@ -108,6 +108,131 @@ cell AMX_NATIVE_CALL get_member(AMX *amx, cell *params) ); } +/* +* Sets a value to an entity's member. +* Safe version, can guarantee that the present member is refers to derived class of the entity. +* +* @param index Entity index +* @param member The specified member, look at the enums with name *_Members +* +* @return 1 on success. +* native set_member_safe(const index, any:member, any:...); +*/ +cell AMX_NATIVE_CALL set_member_s(AMX *amx, cell *params) +{ + enum args_e { arg_count, arg_index, arg_member, arg_value, arg_elem }; + member_t *member = memberlist[params[arg_member]]; + + if (unlikely(member == nullptr)) { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: unknown member id %i", __FUNCTION__, params[arg_member]); + return FALSE; + } + + edict_t *pEdict = edictByIndexAmx(params[arg_index]); + if (unlikely(pEdict == nullptr || pEdict->pvPrivateData == nullptr)) { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid or uninitialized entity", __FUNCTION__); + return FALSE; + } + + cell* value = getAmxAddr(amx, params[arg_value]); + size_t element = (PARAMS_COUNT == 4) ? *getAmxAddr(amx, params[arg_elem]) : 0; + CBaseEntity *pEntity = getPrivate(pEdict); + + if (!member->pfnIsRefsToClass(pEntity)) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: '%s' has no refs to the base class of an entity '%s'", __FUNCTION__, member->name, STRING(pEdict->v.classname)); + return FALSE; + } + + return set_member( + get_pdata_custom(pEntity, params[arg_member]), + member, + value, + element + ); +} + +/* +* Returns a value from an entity's member. +* Safe version, can guarantee that the present member is refers to derived class of the entity. +* +* @param index Entity index +* @param member The specified member, look at the enums with name *_Members +* +* @return If an integer or boolean or one byte, array or everything else is passed via the 3rd argument and more, look at the argument list for the specified member +* +* native any:get_member_safe(const index, any:member, any:...); +*/ +cell AMX_NATIVE_CALL get_member_s(AMX *amx, cell *params) +{ + enum args_e { arg_count, arg_index, arg_member, arg_3, arg_4, arg_5 }; + member_t *member = memberlist[params[arg_member]]; + + if (unlikely(member == nullptr)) { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: unknown member id %i", __FUNCTION__, params[arg_member]); + return FALSE; + } + + edict_t *pEdict = edictByIndexAmx(params[arg_index]); + if (unlikely(pEdict == nullptr || pEdict->pvPrivateData == nullptr)) { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid or uninitialized entity", __FUNCTION__); + return FALSE; + } + + cell* dest; + size_t element; + size_t length; + + switch (PARAMS_COUNT) + { + case 5: + dest = getAmxAddr(amx, params[arg_3]); + length = *getAmxAddr(amx, params[arg_4]); + element = *getAmxAddr(amx, params[arg_5]); + break; + case 4: + dest = getAmxAddr(amx, params[arg_3]); + length = *getAmxAddr(amx, params[arg_4]); + element = 0; + break; + case 3: + { + cell* arg3 = getAmxAddr(amx, params[arg_3]); + if (member->isTypeReturnable()) { + dest = nullptr; + element = *arg3; + } + else { + dest = arg3; + element = 0; + } + length = 0; + break; + } + default: + dest = nullptr; + element = 0; + length = 0; + break; + } + + CBaseEntity *pEntity = getPrivate(pEdict); + + if (!member->pfnIsRefsToClass(pEntity)) + { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: '%s' has no refs to the base class of an entity '%s'", __FUNCTION__, member->name, STRING(pEdict->v.classname)); + return FALSE; + } + + return get_member( + get_pdata_custom(pEntity, params[arg_member]), + member, + dest, + element, + length + ); +} + /* * Sets a value to CSGameRules_Members members. * @@ -617,6 +742,9 @@ AMX_NATIVE_INFO ReGameVars_Natives[] = { "set_member", set_member }, { "get_member", get_member }, + { "set_member_s", set_member_s }, + { "get_member_s", get_member_s }, + { "set_member_game", set_member_game }, { "get_member_game", get_member_game },