Fix output hooks when caller/activator are flipped (#1411)
Co-authored-by: Asher Baker <asherkin@limetech.io>
This commit is contained in:
parent
4d6b9895d3
commit
54364d213d
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <sourcemod>
|
||||
#include <sdktools>
|
||||
|
||||
public Plugin:myinfo =
|
||||
public Plugin myinfo =
|
||||
{
|
||||
name = "Entity Output Hook Testing",
|
||||
author = "AlliedModders LLC",
|
||||
@ -10,32 +10,70 @@ public Plugin:myinfo =
|
||||
url = "http://www.sourcemod.net/"
|
||||
};
|
||||
|
||||
public OnPluginStart()
|
||||
public void OnPluginStart()
|
||||
{
|
||||
HookEntityOutput("point_spotlight", "OnLightOn", OutputHook);
|
||||
HookEntityOutput("point_spotlight", "OnLightOff", OutputHook);
|
||||
|
||||
HookEntityOutput("func_door", "OnOpen", OutputHook);
|
||||
HookEntityOutput("func_door_rotating", "OnOpen", OutputHook);
|
||||
HookEntityOutput("prop_door_rotating", "OnOpen", OutputHook);
|
||||
HookEntityOutput("func_door", "OnClose", OutputHook);
|
||||
HookEntityOutput("func_door_rotating", "OnClose", OutputHook);
|
||||
HookEntityOutput("prop_door_rotating", "OnClose", OutputHook);
|
||||
|
||||
if (GetEngineVersion() == Engine_CSGO) {
|
||||
// The server library calls with output names from Activator (from "plated_c4" activator entity in current example).
|
||||
HookEntityOutput("planted_c4", "OnBombBeginDefuse", OutputHook);
|
||||
HookEntityOutput("planted_c4", "OnBombDefuseAborted", OutputHook);
|
||||
|
||||
// Never fired for planted_c4, only planted_c4_training.
|
||||
HookEntityOutput("planted_c4", "OnBombDefused", OutputHook);
|
||||
}
|
||||
}
|
||||
|
||||
public OutputHook(const String:name[], caller, activator, Float:delay)
|
||||
public void OutputHook(const char[] name, int caller, int activator, float delay)
|
||||
{
|
||||
LogMessage("[ENTOUTPUT] %s", name);
|
||||
char callerClassname[64];
|
||||
if (caller >= 0 && IsValidEntity(caller)) {
|
||||
GetEntityClassname(caller, callerClassname, sizeof(callerClassname));
|
||||
}
|
||||
|
||||
char activatorClassname[64];
|
||||
if (activator >= 0 && IsValidEntity(activator)) {
|
||||
GetEntityClassname(activator, activatorClassname, sizeof(activatorClassname));
|
||||
}
|
||||
|
||||
LogMessage("[ENTOUTPUT] %s (caller: %d/%s, activator: %d/%s)", name, caller, callerClassname, activator, activatorClassname);
|
||||
}
|
||||
|
||||
public OnMapStart()
|
||||
public void OnMapStart()
|
||||
{
|
||||
new ent = FindEntityByClassname(-1, "point_spotlight");
|
||||
int ent = FindEntityByClassname(-1, "point_spotlight");
|
||||
|
||||
if (ent == -1)
|
||||
{
|
||||
LogError("Could not find a point_spotlight");
|
||||
LogMessage("[ENTOUTPUT] Could not find a point_spotlight");
|
||||
ent = CreateEntityByName("point_spotlight");
|
||||
DispatchSpawn(ent);
|
||||
}
|
||||
|
||||
LogMessage("[ENTOUTPUT] Begin basic");
|
||||
|
||||
AcceptEntityInput(ent, "LightOff");
|
||||
AcceptEntityInput(ent, "LightOn");
|
||||
|
||||
AcceptEntityInput(ent, "LightOff", .caller = ent);
|
||||
AcceptEntityInput(ent, "LightOn", .caller = ent);
|
||||
|
||||
AcceptEntityInput(ent, "LightOff", .activator = ent);
|
||||
AcceptEntityInput(ent, "LightOn", .activator = ent);
|
||||
|
||||
AcceptEntityInput(ent, "LightOff", ent, ent);
|
||||
AcceptEntityInput(ent, "LightOn", ent, ent);
|
||||
|
||||
LogMessage("[ENTOUTPUT] End basic, begin once");
|
||||
|
||||
HookSingleEntityOutput(ent, "OnLightOn", OutputHook, true);
|
||||
HookSingleEntityOutput(ent, "OnLightOff", OutputHook, true);
|
||||
|
||||
@ -44,16 +82,21 @@ public OnMapStart()
|
||||
|
||||
AcceptEntityInput(ent, "LightOff", ent, ent);
|
||||
AcceptEntityInput(ent, "LightOn", ent, ent);
|
||||
|
||||
LogMessage("[ENTOUTPUT] End once, begin single");
|
||||
|
||||
HookSingleEntityOutput(ent, "OnLightOn", OutputHook, false);
|
||||
HookSingleEntityOutput(ent, "OnLightOff", OutputHook, false);
|
||||
|
||||
AcceptEntityInput(ent, "LightOff", ent, ent);
|
||||
AcceptEntityInput(ent, "LightOn", ent, ent);
|
||||
|
||||
AcceptEntityInput(ent, "LightOff", ent, ent);
|
||||
AcceptEntityInput(ent, "LightOn", ent, ent);
|
||||
|
||||
//Comment these out (and reload the plugin heaps) to test for leaks on plugin unload
|
||||
// Comment these out (and reload the plugin heaps) to test for leaks on plugin unload
|
||||
UnhookSingleEntityOutput(ent, "OnLightOn", OutputHook);
|
||||
UnhookSingleEntityOutput(ent, "OnLightOff", OutputHook);
|
||||
|
||||
LogMessage("[ENTOUTPUT] End single");
|
||||
}
|
Loading…
Reference in New Issue
Block a user