Skip to content

Commit 69dba72

Browse files
committed
Merge branch 'master' of https://github.com/nosoop/tf2attributes
2 parents dbb8456 + 735f074 commit 69dba72

File tree

5 files changed

+1359
-586
lines changed

5 files changed

+1359
-586
lines changed

.github/workflows/spcomp.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ jobs:
1010
- name: Setup SourcePawn Compiler
1111
uses: rumblefrog/setup-sp@master
1212
with:
13-
version: '1.10.x'
13+
version: '1.11.x'
1414
- name: Compile tf2attributes
15-
run: spcomp tf2attributes.sp
15+
run: spcomp scripting/tf2attributes.sp
1616
- name: Compile example
17-
run: spcomp -i. tf2attributes_example.sp
17+
run: spcomp -iscripting/include scripting/tf2attributes_example.sp

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,49 @@
11
# tf2attributes
2+
23
TF2Attributes SourceMod plugin
34

45
https://forums.alliedmods.net/showthread.php?t=210221
6+
7+
## Now featuring the following functionality from [nosoop/tf2attributes](https://github.com/nosoop/tf2attributes)
8+
9+
Add / remove temporary attributes on the player (using the game's own time-based expiry
10+
mechanism for it).
11+
12+
```sourcepawn
13+
// replicates the temporary health bonus granted by the Dalokohs Bar:
14+
TF2Attrib_AddCustomPlayerAttribute(client, "hidden maxhealth non buffed", 50.0, 30.0);
15+
```
16+
17+
Adds the game's "attribute hook" mechanism that collates values using an attribute class:
18+
19+
```sourcepawn
20+
// computes the final damage multiplier based on the given item and owner's attributes:
21+
float damageBonus = TF2Attrib_HookValueFloat(1.0, "mult_dmg", weapon);
22+
```
23+
24+
Support for setting / getting attribute values via strings:
25+
26+
```sourcepawn
27+
// set an entity's custom projectile model:
28+
TF2Attrib_SetFromStringValue(entity, "custom projectile model", "models/weapons/c_models/c_grenadelauncher/c_grenadelauncher.mdl");
29+
30+
// get the name from an item:
31+
TF2Attrib_HookValueString("NO NAME", "custom_name_attr", entity, buffer, sizeof(buffer));
32+
```
33+
34+
Setting custom names / descriptions is not possible. String values that are set by this plugin
35+
are not replicated to the client — this is fine for attributes that are only accessed on
36+
the server, but if you set any that the client will read, the client will crash on access.
37+
38+
## Installing or updating to 1.7
39+
40+
All plugins compiled for previous versions should continue to work with this one.
41+
The installation instructions remain the same as before
42+
43+
1. Download all the non-source code files in [the latest release][].
44+
2. Copy `tf2attributes.smx` to `addons/sourcemod/plugins/`.
45+
3. Copy `tf2.attributes.txt` to `addons/sourcemod/gamedata/`.
46+
4. If you're a developer, copy `tf2attributes.inc` to `addons/sourcemod/scripting/include/`
47+
(or the appropriate path for your compiler toolchain / project).
48+
49+
[the latest release]: https://github.com/flaminsarge/tf2attributes/releases

gamedata/tf2.attributes.txt

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,32 @@
1111
"linux" "14"
1212
"mac" "14"
1313
}
14+
"CAttributeManager::ApplyAttributeStringWrapper"
15+
{
16+
// linux uses a signature
17+
"windows" "15"
18+
}
19+
"ISchemaAttributeTypeBase::BConvertStringToEconAttributeValue"
20+
{
21+
"windows" "4"
22+
"linux" "5"
23+
}
24+
"ISchemaAttributeTypeBase::InitializeNewEconAttributeValue"
25+
{
26+
"windows" "7"
27+
"linux" "8"
28+
}
29+
"ISchemaAttributeTypeBase::UnloadEconAttributeValue"
30+
{
31+
"windows" "8"
32+
"linux" "9"
33+
}
34+
"ISchemaAttributeTypeBase::BSupportsGame..."
35+
{
36+
// "ISchemaAttributeTypeBase::BSupportsGameplayModificationAndNetworking()"
37+
"windows" "10"
38+
"linux" "11"
39+
}
1440
}
1541
"Signatures"
1642
{
@@ -77,6 +103,54 @@
77103
"linux" "@_ZN14CAttributeList20DestroyAllAttributesEv"
78104
"mac" "@_ZN14CAttributeList20DestroyAllAttributesEv"
79105
}
106+
"CAttributeManager::AttribHookValue<float>"
107+
{
108+
// (float value, string_t attrClass, CBaseEntity* ent, CUtlVector<CBaseEntity*> *reentrant, bool const_str)
109+
// called in unique x-ref to "ubercharge_ammo" on Windows
110+
"library" "server"
111+
"linux" "@_ZN17CAttributeManager15AttribHookValueIfEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb"
112+
"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"
113+
}
114+
"CAttributeManager::AttribHookValue<int>"
115+
{
116+
// (int value, string_t attrClass, CBaseEntity* ent, CUtlVector<CBaseEntity*> *reentrant, bool const_str)
117+
// called in unique x-ref to "mod_max_primary_clip_override" on Windows
118+
"library" "server"
119+
"linux" "@_ZN17CAttributeManager15AttribHookValueIiEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb"
120+
"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"
121+
}
122+
"CAttributeManager::ApplyAttributeStringWrapper"
123+
{
124+
// uses a hidden pointer, which ends up looking something like this monstrosity:
125+
// (string_t* returnValue, CAttributeManager* this, string_t input, CBaseEntity* initiator, string_t classname, CUtlVector<CBaseEntity*>* entityList), returns string_t
126+
// windows uses a (mostly) standard calling convention so we use the vtable call for that
127+
"library" "server"
128+
"linux" "@_ZN17CAttributeManager27ApplyAttributeStringWrapperE8string_tP11CBaseEntityS0_P10CUtlVectorIS2_10CUtlMemoryIS2_iEE"
129+
}
130+
"CTFPlayer::AddCustomAttribute" //(const char*, float, float), returns void
131+
{
132+
"library" "server"
133+
"windows" "\x55\x8B\xEC\xF3\x0F\x10\x4D\x10\x83\xEC\x10"
134+
"linux" "@_ZN9CTFPlayer18AddCustomAttributeEPKcff"
135+
"mac" "@_ZN9CTFPlayer18AddCustomAttributeEPKcff"
136+
}
137+
"CTFPlayer::RemoveCustomAttribute" //(const char*), returns void
138+
{
139+
// called with x-ref string "hidden maxhealth non buffed"
140+
"library" "server"
141+
"windows" "\x55\x8B\xEC\x83\xEC\x10\x53\x56\x57\xFF\x75\x08"
142+
"linux" "@_ZN9CTFPlayer21RemoveCustomAttributeEPKc"
143+
"mac" "@_ZN9CTFPlayer21RemoveCustomAttributeEPKc"
144+
}
145+
"CopyStringAttributeValueToCharPointerOutput" //(CAttribute_String*, char**), returns void
146+
{
147+
// called from CAttributeIterator_GetTypedAttributeValue<CAttribute_String, char const*>::OnIterateAttributeValue
148+
// which on Windows has a unique bytesig `55 8B EC 56 8B F1 8B 46 04 3B 45 08 75 ? FF 76 08`
149+
"library" "server"
150+
"windows" "\x55\x8B\xEC\x8B\x45\x08\x8B\x48\x10"
151+
"linux" "@_Z43CopyStringAttributeValueToCharPointerOutputPK17CAttribute_StringPPKc"
152+
"mac" "@_Z43CopyStringAttributeValueToCharPointerOutputPK17CAttribute_StringPPKc"
153+
}
80154
}
81155
}
82156
}

scripting/include/tf2attributes.inc

Lines changed: 106 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* @return True if the attribute was added successfully, false if entity does not have m_AttributeList.
1414
* @error Invalid entity index or attribute name passed.
1515
*/
16-
native bool TF2Attrib_SetByName(int iEntity, char[] strAttrib, float flValue);
16+
native bool TF2Attrib_SetByName(int iEntity, const char[] strAttrib, float flValue);
1717

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

30+
/**
31+
* Parses the attribute name and value strings and applies it on the entity. This parses
32+
* numeric and string attributes.
33+
*
34+
* If you use this on a non-numeric attribute, make sure that only the server reads off of that
35+
* attribute. Non-primitive values aren't replicated correctly between the client and the
36+
* server; the client will read garbage and may crash!
37+
*
38+
* @param iEntity Entity index to set the attribute on. Must have m_AttributeList.
39+
* @param strAttrib Name of the attribute, as from the "name" key in items_game.
40+
* @param strValue Value to set the attribute to.
41+
*
42+
* @return True if the attribute was added successfully, false if the attribute name was invalid.
43+
* @error Invalid entity index or entity does not have m_AttributeList.
44+
*/
45+
native bool TF2Attrib_SetFromStringValue(int iEntity, const char[] strAttrib, const char[] strValue);
46+
3047
/**
3148
* Returns the address of an attribute on an entity.
3249
*
@@ -36,7 +53,7 @@ native bool TF2Attrib_SetByDefIndex(int iEntity, int iDefIndex, float flValue);
3653
* @return Address of the attribute on the entity, or Address_Null if the attribute does not exist on the entity.
3754
* @error Invalid entity index or attribute name passed.
3855
*/
39-
native Address TF2Attrib_GetByName(int iEntity, char[] strAttrib);
56+
native Address TF2Attrib_GetByName(int iEntity, const char[] strAttrib);
4057

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

6380
/**
6481
* Removes an attribute from an entity.
@@ -134,6 +151,20 @@ native void TF2Attrib_SetValue(Address pAttrib, float flValue);
134151
*/
135152
native float TF2Attrib_GetValue(Address pAttrib);
136153

154+
/**
155+
* Returns the string data from its raw value representation (a CAttribute_String instance).
156+
*
157+
* WARNING: This dereferences the input value! Feeding it values that aren't CAttribute_String pointers will result in unexpected behavior, potentially crashing the server.
158+
* In the case where you only want the currently active value, use TF2Attrib_HookValueString instead.
159+
*
160+
* @param pRawValue Raw attribute value. You can get this value with either TF2Attrib_GetValue, TF2Attrib_GetSOCAttribs, or TF2Attrib_GetStaticAttribs.
161+
* @param buffer Buffer to store the resulting string to.
162+
* @param maxlen Maximum length of the buffer.
163+
*
164+
* @return Number of bytes written.
165+
*/
166+
native int TF2Attrib_UnsafeGetStringValue(any pRawValue, char[] buffer, int maxlen);
167+
137168
/**
138169
* Sets the value of m_nRefundableCurrency on an attribute.
139170
*
@@ -202,6 +233,72 @@ native int TF2Attrib_GetSOCAttribs(int iEntity, int[] iAttribIndices, float[] fl
202233
*/
203234
native bool TF2Attrib_IsIntegerValue(int iDefIndex);
204235

236+
/**
237+
* Returns true if an attribute with the specified name exists.
238+
*
239+
* @param strAttrib Name of the attribute, as from the "name" key in items_game.
240+
*
241+
* @return True if the attribute exists, false otherwise.
242+
*/
243+
native bool TF2Attrib_IsValidAttributeName(const char[] strAttrib);
244+
245+
/**
246+
* Adds a custom, potentially temporary attribute to a player.
247+
*
248+
* @param client Client index to set the attribute on.
249+
* @param strAttrib Name of the attribute, as from the "name" key in items_game.
250+
* @param flValue Value to set m_flValue to.
251+
* @param flDuration Duration of the attribute. If less than 0, the attribute will not be automatically removed.
252+
*
253+
* @noreturn
254+
*/
255+
native void TF2Attrib_AddCustomPlayerAttribute(int client, const char[] strAttrib, float flValue, float flDuration = -1.0);
256+
257+
/**
258+
* Removes a previously applied custom attribute on a player.
259+
*
260+
* @param client Client index to remove the attribute from.
261+
* @param strAttrib Name of the attribute, as from the "name" key in items_game.
262+
*
263+
* @noreturn
264+
*/
265+
native void TF2Attrib_RemoveCustomPlayerAttribute(int client, const char[] strAttrib);
266+
267+
/**
268+
* Applies a transformation to the given initial value, following the rules according to the given attribute class.
269+
*
270+
* @param flInitial Initial float value.
271+
* @param attrClass The attribute class, as from the "attribute_class" key in items_game.
272+
* @param iEntity The entity that should be checked. Checking players also checks their equipped items.
273+
*
274+
* @return Transformed initial value.
275+
*/
276+
native float TF2Attrib_HookValueFloat(float flInitial, const char[] attrClass, int iEntity);
277+
278+
/**
279+
* Applies a transformation to the given initial value, following the rules according to the given attribute class.
280+
*
281+
* @param nInitial Initial integer value.
282+
* @param attrClass The attribute class, as from the "attribute_class" key in items_game.
283+
* @param iEntity The entity that should be checked. Checking players also checks their equipped items.
284+
*
285+
* @return Transformed initial value.
286+
*/
287+
native int TF2Attrib_HookValueInt(int nInitial, const char[] attrClass, int iEntity);
288+
289+
/**
290+
* Applies a transformation to the given initial value, following the rules according to the given attribute class.
291+
*
292+
* @param initial Initial string value. (This appears to only be returned if the entity doesn't have the attribute.)
293+
* @param attrClass The attribute class, as from the "attribute_class" key in items_game.
294+
* @param iEntity The entity that should be checked. Checking players also checks their equipped items.
295+
* @param buffer Transformed initial value.
296+
* @param maxlen Buffer size.
297+
*
298+
* @return Number of bytes written.
299+
*/
300+
native int TF2Attrib_HookValueString(const char[] initial, const char[] attrClass, int iEntity, char[] buffer, int maxlen);
301+
205302
/**
206303
* Gets whether the plugin loaded without ANY errors.
207304
* For the purpose of allowing dependencies to ignore the plugin if this returns false.
@@ -244,63 +341,12 @@ public void __pl_tf2attributes_SetNTVOptional()
244341
MarkNativeAsOptional("TF2Attrib_GetSOCAttribs");
245342
MarkNativeAsOptional("TF2Attrib_ListDefIndices");
246343
MarkNativeAsOptional("TF2Attrib_IsIntegerValue");
344+
MarkNativeAsOptional("TF2Attrib_IsValidAttributeName");
345+
MarkNativeAsOptional("TF2Attrib_AddCustomPlayerAttribute");
346+
MarkNativeAsOptional("TF2Attrib_RemoveCustomPlayerAttribute");
347+
MarkNativeAsOptional("TF2Attrib_HookValueFloat");
348+
MarkNativeAsOptional("TF2Attrib_HookValueInt");
349+
247350
MarkNativeAsOptional("TF2Attrib_IsReady");
248-
249-
// MarkNativeAsOptional("TF2Attrib_SetInitialValue");
250-
// MarkNativeAsOptional("TF2Attrib_GetInitialValue");
251-
// MarkNativeAsOptional("TF2Attrib_SetIsSetBonus");
252-
// MarkNativeAsOptional("TF2Attrib_GetIsSetBonus");
253351
}
254352
#endif
255-
256-
//OLD things lie here
257-
//flInitialValue and bSetBonus don't exist anymore
258-
/**
259-
* Sets the value of m_flInitialValue on an attribute.
260-
*
261-
* @param pAttrib Address of the attribute.
262-
* @param flValue Value to set m_flInitialValue to.
263-
*
264-
* @noreturn
265-
*/
266-
//native TF2Attrib_SetInitialValue(Address pAttrib, float flValue);
267-
268-
/**
269-
* Returns the value of m_flInitialValue on an attribute.
270-
*
271-
* @param pAttrib Address of the attribute.
272-
*
273-
* @return The floating point value of m_flInitialValue on the attribute.
274-
*/
275-
//native float TF2Attrib_GetInitialValue(Address pAttrib);
276-
277-
/**
278-
* Sets the boolean value of m_bSetBonus on an attribute.
279-
*
280-
* @param pAttrib Address of the attribute.
281-
* @param bSetBonus Value to set m_bSetBonus to.
282-
*
283-
* @noreturn
284-
*/
285-
//native TF2Attrib_SetIsSetBonus(Address pAttrib, bool bSetBonus);
286-
287-
/**
288-
* Returns the boolean value of m_bSetBonus on an attribute.
289-
*
290-
* @param pAttrib Address of the attribute.
291-
*
292-
* @return The boolean value of m_bSetBonus on the attribute.
293-
*/
294-
//native bool TF2Attrib_GetIsSetBonus(Address pAttrib);
295-
296-
//stock TF2Attrib_IsIntegerValue(iDefIndex)
297-
//{
298-
// switch (iDefIndex)
299-
// {
300-
// 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:
301-
// {
302-
// return true;
303-
// }
304-
// }
305-
// return false;
306-
//}

0 commit comments

Comments
 (0)