Introduction
Have you ever wanted to strip attributes on a weapon, without screwing over the client's min view models, or any other cosmetic effects (like weapon skins) ?
Then this snippet is for you! With the addition of DHooks into sourcemod I can now share my method.
The code
PHP Code:
DynamicHook g_hDHookItemIterateAttribute;
int g_iCEconItem_m_Item;
int g_iCEconItemView_m_bOnlyIterateItemViewAttributes;
static void SDK_Init()
{
Handle hConfig = new GameData("yourgamedata");
int iOffset = GameConfGetOffset(hConfig, "CEconItemView::IterateAttributes");
g_hDHookItemIterateAttribute = new DynamicHook(iOffset, HookType_Raw, ReturnType_Void, ThisPointer_Address);
if (g_hDHookItemIterateAttribute == null)
{
SetFailState("Failed to create hook CEconItemView::IterateAttributes offset from SF2 gamedata!");
}
g_hDHookItemIterateAttribute.AddParam(HookParamType_ObjectPtr);
g_iCEconItem_m_Item = FindSendPropInfo("CEconEntity", "m_Item");
FindSendPropInfo("CEconEntity", "m_bOnlyIterateItemViewAttributes", _, _, g_iCEconItemView_m_bOnlyIterateItemViewAttributes);
delete hConfig;
}
public void OnEntityCreate(int iEntity, const char[] sClassname)
// OR (As long you have a method to detect the weapon/hat/medal/etc... entity)
public void TF2Items_OnGiveNamedItem_Post(int iClient, char[] sClassname, int iItemDefIndex, int iLevel, int iQuality, int iEntity)
{
if (your special weapon detection method here)
{
Address pCEconItemView = GetEntityAddress(iEntity) + view_as<Address>(g_iCEconItem_m_Item);
g_hDHookItemIterateAttribute.HookRaw(Hook_Pre, pCEconItemView, CEconItemView_IterateAttributes);
g_hDHookItemIterateAttribute.HookRaw(Hook_Post, pCEconItemView, CEconItemView_IterateAttributes_Post);
}
}
static MRESReturn CEconItemView_IterateAttributes(Address pThis, DHookParam hParams)
{
StoreToAddress(pThis + view_as<Address>(g_iCEconItemView_m_bOnlyIterateItemViewAttributes), true, NumberType_Int8, false);
return MRES_Ignored;
}
static MRESReturn CEconItemView_IterateAttributes_Post(Address pThis, DHookParam hParams)
{
StoreToAddress(pThis + view_as<Address>(g_iCEconItemView_m_bOnlyIterateItemViewAttributes), false, NumberType_Int8, false);
return MRES_Ignored;
}
In depth explanation
Everytime the
server or
client needs to parse an item's attributes (both dynamic and static), it goes through the item's
CEconItemView (m_Item) and then calls
CEconItemView::IterateAttributes.
CEconItemView::IterateAttributes works like so : After parsing the dynamic attributes, it looks for the member property
CEconItemView::m_bOnlyIterateItemViewAttribut es.
If it's true, the server won't parse the static attributes, if it is false it will.
CEconItemView::m_bOnlyIterateItemViewAttribut es is also networked, therefore if you force set it to true on the server the client will also not parse static attributes. Which we don't want, as we need static attributes like "min view model offset" to still work on the client side.
This is where DHook comes in...
PHP Code:
g_iCEconItem_m_Item = FindSendPropInfo("CEconEntity", "m_Item");
m_Item absolute offset retrieved so we may create a
CEconItemView ptr with it, which is needed by DHook to detour the function
CEconItemView::IterateAttributes.
PHP Code:
FindSendPropInfo("CEconEntity", "m_bOnlyIterateItemViewAttributes", _, _, g_iCEconItemView_m_bOnlyIterateItemViewAttributes);
m_bOnlyIterateItemViewAttributes LOCAL offset is retrieved (relative to CEconItemView) so we can update the property value under DHook's callbacks.
The rest should now be very straight forward now.
- When
CEconItemView_IterateAttributes fires, we change
CEconItemView::m_bOnlyIterateItemViewAttribut es to true so the static attributes aren't parsed on the server.
- When
CEconItemView_IterateAttributes_Post fires, we change
CEconItemView::m_bOnlyIterateItemViewAttribut es back to false so the static attributes are still parsed on the client (remember, it's a networked property).
And voilą! You now have successfully created an item whose static attributes will be ignored by the server but not the client.
Where to find gamedata ?
Use IDA, or Asherkin's very useful tool
vtable.
Look for
CEconItemView::IterateAttributes and you're set.
Edit: This snippet is also technically valid on other source games that have items, but you will have to dig a bit to ensure the logic of attributes iteration I described above is valid there as well.
Raw hooks are also not cleaned upon entity deletion, so don't forget to store the return value of
DynamicHook.HookRaw and remove the hooks when entity is deleted.
__________________