I'm trying to implement TF2 Attributes in Metamod -- I know you'd probably think why not just do it in SourceMod, well my plugin is huge and I'm not really going to be translating it all into sourcemod any time soon. It's in Metamod now and I've struggled to get this to work on my own.
The main problems I have are:
1) As far as I know the signature function is being called correctly using __asm, however it will still occassionaly crash especially if I call it twice with the same attribute.
I've tried the following. strAttrib = "max health additive bonus" for example, flVal = 100 or 200. It will always be OK the first time. Then the second time it will crash.
2) Secondly I can't seem to call the ClearCache function because my method of getting the vtable might be incorrect?
3) There is no update to the player on the hud althought this might just be caused by the inability to access ClearCache ?
Code:
if (TF2_SetAttrib(pEdict, strAttrib, flVal))
CBotGlobals::botMessage(NULL, 0, "OK");
else
CBotGlobals::botMessage(NULL, 0, "FAIL");
this is translated from sourcemod into metamod and my own plugin
Code:
// TF2 Attributes - Flamin Sarge
bool TF2_SetAttrib(edict_t *pedict, const char *strAttrib, float flVal)
{
//CBaseEntity *pEntity;
if (!pedict || pedict->IsFree())
return false;
CAttributeList *pList = CClassInterface::getAttributeList(pedict);
CEconItemSchema *pSchema = g_pGetEconItemSchema->callme();
if (pSchema == NULL)
return false;
CEconItemAttributeDefinition *pAttribDef = g_pGetAttributeDefinitionByName->callme(pSchema, strAttrib);
if ( (unsigned int)pAttribDef < 0x10000)
{
return false;
}
bool bSuccess = g_pSetRuntimeAttributeValue->callme(pedict, pList, pAttribDef, flVal);
return bSuccess;
}
this calls the signature function
Code:
bool CSetRuntimeAttributeValue::callme(edict_t *pEnt, CAttributeList *list, CEconItemAttributeDefinition *attrib,float value)
{
int bret = 0;
void *thefunc = m_func;
int iEntityIndex = ENTINDEX(pEnt);
if ( list && attrib && thefunc )
{
#ifdef _WIN32
__asm
{
mov ecx, list;
push attrib;
push value;
call thefunc;
mov bret, eax;
};
#else
FUNC_SET_ATTRIB_VALUE func = (FUNC_SET_ATTRIB_VALUE)thefunc;
bret = func(list,attrib,value);
#endif
}
return (bret==1) || ((bret & 0x1FFF) == ((iEntityIndex + 4) * 4));
}
I get the signature at startup when I create CSetRuntimeAttributeValue instance
set_attribute_value_win and set_attribute_value_linux are just values I use in a config file to find updated signatures
Code:
CSetRuntimeAttributeValue::CSetRuntimeAttributeValue ( CRCBotKeyValueList *list, void *pAddrBase )
{
#ifdef _WIN32
findFunc(list,"set_attribute_value_win",pAddrBase,"\\x55\\x8B\\xEC\\x83\\xEC\\x14\\x33\\xD2\\x53\\x8B\\xD9\\x56\\x57\\x8B\\x73\\x10\\x85\\xF6");
#else
findFunc(list,"set_attribute_value_linux",pAddrBase,"@_ZN14CAttributeList24SetRuntimeAttributeValueEPK28CEconItemAttributeDefinitionf");
#endif
}
void CSignatureFunction :: findFunc ( CRCBotKeyValueList *kv, const char*pKey, void *pAddrBase, const char *defaultsig )
{
char *sig = NULL;
if ( kv->getString(pKey,&sig) && sig )
m_func = findSignature(pAddrBase,sig);
else
m_func = findSignature(pAddrBase,defaultsig);
}
I've tried Clear Cache but the function is always NULL
Code:
void (CAttributeManager::*OnAttributeValuesChanged)(void) = 0x0;
/*
"CAttributeManager::OnAttributeValuesChanged" //use instead of ClearCache/NotifyManagerOfAttributeValueChanges
{
"windows" "12"
"linux" "13"
"mac" "13"
}
*/
bool RCBotPluginMeta::TF2_ClearAttributeCache(edict_t *pEdict)
{
CAttributeList *pList = CClassInterface::getAttributeList(pEdict);
CAttributeManager *pManager = (CAttributeManager*)(((unsigned long)pList) + 24);
if (!pManager)
return false;
unsigned int *mem = (unsigned int*)*(unsigned int*)pManager; // Get the vtable into mem
if (!mem)
return false;
int offset = 12; // From windows offset for now
*(unsigned int*)&OnAttributeValuesChanged = mem[offset];
if (!OnAttributeValuesChanged)
return false;
// NEVER REACHES HERE!!!
(*pManager.*OnAttributeValuesChanged)();
return true;
}
__________________