diff --git a/extension.cpp b/extension.cpp index 2262c4e..98c029e 100644 --- a/extension.cpp +++ b/extension.cpp @@ -37,6 +37,8 @@ #include #include #include +#include +#include #define SetBit(A,I) ((A)[(I) >> 5] |= (1 << ((I) & 31))) #define ClearBit(A,I) ((A)[(I) >> 5] &= ~(1 << ((I) & 31))) @@ -173,6 +175,13 @@ struct variant_hax const char *pszValue; }; +struct ResponseContext_t +{ + string_t m_iszName; + string_t m_iszValue; + float m_fExpirationTime; +}; + struct inputdata_t { // The entity that initially caused this chain of output events. @@ -202,6 +211,8 @@ IGameConfig *g_pGameConf = NULL; CDetour *g_pDetour_InputTestActivator = NULL; CDetour *g_pDetour_PostConstructor = NULL; +CDetour *g_pDetour_CreateEntityByName = NULL; +CDetour *g_pDetour_PassesFilterImpl = NULL; CDetour *g_pDetour_FindUseEntity = NULL; CDetour *g_pDetour_CTraceFilterSimple = NULL; CDetour *g_pDetour_KeyValue = NULL; @@ -243,6 +254,64 @@ DETOUR_DECL_MEMBER1(DETOUR_PostConstructor, void, const char *, szClassname) DETOUR_MEMBER_CALL(DETOUR_PostConstructor)(szClassname); } +// Implementation for custom filter entities +DETOUR_DECL_MEMBER2(DETOUR_PassesFilterImpl, bool, CBaseEntity*, pCaller, CBaseEntity*, pEntity) +{ + CBaseEntity* pThisEnt = (CBaseEntity*)this; + + // filter_activator_context: filters activators based on whether they have a given context with a nonzero value + // https://developer.valvesoftware.com/wiki/Filter_activator_context + // Implemented here because CUtlVectors are not supported in sourcepawn + if (!strcasecmp(gamehelpers->GetEntityClassname(pThisEnt), "filter_activator_context")) + { + static int m_ResponseContexts_offset = 0, m_iszResponseContext_offset = 0; + + if (!m_ResponseContexts_offset && !m_iszResponseContext_offset) + { + datamap_t *pDataMap = gamehelpers->GetDataMap(pEntity); + sm_datatable_info_t info; + + // Both are CBaseEntity members, so the offsets will always be the same across different entity classes + gamehelpers->FindDataMapInfo(pDataMap, "m_ResponseContexts", &info); + m_ResponseContexts_offset = info.actual_offset; + + gamehelpers->FindDataMapInfo(pDataMap, "m_iszResponseContext", &info); + m_iszResponseContext_offset = info.actual_offset; + } + + CUtlVector vecResponseContexts; + vecResponseContexts = *(CUtlVector*)((uint8_t*)pEntity + m_ResponseContexts_offset); + + const char *szFilterContext = (*(string_t*)((uint8_t*)pThisEnt + m_iszResponseContext_offset)).ToCStr(); + const char *szContext; + int iContextValue; + + for (int i = 0; i < vecResponseContexts.Count(); i++) + { + szContext = vecResponseContexts[i].m_iszName.ToCStr(); + iContextValue = atoi(vecResponseContexts[i].m_iszValue.ToCStr()); + + if (!strcasecmp(szFilterContext, szContext) && iContextValue > 0) + return true; + } + + return false; + } + + // CBaseFilter::PassesFilterImpl just returns true so no need to call it + return true; +} + +// Switch new entity classnames to ones that can be instantiated while keeping the classname keyvalue intact so it can be used later +DETOUR_DECL_STATIC2(DETOUR_CreateEntityByName, CBaseEntity*, const char*, className, int, iForceEdictIndex) +{ + // Nice of valve to expose CBaseFilter as filter_base :) + if (strcasecmp(className, "filter_activator_context") == 0) + className = "filter_base"; + + return DETOUR_STATIC_CALL(DETOUR_CreateEntityByName)(className, iForceEdictIndex); +} + DETOUR_DECL_MEMBER2(DETOUR_KeyValue, bool, const char *, szKeyName, const char *, szValue) { // Fix crash bug in engine @@ -455,6 +524,22 @@ bool CSSFixes::SDK_OnLoad(char *error, size_t maxlength, bool late) return false; } + g_pDetour_CreateEntityByName = DETOUR_CREATE_STATIC(DETOUR_CreateEntityByName, "CreateEntityByName"); + if (g_pDetour_CreateEntityByName == NULL) + { + snprintf(error, maxlength, "Could not create detour for CreateEntityByName"); + SDK_OnUnload(); + return false; + } + + g_pDetour_PassesFilterImpl = DETOUR_CREATE_MEMBER(DETOUR_PassesFilterImpl, "CBaseFilter_PassesFilterImpl"); + if (g_pDetour_PassesFilterImpl == NULL) + { + snprintf(error, maxlength, "Could not create detour for CBaseFilter_PassesFilterImpl"); + SDK_OnUnload(); + return false; + } + g_pDetour_FindUseEntity = DETOUR_CREATE_MEMBER(DETOUR_FindUseEntity, "CBasePlayer_FindUseEntity"); if(g_pDetour_FindUseEntity == NULL) { @@ -497,6 +582,8 @@ bool CSSFixes::SDK_OnLoad(char *error, size_t maxlength, bool late) g_pDetour_InputTestActivator->EnableDetour(); g_pDetour_PostConstructor->EnableDetour(); + g_pDetour_CreateEntityByName->EnableDetour(); + g_pDetour_PassesFilterImpl->EnableDetour(); g_pDetour_FindUseEntity->EnableDetour(); g_pDetour_CTraceFilterSimple->EnableDetour(); g_pDetour_KeyValue->EnableDetour(); @@ -625,6 +712,18 @@ void CSSFixes::SDK_OnUnload() g_pDetour_PostConstructor = NULL; } + if (g_pDetour_CreateEntityByName != NULL) + { + g_pDetour_CreateEntityByName->Destroy(); + g_pDetour_CreateEntityByName = NULL; + } + + if (g_pDetour_PassesFilterImpl != NULL) + { + g_pDetour_PassesFilterImpl->Destroy(); + g_pDetour_PassesFilterImpl = NULL; + } + if(g_pDetour_FindUseEntity != NULL) { g_pDetour_FindUseEntity->Destroy(); diff --git a/gamedata/CSSFixes.txt b/gamedata/CSSFixes.txt index 7fa85d6..e20b7f2 100644 --- a/gamedata/CSSFixes.txt +++ b/gamedata/CSSFixes.txt @@ -15,6 +15,18 @@ "library" "server" "linux" "@_ZN11CBaseEntity15PostConstructorEPKc" } + + "CBaseFilter_PassesFilterImpl" + { + "library" "server" + "linux" "@_ZN11CBaseFilter16PassesFilterImplEP11CBaseEntityS1_" + } + + "CreateEntityByName" + { + "library" "server" + "linux" "@_Z18CreateEntityByNamePKci" + } "CBasePlayer_FindUseEntity" {