Merge pull request #189 from Arkshine/feature/add-cs_create_entity
Add cs_create_entity and cs_find_ent_by_class natives
This commit is contained in:
		| @@ -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', | ||||
|   | ||||
| @@ -140,23 +140,26 @@ | ||||
| 	#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_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_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_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 | ||||
|  | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										118
									
								
								dlls/cstrike/cstrike/CstrikeHLTypeConversion.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								dlls/cstrike/cstrike/CstrikeHLTypeConversion.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||
| } | ||||
							
								
								
									
										49
									
								
								dlls/cstrike/cstrike/CstrikeHLTypeConversion.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								dlls/cstrike/cstrike/CstrikeHLTypeConversion.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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} | ||||
| }; | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
| ############################################## | ||||
|   | ||||
| @@ -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<CreateNamedEntityFunc>(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_FindEntityByStringFunc>(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() | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user