Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
FlaminSarge committed Sep 18, 2022
2 parents dbb8456 + 735f074 commit 69dba72
Show file tree
Hide file tree
Showing 5 changed files with 1,359 additions and 586 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/spcomp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ jobs:
- name: Setup SourcePawn Compiler
uses: rumblefrog/setup-sp@master
with:
version: '1.10.x'
version: '1.11.x'
- name: Compile tf2attributes
run: spcomp tf2attributes.sp
run: spcomp scripting/tf2attributes.sp
- name: Compile example
run: spcomp -i. tf2attributes_example.sp
run: spcomp -iscripting/include scripting/tf2attributes_example.sp
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,49 @@
# tf2attributes

TF2Attributes SourceMod plugin

https://forums.alliedmods.net/showthread.php?t=210221

## Now featuring the following functionality from [nosoop/tf2attributes](https://github.com/nosoop/tf2attributes)

Add / remove temporary attributes on the player (using the game's own time-based expiry
mechanism for it).

```sourcepawn
// replicates the temporary health bonus granted by the Dalokohs Bar:
TF2Attrib_AddCustomPlayerAttribute(client, "hidden maxhealth non buffed", 50.0, 30.0);
```

Adds the game's "attribute hook" mechanism that collates values using an attribute class:

```sourcepawn
// computes the final damage multiplier based on the given item and owner's attributes:
float damageBonus = TF2Attrib_HookValueFloat(1.0, "mult_dmg", weapon);
```

Support for setting / getting attribute values via strings:

```sourcepawn
// set an entity's custom projectile model:
TF2Attrib_SetFromStringValue(entity, "custom projectile model", "models/weapons/c_models/c_grenadelauncher/c_grenadelauncher.mdl");
// get the name from an item:
TF2Attrib_HookValueString("NO NAME", "custom_name_attr", entity, buffer, sizeof(buffer));
```

Setting custom names / descriptions is not possible. String values that are set by this plugin
are not replicated to the client — this is fine for attributes that are only accessed on
the server, but if you set any that the client will read, the client will crash on access.

## Installing or updating to 1.7

All plugins compiled for previous versions should continue to work with this one.
The installation instructions remain the same as before

1. Download all the non-source code files in [the latest release][].
2. Copy `tf2attributes.smx` to `addons/sourcemod/plugins/`.
3. Copy `tf2.attributes.txt` to `addons/sourcemod/gamedata/`.
4. If you're a developer, copy `tf2attributes.inc` to `addons/sourcemod/scripting/include/`
(or the appropriate path for your compiler toolchain / project).

[the latest release]: https://github.com/flaminsarge/tf2attributes/releases
74 changes: 74 additions & 0 deletions gamedata/tf2.attributes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,32 @@
"linux" "14"
"mac" "14"
}
"CAttributeManager::ApplyAttributeStringWrapper"
{
// linux uses a signature
"windows" "15"
}
"ISchemaAttributeTypeBase::BConvertStringToEconAttributeValue"
{
"windows" "4"
"linux" "5"
}
"ISchemaAttributeTypeBase::InitializeNewEconAttributeValue"
{
"windows" "7"
"linux" "8"
}
"ISchemaAttributeTypeBase::UnloadEconAttributeValue"
{
"windows" "8"
"linux" "9"
}
"ISchemaAttributeTypeBase::BSupportsGame..."
{
// "ISchemaAttributeTypeBase::BSupportsGameplayModificationAndNetworking()"
"windows" "10"
"linux" "11"
}
}
"Signatures"
{
Expand Down Expand Up @@ -77,6 +103,54 @@
"linux" "@_ZN14CAttributeList20DestroyAllAttributesEv"
"mac" "@_ZN14CAttributeList20DestroyAllAttributesEv"
}
"CAttributeManager::AttribHookValue<float>"
{
// (float value, string_t attrClass, CBaseEntity* ent, CUtlVector<CBaseEntity*> *reentrant, bool const_str)
// called in unique x-ref to "ubercharge_ammo" on Windows
"library" "server"
"linux" "@_ZN17CAttributeManager15AttribHookValueIfEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb"
"windows" "\x55\x8B\xEC\x83\xEC\x0C\x8B\x0D\x2A\x2A\x2A\x2A\x53\x56\x57\x33\xF6\x33\xFF\x89\x75\xF4\x89\x7D\xF8\x8B\x41\x08\x85\xC0\x74\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x6A\x6B"
}
"CAttributeManager::AttribHookValue<int>"
{
// (int value, string_t attrClass, CBaseEntity* ent, CUtlVector<CBaseEntity*> *reentrant, bool const_str)
// called in unique x-ref to "mod_max_primary_clip_override" on Windows
"library" "server"
"linux" "@_ZN17CAttributeManager15AttribHookValueIiEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb"
"windows" "\x55\x8B\xEC\x83\xEC\x10\x8B\x0D\x2A\x2A\x2A\x2A\x53\x56\x57\x33\xFF\x33\xDB\x89\x7D\xF0\x89\x5D\xF4\x8B\x41\x08\x85\xC0\x74\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x68\x2A\x2A\x2A\x2A\x6A\x6B"
}
"CAttributeManager::ApplyAttributeStringWrapper"
{
// uses a hidden pointer, which ends up looking something like this monstrosity:
// (string_t* returnValue, CAttributeManager* this, string_t input, CBaseEntity* initiator, string_t classname, CUtlVector<CBaseEntity*>* entityList), returns string_t
// windows uses a (mostly) standard calling convention so we use the vtable call for that
"library" "server"
"linux" "@_ZN17CAttributeManager27ApplyAttributeStringWrapperE8string_tP11CBaseEntityS0_P10CUtlVectorIS2_10CUtlMemoryIS2_iEE"
}
"CTFPlayer::AddCustomAttribute" //(const char*, float, float), returns void
{
"library" "server"
"windows" "\x55\x8B\xEC\xF3\x0F\x10\x4D\x10\x83\xEC\x10"
"linux" "@_ZN9CTFPlayer18AddCustomAttributeEPKcff"
"mac" "@_ZN9CTFPlayer18AddCustomAttributeEPKcff"
}
"CTFPlayer::RemoveCustomAttribute" //(const char*), returns void
{
// called with x-ref string "hidden maxhealth non buffed"
"library" "server"
"windows" "\x55\x8B\xEC\x83\xEC\x10\x53\x56\x57\xFF\x75\x08"
"linux" "@_ZN9CTFPlayer21RemoveCustomAttributeEPKc"
"mac" "@_ZN9CTFPlayer21RemoveCustomAttributeEPKc"
}
"CopyStringAttributeValueToCharPointerOutput" //(CAttribute_String*, char**), returns void
{
// called from CAttributeIterator_GetTypedAttributeValue<CAttribute_String, char const*>::OnIterateAttributeValue
// which on Windows has a unique bytesig `55 8B EC 56 8B F1 8B 46 04 3B 45 08 75 ? FF 76 08`
"library" "server"
"windows" "\x55\x8B\xEC\x8B\x45\x08\x8B\x48\x10"
"linux" "@_Z43CopyStringAttributeValueToCharPointerOutputPK17CAttribute_StringPPKc"
"mac" "@_Z43CopyStringAttributeValueToCharPointerOutputPK17CAttribute_StringPPKc"
}
}
}
}
166 changes: 106 additions & 60 deletions scripting/include/tf2attributes.inc
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* @return True if the attribute was added successfully, false if entity does not have m_AttributeList.
* @error Invalid entity index or attribute name passed.
*/
native bool TF2Attrib_SetByName(int iEntity, char[] strAttrib, float flValue);
native bool TF2Attrib_SetByName(int iEntity, const char[] strAttrib, float flValue);

/**
* Sets an attribute's value on an entity, adding it if it isn't on the entity.
Expand All @@ -27,6 +27,23 @@ native bool TF2Attrib_SetByName(int iEntity, char[] strAttrib, float flValue);
*/
native bool TF2Attrib_SetByDefIndex(int iEntity, int iDefIndex, float flValue);

/**
* Parses the attribute name and value strings and applies it on the entity. This parses
* numeric and string attributes.
*
* If you use this on a non-numeric attribute, make sure that only the server reads off of that
* attribute. Non-primitive values aren't replicated correctly between the client and the
* server; the client will read garbage and may crash!
*
* @param iEntity Entity index to set the attribute on. Must have m_AttributeList.
* @param strAttrib Name of the attribute, as from the "name" key in items_game.
* @param strValue Value to set the attribute to.
*
* @return True if the attribute was added successfully, false if the attribute name was invalid.
* @error Invalid entity index or entity does not have m_AttributeList.
*/
native bool TF2Attrib_SetFromStringValue(int iEntity, const char[] strAttrib, const char[] strValue);

/**
* Returns the address of an attribute on an entity.
*
Expand All @@ -36,7 +53,7 @@ native bool TF2Attrib_SetByDefIndex(int iEntity, int iDefIndex, float flValue);
* @return Address of the attribute on the entity, or Address_Null if the attribute does not exist on the entity.
* @error Invalid entity index or attribute name passed.
*/
native Address TF2Attrib_GetByName(int iEntity, char[] strAttrib);
native Address TF2Attrib_GetByName(int iEntity, const char[] strAttrib);

/**
* Returns the address of an attribute (by attribute index) on an entity.
Expand All @@ -58,7 +75,7 @@ native Address TF2Attrib_GetByDefIndex(int iEntity, int iDefIndex);
* @return True if the SDKCall was made, false if entity had invalid address or m_AttributeList missing.
* @error Invalid entity index or attribute name passed.
*/
native bool TF2Attrib_RemoveByName(int iEntity, char[] strAttrib);
native bool TF2Attrib_RemoveByName(int iEntity, const char[] strAttrib);

/**
* Removes an attribute from an entity.
Expand Down Expand Up @@ -134,6 +151,20 @@ native void TF2Attrib_SetValue(Address pAttrib, float flValue);
*/
native float TF2Attrib_GetValue(Address pAttrib);

/**
* Returns the string data from its raw value representation (a CAttribute_String instance).
*
* WARNING: This dereferences the input value! Feeding it values that aren't CAttribute_String pointers will result in unexpected behavior, potentially crashing the server.
* In the case where you only want the currently active value, use TF2Attrib_HookValueString instead.
*
* @param pRawValue Raw attribute value. You can get this value with either TF2Attrib_GetValue, TF2Attrib_GetSOCAttribs, or TF2Attrib_GetStaticAttribs.
* @param buffer Buffer to store the resulting string to.
* @param maxlen Maximum length of the buffer.
*
* @return Number of bytes written.
*/
native int TF2Attrib_UnsafeGetStringValue(any pRawValue, char[] buffer, int maxlen);

/**
* Sets the value of m_nRefundableCurrency on an attribute.
*
Expand Down Expand Up @@ -202,6 +233,72 @@ native int TF2Attrib_GetSOCAttribs(int iEntity, int[] iAttribIndices, float[] fl
*/
native bool TF2Attrib_IsIntegerValue(int iDefIndex);

/**
* Returns true if an attribute with the specified name exists.
*
* @param strAttrib Name of the attribute, as from the "name" key in items_game.
*
* @return True if the attribute exists, false otherwise.
*/
native bool TF2Attrib_IsValidAttributeName(const char[] strAttrib);

/**
* Adds a custom, potentially temporary attribute to a player.
*
* @param client Client index to set the attribute on.
* @param strAttrib Name of the attribute, as from the "name" key in items_game.
* @param flValue Value to set m_flValue to.
* @param flDuration Duration of the attribute. If less than 0, the attribute will not be automatically removed.
*
* @noreturn
*/
native void TF2Attrib_AddCustomPlayerAttribute(int client, const char[] strAttrib, float flValue, float flDuration = -1.0);

/**
* Removes a previously applied custom attribute on a player.
*
* @param client Client index to remove the attribute from.
* @param strAttrib Name of the attribute, as from the "name" key in items_game.
*
* @noreturn
*/
native void TF2Attrib_RemoveCustomPlayerAttribute(int client, const char[] strAttrib);

/**
* Applies a transformation to the given initial value, following the rules according to the given attribute class.
*
* @param flInitial Initial float value.
* @param attrClass The attribute class, as from the "attribute_class" key in items_game.
* @param iEntity The entity that should be checked. Checking players also checks their equipped items.
*
* @return Transformed initial value.
*/
native float TF2Attrib_HookValueFloat(float flInitial, const char[] attrClass, int iEntity);

/**
* Applies a transformation to the given initial value, following the rules according to the given attribute class.
*
* @param nInitial Initial integer value.
* @param attrClass The attribute class, as from the "attribute_class" key in items_game.
* @param iEntity The entity that should be checked. Checking players also checks their equipped items.
*
* @return Transformed initial value.
*/
native int TF2Attrib_HookValueInt(int nInitial, const char[] attrClass, int iEntity);

/**
* Applies a transformation to the given initial value, following the rules according to the given attribute class.
*
* @param initial Initial string value. (This appears to only be returned if the entity doesn't have the attribute.)
* @param attrClass The attribute class, as from the "attribute_class" key in items_game.
* @param iEntity The entity that should be checked. Checking players also checks their equipped items.
* @param buffer Transformed initial value.
* @param maxlen Buffer size.
*
* @return Number of bytes written.
*/
native int TF2Attrib_HookValueString(const char[] initial, const char[] attrClass, int iEntity, char[] buffer, int maxlen);

/**
* Gets whether the plugin loaded without ANY errors.
* For the purpose of allowing dependencies to ignore the plugin if this returns false.
Expand Down Expand Up @@ -244,63 +341,12 @@ public void __pl_tf2attributes_SetNTVOptional()
MarkNativeAsOptional("TF2Attrib_GetSOCAttribs");
MarkNativeAsOptional("TF2Attrib_ListDefIndices");
MarkNativeAsOptional("TF2Attrib_IsIntegerValue");
MarkNativeAsOptional("TF2Attrib_IsValidAttributeName");
MarkNativeAsOptional("TF2Attrib_AddCustomPlayerAttribute");
MarkNativeAsOptional("TF2Attrib_RemoveCustomPlayerAttribute");
MarkNativeAsOptional("TF2Attrib_HookValueFloat");
MarkNativeAsOptional("TF2Attrib_HookValueInt");

MarkNativeAsOptional("TF2Attrib_IsReady");

// MarkNativeAsOptional("TF2Attrib_SetInitialValue");
// MarkNativeAsOptional("TF2Attrib_GetInitialValue");
// MarkNativeAsOptional("TF2Attrib_SetIsSetBonus");
// MarkNativeAsOptional("TF2Attrib_GetIsSetBonus");
}
#endif

//OLD things lie here
//flInitialValue and bSetBonus don't exist anymore
/**
* Sets the value of m_flInitialValue on an attribute.
*
* @param pAttrib Address of the attribute.
* @param flValue Value to set m_flInitialValue to.
*
* @noreturn
*/
//native TF2Attrib_SetInitialValue(Address pAttrib, float flValue);

/**
* Returns the value of m_flInitialValue on an attribute.
*
* @param pAttrib Address of the attribute.
*
* @return The floating point value of m_flInitialValue on the attribute.
*/
//native float TF2Attrib_GetInitialValue(Address pAttrib);

/**
* Sets the boolean value of m_bSetBonus on an attribute.
*
* @param pAttrib Address of the attribute.
* @param bSetBonus Value to set m_bSetBonus to.
*
* @noreturn
*/
//native TF2Attrib_SetIsSetBonus(Address pAttrib, bool bSetBonus);

/**
* Returns the boolean value of m_bSetBonus on an attribute.
*
* @param pAttrib Address of the attribute.
*
* @return The boolean value of m_bSetBonus on the attribute.
*/
//native bool TF2Attrib_GetIsSetBonus(Address pAttrib);

//stock TF2Attrib_IsIntegerValue(iDefIndex)
//{
// switch (iDefIndex)
// {
// case 133, 143, 147, 152, 184, 185, 186, 192, 193, 194, 198, 211, 214, 227, 228, 229, 262, 294, 302, 372, 373, 374, 379, 381, 383, 403, 420:
// {
// return true;
// }
// }
// return false;
//}
Loading

0 comments on commit 69dba72

Please sign in to comment.