Fix output hooks when caller/activator are flipped (#1411)

Co-authored-by: Asher Baker <[email protected]>
This commit is contained in:
Vladimir
2021-07-17 15:30:09 +01:00
committed by GitHub
co-authored by Asher Baker
parent 4d6b9895d3
commit 54364d213d
3 changed files with 103 additions and 19 deletions
+51 -10
View File
@@ -121,14 +121,10 @@ bool EntityOutputManager::FireEventDetour(void *pOutput, CBaseEntity *pActivator
// attempt to directly lookup a hook using the pOutput pointer
OutputNameStruct *pOutputName = NULL;
const char *classname = gamehelpers->GetEntityClassname(pCaller);
if (!classname)
{
return true;
}
const char *classname;
const char *outputname = FindOutputName(pOutput, pActivator, pCaller, &classname);
const char *outputname = FindOutputName(pOutput, pCaller);
if (!outputname)
if (!outputname || !classname)
{
return true;
}
@@ -345,8 +341,15 @@ OutputNameStruct *EntityOutputManager::FindOutputPointer(const char *classname,
return pOutputName;
}
// Iterate the datamap of pCaller and look for output pointers with the same address as pOutput
const char *EntityOutputManager::FindOutputName(void *pOutput, CBaseEntity *pCaller)
// Iterate the datamap of pCaller/pActivator and look for output pointers with the same address as pOutput.
// Store the classname of the entity we found the output on in |entity_classname| if provided.
//
// TODO: It turns out this logic isn't very sane, and it relies heavily on convention how most entities call
// FireOutput rather than explicitly conforming to the design of the engine's output system. We need a
// big refactor here to lookup the underlying per-entity CBaseEntityOutput instances and introduce an
// explicit concept of the entity owning the output being triggered, rather than assuming it is also at
// least one of the caller or activator entity.
const char *EntityOutputManager::FindOutputName(void *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, const char **entity_classname)
{
datamap_t *pMap = gamehelpers->GetDataMap(pCaller);
@@ -358,6 +361,11 @@ const char *EntityOutputManager::FindOutputName(void *pOutput, CBaseEntity *pCal
{
if ((char *)pCaller + GetTypeDescOffs(&pMap->dataDesc[i]) == pOutput)
{
if (entity_classname)
{
*entity_classname = gamehelpers->GetEntityClassname(pCaller);
}
return pMap->dataDesc[i].externalName;
}
}
@@ -365,5 +373,38 @@ const char *EntityOutputManager::FindOutputName(void *pOutput, CBaseEntity *pCal
pMap = pMap->baseMap;
}
// HACK: Generally, the game passes the entity that triggered the output as pCaller, but occasionally (because the
// param order is confusing), the entity gets passed in as pActivator instead. We do a 2nd pass over
// pActivator looking for the output if we couldn't find it on pCaller.
if (pActivator)
{
pMap = gamehelpers->GetDataMap(pActivator);
while (pMap)
{
for (int i=0; i<pMap->dataNumFields; i++)
{
if (pMap->dataDesc[i].flags & FTYPEDESC_OUTPUT)
{
if ((char *)pActivator + GetTypeDescOffs(&pMap->dataDesc[i]) == pOutput)
{
if (entity_classname)
{
*entity_classname = gamehelpers->GetEntityClassname(pActivator);
}
return pMap->dataDesc[i].externalName;
}
}
}
pMap = pMap->baseMap;
}
}
if(entity_classname)
{
*entity_classname = nullptr;
}
return NULL;
}
}
+1 -1
View File
@@ -119,7 +119,7 @@ private:
bool CreateFireEventDetour();
void DeleteFireEventDetour();
const char *FindOutputName(void *pOutput, CBaseEntity *pCaller);
const char *FindOutputName(void *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, const char **entity_classname);
// Maps classname to a ClassNameStruct
IBasicTrie *ClassNames;