diff --git a/dlls/cstrike/cstrike/AMBuilder b/dlls/cstrike/cstrike/AMBuilder index aaffd58f..bae60bed 100644 --- a/dlls/cstrike/cstrike/AMBuilder +++ b/dlls/cstrike/cstrike/AMBuilder @@ -14,6 +14,7 @@ binary.sources = [ 'CstrikeNatives.cpp', 'CstrikeHacks.cpp', 'CstrikeUtils.cpp', + 'CstrikeHLTypeConversion.cpp', '../../../public/memtools/MemoryUtils.cpp', '../../../public/memtools/CDetour/detours.cpp', '../../../public/memtools/CDetour/asm/asm.c', diff --git a/dlls/cstrike/cstrike/CstrikeDatas.h b/dlls/cstrike/cstrike/CstrikeDatas.h index 59d955e1..f65af4e1 100644 --- a/dlls/cstrike/cstrike/CstrikeDatas.h +++ b/dlls/cstrike/cstrike/CstrikeDatas.h @@ -140,24 +140,27 @@ #define CS_CLICMD_OFFS_BOTARGS 22 #endif -/** - * CS_OnBuy forward - */ #if defined(__linux__) - #define CS_IDENT_GIVENSHIELD "_ZN11CBasePlayer10GiveShieldEb" - #define CS_IDENT_GIVENAMEDITEM "_ZN11CBasePlayer13GiveNamedItemEPKc" - #define CS_IDENT_ADDACCOUNT "_ZN11CBasePlayer10AddAccountEib" - #define CS_IDENT_HIDDEN_STATE false + #define CS_IDENT_GIVENSHIELD "_ZN11CBasePlayer10GiveShieldEb" // CS_OnBuy forward + #define CS_IDENT_GIVENAMEDITEM "_ZN11CBasePlayer13GiveNamedItemEPKc" // CS_OnBuy forward + #define CS_IDENT_ADDACCOUNT "_ZN11CBasePlayer10AddAccountEib" // CS_OnBuy forward + #define CS_IDENT_CREATENAMEDENTITY "_Z19CREATE_NAMED_ENTITYj" // cs_create_entity + #define CS_IDENT_UTIL_FINDENTITYBYSTRING "_Z23UTIL_FindEntityByStringP11CBaseEntityPKcS2_" // cs_create_entity + #define CS_IDENT_HIDDEN_STATE false #elif defined(__APPLE__) - #define CS_IDENT_GIVENSHIELD "_ZN11CBasePlayer10GiveShieldEb" - #define CS_IDENT_GIVENAMEDITEM "_ZN11CBasePlayer13GiveNamedItemEPKc" - #define CS_IDENT_ADDACCOUNT "_ZN11CBasePlayer10AddAccountEib" - #define CS_IDENT_HIDDEN_STATE true + #define CS_IDENT_GIVENSHIELD "_ZN11CBasePlayer10GiveShieldEb" + #define CS_IDENT_GIVENAMEDITEM "_ZN11CBasePlayer13GiveNamedItemEPKc" + #define CS_IDENT_ADDACCOUNT "_ZN11CBasePlayer10AddAccountEib" + #define CS_IDENT_CREATENAMEDENTITY "_Z19CREATE_NAMED_ENTITYj" + #define CS_IDENT_UTIL_FINDENTITYBYSTRING "_Z23UTIL_FindEntityByStringP11CBaseEntityPKcS2_" + #define CS_IDENT_HIDDEN_STATE true #elif defined(WIN32) - #define CS_IDENT_GIVENSHIELD "\\x56\\x8B\\x2A\\x57\\x33\\x2A\\x8B\\x2A\\x2A\\x2A\\x2A\\x2A\\xB0" - #define CS_IDENT_GIVENAMEDITEM "\\x8B\\x2A\\x2A\\x2A\\x56\\x57\\x8B\\x2A\\x8B\\x2A\\x2A\\x2A\\x2A\\x2A\\x2B" - #define CS_IDENT_ADDACCOUNT "\\x8B\\x2A\\x2A\\x2A\\x56\\x8B\\x2A\\x8B\\x2A\\x2A\\x2A\\x2A\\x2A\\x03" - #define CS_IDENT_HIDDEN_STATE false + #define CS_IDENT_GIVENSHIELD "\\x56\\x8B\\x2A\\x57\\x33\\x2A\\x8B\\x2A\\x2A\\x2A\\x2A\\x2A\\xB0" + #define CS_IDENT_GIVENAMEDITEM "\\x8B\\x2A\\x2A\\x2A\\x56\\x57\\x8B\\x2A\\x8B\\x2A\\x2A\\x2A\\x2A\\x2A\\x2B" + #define CS_IDENT_ADDACCOUNT "\\x8B\\x2A\\x2A\\x2A\\x56\\x8B\\x2A\\x8B\\x2A\\x2A\\x2A\\x2A\\x2A\\x03" + #define CS_IDENT_CREATENAMEDENTITY "\\x56\\x57\\x8B\\x2A\\x2A\\x2A\\x57\\xFF\\x2A\\x2A\\x2A\\x2A\\x2A\\x8B" + #define CS_IDENT_UTIL_FINDENTITYBYSTRING "\\x51\\x8B\\x2A\\x2A\\x2A\\x53\\x55\\x56\\x85\\x2A\\x57" + #define CS_IDENT_HIDDEN_STATE false #endif #define CSI_P228 CSW_P228 @@ -349,4 +352,7 @@ typedef enum } Menu; +typedef edict_t* (*CreateNamedEntityFunc)(string_t iszClassname); +typedef void* (*UTIL_FindEntityByStringFunc)(void* pStartEntity, const char *szKeyword, const char *szValue); + #endif // CSTRIKE_DATA_H diff --git a/dlls/cstrike/cstrike/CstrikeHLTypeConversion.cpp b/dlls/cstrike/cstrike/CstrikeHLTypeConversion.cpp new file mode 100644 index 00000000..3b971af3 --- /dev/null +++ b/dlls/cstrike/cstrike/CstrikeHLTypeConversion.cpp @@ -0,0 +1,118 @@ +// +// 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 + +// +// Counter-Strike Module +// + +#include "CstrikeHLTypeConversion.h" + +OffsetHandler* G_OffsetHandler; +HL_TypeConversion G_HL_TypeConversion; + +void OffsetHandler::search_pev() +{ + edict_t* edict = INDEXENT(0); + entvars_t* entvars = &edict->v; + + byte* private_c = (byte*)edict->pvPrivateData; + + for (int i = 0; i < 0xFFF; i++) + { + uintptr_t val = *((uintptr_t*)(private_c + i)); + + if (val == (uintptr_t)entvars) + { + this->pev = i; + return; + } + } + + // This should not happen. + this->pev = 0; +} + +inline edict_t* HL_TypeConversion::INDEXENT2(int iEdictNum) +{ + if (iEdictNum >= 1 && iEdictNum <= gpGlobals->maxClients) + { + return MF_GetPlayerEdict(iEdictNum); + } + else + { + return (*g_engfuncs.pfnPEntityOfEntIndex)(iEdictNum); + } +} + +edict_t* HL_TypeConversion::entvar_to_edict(entvars_t *pev) +{ + if (!pev) + { + return nullptr; + } + + return pev->pContainingEntity; +} + +int HL_TypeConversion::entvar_to_id(entvars_t *pev) +{ + if (!pev) + { + return -1; + } + + if (!pev->pContainingEntity) + { + return -1; + } + + return ENTINDEX(pev->pContainingEntity); +} + +void* HL_TypeConversion::id_to_cbase(int index) +{ + edict_t* edict = INDEXENT2(index); + return edict ? edict->pvPrivateData : nullptr; +} + +entvars_t* HL_TypeConversion::id_to_entvar(int index) +{ + return &(INDEXENT2(index)->v); +} + +entvars_t* HL_TypeConversion::cbase_to_entvar(void* cbase) +{ + if (!cbase) + { + return nullptr; + } + + return *(entvars_t **)((char *)(cbase) + G_OffsetHandler->pev); +} + +int HL_TypeConversion::cbase_to_id(void *cbase) +{ + if (!cbase) + { + return -1; + } + + entvars_t* pev = this->cbase_to_entvar(cbase); + + if (!pev) + { + return -1; + } + + if (!pev->pContainingEntity || FNullEnt(pev->pContainingEntity)) + { + return -1; + } + + return ENTINDEX(pev->pContainingEntity); +} diff --git a/dlls/cstrike/cstrike/CstrikeHLTypeConversion.h b/dlls/cstrike/cstrike/CstrikeHLTypeConversion.h new file mode 100644 index 00000000..327878e3 --- /dev/null +++ b/dlls/cstrike/cstrike/CstrikeHLTypeConversion.h @@ -0,0 +1,49 @@ +// +// 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 + +// +// Counter-Strike Module +// + +#ifndef HL_TYPE_CONVERSION_H +#define HL_TYPE_CONVERSION_H + +#include "amxxmodule.h" + +struct OffsetHandler +{ + int pev; + + void search_pev(); + + OffsetHandler() + { + search_pev(); + } +}; + +class HL_TypeConversion +{ + public: + + inline edict_t* INDEXENT2(int iEdictNum); + + edict_t* entvar_to_edict(entvars_t *pev); + int entvar_to_id(entvars_t *pev); + + void* id_to_cbase(int index); + int cbase_to_id(void *cbase); + + entvars_t* id_to_entvar(int index); + entvars_t* cbase_to_entvar(void* cbase); +}; + +extern OffsetHandler* G_OffsetHandler; +extern HL_TypeConversion G_HL_TypeConversion; + +#endif // HL_TYPE_CONVERSION_H diff --git a/dlls/cstrike/cstrike/CstrikeHacks.cpp b/dlls/cstrike/cstrike/CstrikeHacks.cpp index 094e88d5..29ee8c82 100644 --- a/dlls/cstrike/cstrike/CstrikeHacks.cpp +++ b/dlls/cstrike/cstrike/CstrikeHacks.cpp @@ -251,9 +251,9 @@ void CtrlDetours_ClientCommand(bool set) #endif ClientCommandDetour = DETOUR_CREATE_STATIC_FIXED(C_ClientCommand, target); - if (ClientCommandDetour == NULL) + if (!ClientCommandDetour) { - MF_Log("No Client Commands detour could be initialized - Disabled Client Command forward."); + MF_Log("ClientCommand is not available - forward client_command has been disabled"); } } else @@ -337,9 +337,24 @@ void CtrlDetours_BuyCommands(bool set) GiveNamedItemDetour = DETOUR_CREATE_MEMBER_FIXED(GiveNamedItem, giveNamedItemAddress); AddAccountDetour = DETOUR_CREATE_MEMBER_FIXED(AddAccount, addAccountAddress); - if (GiveNamedItemDetour == NULL || AddAccountDetour == NULL) + if (!GiveShieldDetour || !GiveNamedItemDetour || !AddAccountDetour) { - MF_Log("No Buy Commands detours could be initialized - Disabled Buy forward."); + if (!GiveShieldDetour) + { + MF_Log("GiveShield is not available"); + } + + if (!GiveNamedItemDetour) + { + MF_Log("GiveNamedItem is not available"); + } + + if (!AddAccountDetour) + { + MF_Log("AddAccount is not available"); + } + + MF_Log("Some functions are not available - forward CS_OnBuyAttempt and CS_OnBuy have been disabled"); } } else diff --git a/dlls/cstrike/cstrike/CstrikeNatives.cpp b/dlls/cstrike/cstrike/CstrikeNatives.cpp index e494bfb0..4d84c3ca 100644 --- a/dlls/cstrike/cstrike/CstrikeNatives.cpp +++ b/dlls/cstrike/cstrike/CstrikeNatives.cpp @@ -14,6 +14,7 @@ #include "CstrikeDatas.h" #include "CstrikePlayer.h" #include "CstrikeUtils.h" +#include "CstrikeHLTypeConversion.h" CCstrikePlayer g_players[33]; int g_zooming[33] = {0}; @@ -1707,6 +1708,55 @@ static cell AMX_NATIVE_CALL cs_set_c4_defusing(AMX* amx, cell* params) return 1; } +extern CreateNamedEntityFunc CS_CreateNamedEntity; +extern UTIL_FindEntityByStringFunc CS_UTIL_FindEntityByString; + +// cs_create_entity(const classname[]) +static cell AMX_NATIVE_CALL cs_create_entity(AMX* amx, cell* params) +{ + if (CS_CreateNamedEntity <= 0) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_create_entity() is disabled"); + return 0; + } + + int len; + int iszClass = ALLOC_STRING(MF_GetAmxString(amx, params[1], 0, &len)); + + edict_t *pEnt = CS_CreateNamedEntity(iszClass); + + if (!FNullEnt(pEnt)) + { + return ENTINDEX(pEnt); + } + + return 0; +} + +// cs_find_ent_by_class(start_index, const classname[]) +static cell AMX_NATIVE_CALL cs_find_ent_by_class(AMX* amx, cell* params) +{ + if (CS_UTIL_FindEntityByString <= 0) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_find_ent_by_class() is disabled"); + return 0; + } + + int len; + void* pEntity = G_HL_TypeConversion.id_to_cbase(params[1]); + const char* value = MF_GetAmxString(amx, params[2], 0, &len); + + int index = G_HL_TypeConversion.cbase_to_id(CS_UTIL_FindEntityByString(pEntity, "classname", value)); + + if (index != -1) + { + return index; + } + + return 0; +} + + #else static cell AMX_NATIVE_CALL not_on_64(AMX* amx, cell* params) @@ -1791,6 +1841,8 @@ AMX_NATIVE_INFO CstrikeNatives[] = { {"cs_set_c4_explode_time", cs_set_c4_explode_time}, {"cs_get_c4_defusing", cs_get_c4_defusing}, {"cs_set_c4_defusing", cs_set_c4_defusing}, + {"cs_create_entity", cs_create_entity }, + {"cs_find_ent_by_class", cs_find_ent_by_class}, {NULL, NULL} }; diff --git a/dlls/cstrike/cstrike/Makefile b/dlls/cstrike/cstrike/Makefile index a56c9de2..d4397e2f 100755 --- a/dlls/cstrike/cstrike/Makefile +++ b/dlls/cstrike/cstrike/Makefile @@ -15,7 +15,7 @@ PUBLIC_ROOT = ../../../public PROJECT = cstrike -OBJECTS = amxxmodule.cpp amxx_api.cpp CstrikePlayer.cpp CstrikeNatives.cpp CstrikeHacks.cpp CstrikeUtils.cpp \ +OBJECTS = amxxmodule.cpp amxx_api.cpp CstrikePlayer.cpp CstrikeNatives.cpp CstrikeHacks.cpp CstrikeUtils.cpp CstrikeHLTypeConversion.cpp \ MemoryUtils.cpp detours.cpp asm.c ############################################## diff --git a/dlls/cstrike/cstrike/amxx_api.cpp b/dlls/cstrike/cstrike/amxx_api.cpp index 17dfd227..1cf5c169 100644 --- a/dlls/cstrike/cstrike/amxx_api.cpp +++ b/dlls/cstrike/cstrike/amxx_api.cpp @@ -13,6 +13,8 @@ #include "amxxmodule.h" #include "CstrikeUtils.h" +#include "CstrikeDatas.h" +#include "CstrikeHLTypeConversion.h" extern AMX_NATIVE_INFO CstrikeNatives[]; @@ -25,6 +27,8 @@ void ShutdownHacks(); void ToggleDetour_ClientCommands(bool enable); void ToggleDetour_BuyCommands(bool enable); +CreateNamedEntityFunc CS_CreateNamedEntity = nullptr; +UTIL_FindEntityByStringFunc CS_UTIL_FindEntityByString = nullptr; int AmxxCheckGame(const char *game) { @@ -39,7 +43,27 @@ int AmxxCheckGame(const char *game) void OnAmxxAttach() { MF_AddNatives(CstrikeNatives); + InitializeHacks(); + + // cs_create_entity() + CS_CreateNamedEntity = reinterpret_cast(UTIL_FindAddressFromEntry(CS_IDENT_CREATENAMEDENTITY, CS_IDENT_HIDDEN_STATE)); + + if (CS_CreateNamedEntity <= 0) + { + MF_Log("CREATE_NAMED_ENITTY is not available - native cs_create_entity() has been disabled"); + } + + // cs_find_ent_by_class() + CS_UTIL_FindEntityByString = reinterpret_cast(UTIL_FindAddressFromEntry(CS_IDENT_UTIL_FINDENTITYBYSTRING, CS_IDENT_HIDDEN_STATE)); + + if (CS_UTIL_FindEntityByString <= 0) + { + MF_Log("UTIL_FindEntByString is not available - native cs_find_ent_by_class() has been disabled"); + } + + // Search pev offset automatically. + G_OffsetHandler = new OffsetHandler; } void OnPluginsLoaded() diff --git a/plugins/include/cstrike.inc b/plugins/include/cstrike.inc index 6c404eda..6bd2846d 100755 --- a/plugins/include/cstrike.inc +++ b/plugins/include/cstrike.inc @@ -350,6 +350,48 @@ native bool:cs_get_c4_defusing(c4index); native cs_set_c4_defusing(c4index, bool:defusing); +/** + * Creates an entity using Counter-Strike's custom CreateNamedEntity wrapper. + * + * @note Unlike other mods CS keeps track of entities using a custom hashtable. + * This function adds entities to this hashtable, providing benefits over + * the default CreateNamedEntity (used by create_entity() for example): + * - Storing entities in a hashtable allows CS to improve classname lookup + * performance compared to functions like FindEntityByString (used by + * find_ent_by_class() for example) that usually have to loop + * through all entities incrementally. + * - As CS exclusively uses the hashtable for classname lookup, entities + * created using the default engine functions will not be found by the + * game. For example "weaponbox" entities are supposed to be + * automatically cleaned up on round restart but are not considered if + * they have not been added to the hashtable. + * @note The faster hashtable lookup can be utilized with cs_find_ent_by_class() + * + * @param classname Entity class name + * + * @return Index of the created entity, 0 otherwise. + */ +native cs_create_entity(const classname[]); + +/** + * Finds an entity in the world using Counter-Strike's custom FindEntityByString + * wrapper. + * + * @note Unlike other mods CS keeps track of entities using a custom hashtable. + * This function utilizes the hasthable and allows for considerably faster + * classname lookup compared to the default FindEntityByString (used by + * find_ent_by_class() for example). + * @note This exclusively considers entities in the hashtable, created by the + * game itself or using cs_create_entity(). + * + * @param start_index Entity index to start searching from. -1 to start from + * the first entity + * @param classname Classname to search for + * + * @return Entity index > 0 if found, 0 otherwise + */ +native cs_find_ent_by_class(start_index, const classname[]); + /** * Called when CS internally fires a command to a player. It does this for a few * functions, most notably rebuy/autobuy functionality. This is also used to pass