Data Descriptions: Difference between revisions
(25 intermediate revisions by 8 users not shown) | |||
Line 1: | Line 1: | ||
{{ | {{LanguageBar}} | ||
}} | |||
'''Data description tables''' contain [[wikipedia:metadata|metadata]]. They have many uses, but the most important is to define what internal information about a class should be stored when the game is saved. | |||
{{note|The Preprocessor directive <code>USES_SAVERESTORE</code> ''may'' be required for data descriptions (and the save/restore system in general) to function. It is included by default.}} | {{note|The Preprocessor directive <code>USES_SAVERESTORE</code> ''may'' be required for data descriptions (and the save/restore system in general) to function. It is included by default.}} | ||
== | == Creation == | ||
A Datadesc table must be declared in the class with <code>DECLARE_DATADESC()</code>: | |||
class CMyClass : public CBaseEntity | |||
{ | |||
DECLARE_CLASS( CMyClass, CBaseEntity ); | |||
DECLARE_DATADESC(); | |||
public: | |||
string_t m_iMyString; | |||
bool m_bMyBool; | |||
}; | |||
Table elements are then declared between <code>BEGIN_DATADESC</code> and <code>END_DATADESC</code>, outside any functions: | |||
BEGIN_DATADESC( CMyClass ) | BEGIN_DATADESC( CMyClass ) | ||
DEFINE_FIELD( | DEFINE_FIELD( m_iMyString, FIELD_STRING ), | ||
DEFINE_FIELD( | DEFINE_FIELD( m_bMyBool, FIELD_BOOLEAN ), | ||
END_DATADESC() | END_DATADESC() | ||
Note the absence of semicolons. | Note the absence of semicolons. The order of the elements does not matter as long as they are between <code>BEGIN_DATADESC</code> and <tt>END_DATADESC</tt>. | ||
{{warning|Take extra care to get your Datadesc syntax right. The compiler sees only the code ''behind'' the macros, so any errors it throws may not make much sense! Sometimes, they're not even real errors!}} | |||
{{warning|Take extra care to get your Datadesc syntax right. The compiler sees only the code ''behind'' the macros, so any errors it throws may not make much sense!}} | |||
== Example == | == Example == | ||
For {{ent|npc_barnacle}}: | |||
For | |||
BEGIN_DATADESC( CNPC_Barnacle ) | BEGIN_DATADESC( CNPC_Barnacle ) | ||
Line 75: | Line 82: | ||
END_DATADESC() | END_DATADESC() | ||
== Field Types == | |||
Data descriptions heavily rely on <code>fieldtype_t</code> for determining what a variable is and how to save or restore it. Below is a list of all field types and how they can be used in the various elements in a data description. | |||
{| class="standard-table" | |||
! Name | |||
! Description | |||
! Variable(s) | |||
! Can be used as a keyfield | |||
! Can be used as an input | |||
|- id="void" | |||
| '''<center id="FIELD_VOID">FIELD_VOID</center>''' | |||
|No type or value. Used for functions without parameters. | |||
|{{N/A}} | |||
|{{N/A}} | |||
|{{Yes}} | |||
|- id="float" | |||
| '''<center id="FIELD_FLOAT">FIELD_FLOAT</center>''' | |||
|Any [[float|floating point]] value. | |||
|{{Yes|float}} | |||
|{{Yes}} | |||
|{{Yes}} | |||
|- id="string" | |||
| '''<center id="FIELD_STRING">FIELD_STRING</center>''' | |||
|A [[string_t]]. (return from ALLOC_STRING) {{confirm|Can this use <code>const char*</code>?}} | |||
|{{Yes|string_t}} | |||
|{{Yes}} | |||
|{{Yes}} | |||
|- id="vector" | |||
| '''<center id="FIELD_VECTOR">FIELD_VECTOR</center>''' | |||
|Any [[vector]], [[QAngle]], or [[AngularImpulse]]. | |||
|{{Yes|Vector<br>QAngle<br>AngularImpulse}} | |||
|{{Yes}} | |||
|{{Yes}} | |||
|- id="quaternion" | |||
| '''<center id="FIELD_QUATERNION">FIELD_QUATERNION</center>''' | |||
|A [[quaternion]] value. Few references have been found relating to this field type. | |||
|{{N/A|Quaternion}} | |||
|{{N/A|Not known}} | |||
|{{N/A|Not known}} | |||
|- id="integer" | |||
| '''<center id="FIELD_INTEGER">FIELD_INTEGER</center>''' | |||
|Any [[integer]] or enum value. | |||
|{{Yes|int}} | |||
|{{Yes}} | |||
|{{Yes}} | |||
|- id="boolean" | |||
| '''<center id="FIELD_BOOLEAN">FIELD_BOOLEAN</center>''' | |||
|[[Boolean]] value (represented as an integer) | |||
|{{Yes|bool}} | |||
|{{Yes}} | |||
|{{Yes}} | |||
|- id="short" | |||
| '''<center id="FIELD_SHORT">FIELD_SHORT</center>''' | |||
|2 byte integer, or [[short]] | |||
|{{Yes|short}} | |||
|{{N/A|Maybe}} | |||
|{{N/A|Maybe}} | |||
|- id="character" | |||
| '''<center id="FIELD_CHARACTER">FIELD_CHARACTER</center>''' | |||
|One byte | |||
|{{Yes|char}} | |||
|{{N/A|Maybe}} | |||
|{{N/A|Maybe}} | |||
|- id="color32" | |||
| '''<center id="FIELD_COLOR32">FIELD_COLOR32</center>''' | |||
|8-bit per channel [R,G,B,A] (32-bit color) | |||
|{{Yes|color32}} | |||
|{{Yes}} | |||
|{{Yes}} | |||
|- id="embedded" | |||
| '''<center id="FIELD_EMBEDDED">FIELD_EMBEDDED</center>''' | |||
|An object with its own data description, like <code>CTakeDamageInfo</code>. You should usually use <code>DEFINE_EMBEDDED</code> in those cases. | |||
|{{N/A}} | |||
|{{N/A}} | |||
|{{N/A}} | |||
|- id="custom" | |||
| '''<center id="FIELD_CUSTOM">FIELD_CUSTOM</center>''' | |||
|A type with a special save/restore class containing its read/write/parse functions. (Use <code>DEFINE_CUSTOM_FIELD</code> instead, more information below) | |||
|{{N/A}} | |||
|{{N/A}} | |||
|{{N/A}} | |||
|- id="classptr" | |||
| '''<center id="FIELD_CLASSPTR">FIELD_CLASSPTR</center>''' | |||
|[[CBaseEntity]] [[pointer]]. <code>FIELD_EHANDLE</code> is usually more preferable. | |||
|{{Yes|CBaseEntity*<br>(or any derived class?)}} | |||
|{{No}} | |||
|{{N/A|Maybe}} | |||
|- id="ehandle" | |||
| '''<center id="FIELD_EHANDLE">FIELD_EHANDLE</center>''' | |||
|[[CHandle]] or EHANDLE. {{tip|Input functions that take EHANDLE and receive a string convert it to <code>FIELD_EHANDLE</code> by searching for an entity with a name that matches the string. {{bugfix|This conversion does not support !activator or !caller.|!activator and !caller support can be added by changing <code>CBaseEntity::AcceptInput()</code> and modifying or creating a new version of <code>variant_t::Convert()</code> that accepts !activator, !caller, and/or !self. {{Game link|Mapbase}} already uses this fix.|code}}}} | |||
|{{Yes|CHandle<br>(EHANDLE)}} | |||
|{{No}} | |||
|{{Yes}} | |||
|- id="edict" | |||
| '''<center id="FIELD_EDICT">FIELD_EDICT</center>''' | |||
|edict_t pointer. Functionality for this field type exists, but it does not appear to be used anywhere in the public release of Source 2013. | |||
|{{N/A|edict_t*}} | |||
|{{No}} | |||
|{{No}} | |||
|- id="position_vector" | |||
| '''<center id="FIELD_POSITION_VECTOR">FIELD_POSITION_VECTOR</center>''' | |||
|A world coordinate value, which is fixed up across level-transitions automatically. Use <code>FIELD_VECTOR</code> for simple, portable vectors. | |||
|{{Yes|Vector}} | |||
|{{Yes}} | |||
|{{No}} | |||
|- id="time" | |||
| '''<center id="FIELD_TIME">FIELD_TIME</center>''' | |||
|A floating point time value, which is fixed up across level-transitions automatically. Use this for floats involved in <code>gpGlobals->curtime</code> calculations. | |||
|{{Yes|float}} | |||
|{{Yes}} | |||
|{{No}} | |||
|- id="tick" | |||
| '''<center id="FIELD_TICK">FIELD_TICK</center>''' | |||
|An integer tick count, which is fixed up similarly to FIELD_TIME. Use this for integers involved in <code>gpGlobals->tickcount</code> calculations. | |||
|{{Yes|int}} | |||
|{{Yes}} | |||
|{{No}} | |||
|- id="modelname" | |||
| '''<center id="FIELD_MODELNAME">FIELD_MODELNAME</center>''' | |||
|Engine string that is a model name. {{confirm|Is this precached automatically?}} | |||
|{{Yes|string_t}} | |||
|{{Yes}} | |||
|{{No}} | |||
|- id="soundname" | |||
| '''<center id="FIELD_SOUNDNAME">FIELD_SOUNDNAME</center>''' | |||
|Engine string that is a sound name. {{confirm|Is this precached automatically?}} | |||
|{{Yes|string_t}} | |||
|{{Yes}} | |||
|{{No}} | |||
|- id="input" | |||
| '''<center id="FIELD_INPUT">FIELD_INPUT</center>''' | |||
|This can be used with inputted data fields derived from CMultiInputVar, but it's also used as an input parameter that can take an input parameter without converting it. The latter is the most common usage. | |||
|{{Yes|CMultiInputVar}} | |||
|{{N/A|Maybe}} | |||
|{{Yes}} | |||
|- id="function" | |||
| '''<center id="FIELD_FUNCTION">FIELD_FUNCTION</center>''' | |||
|Pointer to a class function defined by DEFINE_FUNCTION, DEFINE_THINKFUNC, etc. (Think, Use, etc.) | |||
|{{Yes}} | |||
|{{No}} | |||
|{{No}} | |||
|- id="vmatrix" | |||
| '''<center id="FIELD_VMATRIX">FIELD_VMATRIX</center>''' | |||
|A VMatrix. Output coordinates are NOT worldspace. | |||
|{{Yes|VMatrix}} | |||
|{{Yes}} | |||
|{{No}} | |||
|- id="vmatrix_worldspace" | |||
| '''<center id="FIELD_VMATRIX_WORLDSPACE">FIELD_VMATRIX_WORLDSPACE</center>''' | |||
|A VMatrix that maps some localspace to worldspace (translation is fixed up on level-transitions) | |||
|{{Yes|VMatrix}} | |||
|{{Yes}} | |||
|{{No}} | |||
|- id="matrix3x4_worldspace" | |||
| '''<center id="FIELD_MATRIX3X4_WORLDSPACE">FIELD_MATRIX3X4_WORLDSPACE</center>''' | |||
|matrix3x4_t that maps some localspace to worldspace (translation is fixed up on level-transitions) | |||
|{{Yes|matrix3x4_t}} | |||
|{{Yes}} | |||
|{{No}} | |||
|- id="interval" | |||
| '''<center id="FIELD_INTERVAL">FIELD_INTERVAL</center>''' | |||
|A start and range floating point interval, or interval_t. This is used for ranges like "9,16" producing any number between 9 and 16, seen in systems like [[Soundscripts]]. Functionality for this field type exists, and is confirmed to function, but it does not appear to be used anywhere in the public release of Source 2013. | |||
|{{Yes|interval_t}} | |||
|{{No}} | |||
|{{No}} | |||
|- id="modelindex" | |||
| '''<center id="FIELD_MODELINDEX">FIELD_MODELINDEX</center>''' | |||
|A [[model]] index | |||
|{{Yes|int}} | |||
|{{No}} | |||
|{{No}} | |||
|- id="materialindex" | |||
| '''<center id="FIELD_MATERIALINDEX">FIELD_MATERIALINDEX</center>''' | |||
|A [[material]] index (using the material precache string table) | |||
|{{Yes|int}} | |||
|{{No}} | |||
|{{No}} | |||
|- id="vector2d" | |||
| '''<center id="FIELD_VECTOR2D">FIELD_VECTOR2D</center>''' | |||
|A Vector2D, a special version of Vector with only 2 floats. | |||
|{{Yes|Vector2D}} | |||
|{{No}} | |||
|{{No}} | |||
|} | |||
Adding keyfield support to existing field types can be done through the <code>ParseKeyvalue()</code> function in <code>saverestore_gamedll.cpp</code>. The addition of entirely new field types has not been tested. | |||
== Elements == | == Elements == | ||
Most elements in a Datadesc involve member variables and function names. Empty lines between elements have no effect. It is possible to use array elements and even the variables of an embedded class in a Datadesc: | |||
BEGIN_DATADESC( CMyClass ) | |||
DEFINE_KEYFIELD( m_iszParams[0], FIELD_STRING, "param0" ), | |||
DEFINE_KEYFIELD( m_iszParams[1], FIELD_STRING, "param1" ), | |||
DEFINE_KEYFIELD( m_iszParams[2], FIELD_STRING, "param2" ), | |||
DEFINE_FIELD( m_iszParams[3], FIELD_STRING ), | |||
DEFINE_FIELD( m_MyEmbedded.embeddedstring, FIELD_STRING ), | |||
DEFINE_KEYFIELD( m_MyEmbedded.embeddedfloat, FIELD_FLOAT, "EmbeddedFloat" ), | |||
END_DATADESC() | |||
You could also use a single variable in more than one element: | |||
BEGIN_DATADESC( CMyClass ) | |||
DEFINE_KEYFIELD( m_flRange, FIELD_FLOAT, "range" ), | |||
DEFINE_KEYFIELD( m_flRange, FIELD_FLOAT, "distance" ), | |||
DEFINE_KEYFIELD( m_flRange, FIELD_FLOAT, "MyRange" ), | |||
DEFINE_INPUTFUNC( FIELD_VOID, "MyInput", InputMyInput ), | |||
DEFINE_INPUTFUNC( FIELD_VOID, "DoMyInput", InputMyInput ), | |||
DEFINE_OUTPUT( m_OnMyInput, "OnMyInput" ), | |||
DEFINE_OUTPUT( m_OnMyInput, "OnInput" ), | |||
END_DATADESC() | |||
This trick is occasionally used by Valve to make regular fields on base entities turn into keyfields on derived entities, but it is not known if this creates unnecessary data in save/restore. | |||
=== DEFINE_FIELD === | === DEFINE_FIELD === | ||
Line 84: | Line 310: | ||
DEFINE_FIELD( variable, field_type ) | DEFINE_FIELD( variable, field_type ) | ||
This macro does nothing more than save the variable. | |||
Both link a variable to a [[FGD]] keyvalue, allowing it to be configured by mappers in [[Hammer]] (i.e. read from the [[entity lump]]). Depending on which you use, the value will or will not be embedded in saved games. Arguments are the same in either case: | ===<tt>DEFINE_KEYFIELD</tt> and <tt>DEFINE_KEYFIELD_NOT_SAVED</tt>=== | ||
Both macros link a variable to a [[FGD]] keyvalue, allowing it to be configured by mappers in [[Hammer]] (i.e., read from the [[entity lump]]). Depending on which macro you use, the value will or will not be embedded in saved games. Arguments are the same in either case: | |||
DEFINE_KEYFIELD( variable, field_type, "HammerName" ) | DEFINE_KEYFIELD( variable, field_type, "HammerName" ) | ||
Line 123: | Line 319: | ||
{{note|HammerName is case insensitive.}} | {{note|HammerName is case insensitive.}} | ||
{{tip| | There is also <tt>KeyValue()</tt>, a function which allows programmers to do something when a keyvalue is received, like use a specific kind of parsing or invoke a function. This is a virtual function, but at its root in <code>CBaseEntity::KeyValue()</code> on <tt>baseentity_shared.cpp</tt>, keyvalues are handed to the datadesc for finding keyfields to set. Custom save/restore ops also support custom keyfield parsing. | ||
{{tip|If you want to have a keyfield that specifies an entity, you could store the name in a <code>string_t</code> under <code>FIELD_STRING</code> and cache it later as a (saved) CHandle/EHANDLE, which is safer and offers more control.}} | |||
===<tt>DEFINE_CUSTOM_FIELD</tt> and <tt>DEFINE_CUSTOM_KEYFIELD</tt>=== | |||
Allows custom interpretation of a variable with regards to how it is saved and restored. By passing in the reference to a handler class, the user is able to handle serialization of the data entirely. | Allows custom interpretation of a variable with regards to how it is saved and restored. By passing in the reference to a handler class, the user is able to handle serialization of the data entirely. | ||
Line 133: | Line 329: | ||
For more information, see uses of <code>CClassPtrSaveRestoreOps</code> within the code base. | For more information, see uses of <code>CClassPtrSaveRestoreOps</code> within the code base. | ||
=== DEFINE_OUTPUT === | ===<tt>DEFINE_OUTPUT</tt>=== | ||
Links an [[Inputs_and_Outputs#Outputs|output event]] to a named identifier used by Hammer. | |||
Links an [[ | |||
DEFINE_OUTPUT( COutputEvent, "HammerOutputName") | DEFINE_OUTPUT( COutputEvent, "HammerOutputName") | ||
{{tip|Outputs that can output parameters use a saved variable that could be set or retrieved, even without firing the output itself. If you need a variable specifically for firing an output, you can set and retrieve the output's value as if it were a regular variable. See <code>m_OutValue</code> on <code>CMathCounter</code> for an example.}} | |||
This macro is used to link named inputs from Hammer to functions in the engine | ===<tt>DEFINE_INPUTFUNC</tt>=== | ||
This macro is used to link named [[Inputs_and_Outputs#Inputs|inputs]] from Hammer to functions in the engine. The macro is defined as: | |||
DEFINE_INPUTFUNC( parameterType, "inputName", InputFunction ) | DEFINE_INPUTFUNC( parameterType, "inputName", InputFunction ) | ||
The <code> | The function's parameter must be a single <code>inputdata_t</code> C++ reference. (e.g. <code>&inputdata</code>) Any variable name can be used. | ||
Linked functions should be a member function declared inside of the class, but functions from a base class can be linked in a derived class's data description. Functions linked in a base class can also be re-defined by derived classes without any need to add it to their own data descriptions. | |||
Input functions can accept a value, also known as a parameter, that could be specified in Hammer or passed by an output. This value is accessed by <tt>inputdata_t</tt>'s <code>value</code> variable, which returns a <tt>variant_t</tt>, a storage class used to hold various types of values for I/O purposes. The type of value actually stored in the <code>variant_t</code> is controlled by the macro's <code>parameterType</code>, which only supports certain field types, as specified in the "Field Types" section. Use <code>FIELD_VOID</code> for functions with no parameter. | |||
The passed value will be converted to the specified field type via <code>variant_t::Convert()</code> if necessary. Parameter overrides passed in Hammer always start as <tt>FIELD_STRING</tt>, but some outputs could pass values of a specific type. Using <code>FIELD_INPUT</code> prevents automatic conversion and always takes the original passed value, which is useful when an input is designed to take multiple data types. (use <code>fieldType</code> to get a <tt>variant_t</tt>'s data type) | |||
Only <tt>FIELD_VOID</tt>, <tt>FIELD_STRING</tt>, and <tt>FIELD_INPUT</tt> inputs support empty values. Others will print a conversion failure warning in the console and do nothing. | |||
===<tt>DEFINE_INPUT</tt>=== | |||
Not to be confused with <tt>FIELD_INPUT</tt>, this macro is a combination of <code>DEFINE_INPUTFUNC</code> and <tt>DEFINE_KEYFIELD</tt>. It links both a keyvalue and an input with the specified name to a single variable. The input sets the linked variable to whatever parameter is passed through. This bypasses the need to create an input function whose sole purpose is to set a data member to a passed value. The macro is declared as: | |||
DEFINE_INPUT( variableName, variableType, "keyvalue_and_input_name" ) | |||
As their names suggest, these macros deal with saving and restoring [[array]] values. The number of elements in the [[array]] must be declared when using < | ===<tt>DEFINE_ARRAY</tt> and <tt>DEFINE_AUTO_ARRAY</tt>=== | ||
As their names suggest, these macros deal with saving and restoring [[array]] values. The number of elements in the [[array]] must be declared when using <tt>DEFINE_ARRAY</tt>, whereas <code>DEFINE_AUTO_ARRAY</code> will cause the code to automatically determine the size of the array at compile time. Arrays can also be saved individually, but this is usually only useful when you need to link them to keyvalues. | |||
The macros are declared as: | The macros are declared as: | ||
Line 171: | Line 365: | ||
DEFINE_AUTO_ARRAY( variable, variableType ) | DEFINE_AUTO_ARRAY( variable, variableType ) | ||
===DEFINE_THINKFUNC=== | ===<tt>DEFINE_THINKFUNC</tt>=== | ||
Entities with a custom [[Thinking|think function]] must declare it: | |||
Entities with a custom [[ | |||
DEFINE_THINKFUNC( MyThink ) | DEFINE_THINKFUNC( MyThink ) | ||
Think functions must be < | Think functions must be <tt>void</tt>. <!-- typedef void (*ThinkFunc)( void) --> | ||
===<tt>DEFINE_ENTITYFUNC</tt>=== | |||
Entities using a custom touch function must declare that function via this macro declaration. | Entities using a custom touch function must declare that function via this macro declaration. | ||
Line 187: | Line 379: | ||
typedef void (CBaseEntity::*ENTITYFUNCPTR)(CBaseEntity *pOther ); | typedef void (CBaseEntity::*ENTITYFUNCPTR)(CBaseEntity *pOther ); | ||
===DEFINE_USEFUNC=== | ===<tt>DEFINE_USEFUNC</tt>=== | ||
Entities using a custom use function must declare that function via this macro declaration. | Entities using a custom use function must declare that function via this macro declaration. | ||
Line 201: | Line 392: | ||
===Others=== | ===Others=== | ||
Some variable types are independent from the common fields and must use specific macros to work correctly: | |||
*dlls\simtimer.h | *dlls\simtimer.h | ||
**DEFINE_SIMTIMER - | **DEFINE_SIMTIMER - Defines a [[SimTimer]] | ||
*game_shared\physics_saverestore.h | *game_shared\physics_saverestore.h | ||
**DEFINE_PHYSPTR - | **DEFINE_PHYSPTR - Defines a physics pointer | ||
**DEFINE_PHYSPTR_ARRAY - | **DEFINE_PHYSPTR_ARRAY - Defines a physics pointer array | ||
*game_shared\saverestore_bitstring.h | *game_shared\saverestore_bitstring.h | ||
**DEFINE_BITSTRING - | **DEFINE_BITSTRING - Defines a [[CBitStringT]], [[CBitString]], or [[CFixedBitString]] | ||
*game_shared\saverestore_utlmap.h | *game_shared\saverestore_utlmap.h | ||
**DEFINE_UTLMAP - | **DEFINE_UTLMAP - Defines a [[CUtlMap]] | ||
*game_shared\saverestore_utlrbtree.h | *game_shared\saverestore_utlrbtree.h | ||
**DEFINE_UTLRBTREE - | **DEFINE_UTLRBTREE - Defines a [[CUtlRBTree]] | ||
*game_shared\saverestore_utlvector.h | *game_shared\saverestore_utlvector.h | ||
**DEFINE_UTLVECTOR - | **DEFINE_UTLVECTOR - Defines a [[CUtlVector]] | ||
*game_shared\soundenvelope.h | *game_shared\soundenvelope.h | ||
**DEFINE_SOUNDPATCH | **DEFINE_SOUNDPATCH - Defines a [[CSoundPatch]] | ||
*public\saverestore.h | *public\saverestore.h | ||
**DEFINE_ENTITY_FIELD | **DEFINE_ENTITY_FIELD | ||
**DEFINE_ENTITY_GLOBAL_FIELD | **DEFINE_ENTITY_GLOBAL_FIELD | ||
**DEFINE_GLOBAL_FIELD | **DEFINE_GLOBAL_FIELD | ||
**DEFINE_GLOBAL_KEYFIELD | **DEFINE_GLOBAL_KEYFIELD | ||
**DEFINE_AUTO_ARRAY2D | **DEFINE_AUTO_ARRAY2D | ||
**DEFINE_EMBEDDED_OVERRIDE | **DEFINE_EMBEDDED_OVERRIDE | ||
**DEFINE_EMBEDDEDBYREF | **DEFINE_EMBEDDEDBYREF | ||
Line 239: | Line 428: | ||
**DEFINE_FIELD_NAME_TOL | **DEFINE_FIELD_NAME_TOL | ||
*public\stdstring.h | *public\stdstring.h | ||
**DEFINE_STDSTRING | **DEFINE_STDSTRING - Defines a [[std::string]]. | ||
[[Category:Macros]] | [[Category:Macros]] |
Latest revision as of 13:22, 29 September 2024
Data description tables contain metadata. They have many uses, but the most important is to define what internal information about a class should be stored when the game is saved.

USES_SAVERESTORE
may be required for data descriptions (and the save/restore system in general) to function. It is included by default.Creation
A Datadesc table must be declared in the class with DECLARE_DATADESC()
:
class CMyClass : public CBaseEntity { DECLARE_CLASS( CMyClass, CBaseEntity ); DECLARE_DATADESC(); public: string_t m_iMyString; bool m_bMyBool; };
Table elements are then declared between BEGIN_DATADESC
and END_DATADESC
, outside any functions:
BEGIN_DATADESC( CMyClass ) DEFINE_FIELD( m_iMyString, FIELD_STRING ), DEFINE_FIELD( m_bMyBool, FIELD_BOOLEAN ), END_DATADESC()
Note the absence of semicolons. The order of the elements does not matter as long as they are between BEGIN_DATADESC
and END_DATADESC.

Example
For npc_barnacle:
BEGIN_DATADESC( CNPC_Barnacle ) DEFINE_FIELD( m_flAltitude, FIELD_FLOAT ), DEFINE_FIELD( m_cGibs, FIELD_INTEGER ),// barnacle loads up on gibs each time it kills something. DEFINE_FIELD( m_bLiftingPrey, FIELD_BOOLEAN ), DEFINE_FIELD( m_bSwallowingPrey, FIELD_BOOLEAN ), DEFINE_FIELD( m_flDigestFinish, FIELD_TIME ), DEFINE_FIELD( m_bPlayedPullSound, FIELD_BOOLEAN ), DEFINE_FIELD( m_bPlayerWasStanding, FIELD_BOOLEAN ), DEFINE_FIELD( m_flVictimHeight, FIELD_FLOAT ), DEFINE_FIELD( m_iGrabbedBoneIndex, FIELD_INTEGER ), DEFINE_FIELD( m_vecRoot, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_vecTip, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_hTongueRoot, FIELD_EHANDLE ), DEFINE_FIELD( m_hTongueTip, FIELD_EHANDLE ), DEFINE_FIELD( m_hRagdoll, FIELD_EHANDLE ), DEFINE_AUTO_ARRAY( m_pRagdollBones, FIELD_MATRIX3X4_WORLDSPACE ), DEFINE_PHYSPTR( m_pConstraint ), DEFINE_KEYFIELD( m_flRestUnitsAboveGround, FIELD_FLOAT, "RestDist" ), DEFINE_FIELD( m_nSpitAttachment, FIELD_INTEGER ), DEFINE_FIELD( m_hLastSpitEnemy, FIELD_EHANDLE ), DEFINE_FIELD( m_nShakeCount, FIELD_INTEGER ), DEFINE_FIELD( m_flNextBloodTime, FIELD_TIME ), #ifndef _XBOX DEFINE_FIELD( m_nBloodColor, FIELD_INTEGER ), #endif DEFINE_FIELD( m_vecBloodPos, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_flBarnaclePullSpeed, FIELD_FLOAT ), DEFINE_FIELD( m_flLocalTimer, FIELD_TIME ), DEFINE_FIELD( m_vLastEnemyPos, FIELD_POSITION_VECTOR ), DEFINE_FIELD( m_flLastPull, FIELD_FLOAT ), DEFINE_EMBEDDED( m_StuckTimer ), DEFINE_INPUTFUNC( FIELD_VOID, "DropTongue", InputDropTongue ), DEFINE_INPUTFUNC( FIELD_INTEGER, "SetDropTongueSpeed", InputSetDropTongueSpeed ), #ifdef HL2_EPISODIC DEFINE_INPUTFUNC( FIELD_VOID, "LetGo", InputLetGo ), DEFINE_OUTPUT( m_OnGrab, "OnGrab" ), DEFINE_OUTPUT( m_OnRelease, "OnRelease" ), #endif // Function pointers DEFINE_THINKFUNC( BarnacleThink ), DEFINE_THINKFUNC( WaitTillDead ), DEFINE_FIELD( m_bSwallowingBomb, FIELD_BOOLEAN ), END_DATADESC()
Field Types
Data descriptions heavily rely on fieldtype_t
for determining what a variable is and how to save or restore it. Below is a list of all field types and how they can be used in the various elements in a data description.
Name | Description | Variable(s) | Can be used as a keyfield | Can be used as an input |
---|---|---|---|---|
No type or value. Used for functions without parameters. | N/A | N/A | Yes | |
Any floating point value. | float | Yes | Yes | |
A string_t. (return from ALLOC_STRING) ![]() const char* ? |
string_t | Yes | Yes | |
Any vector, QAngle, or AngularImpulse. | Vector QAngle AngularImpulse |
Yes | Yes | |
A quaternion value. Few references have been found relating to this field type. | Quaternion | Not known | Not known | |
Any integer or enum value. | int | Yes | Yes | |
Boolean value (represented as an integer) | bool | Yes | Yes | |
2 byte integer, or short | short | Maybe | Maybe | |
One byte | char | Maybe | Maybe | |
8-bit per channel [R,G,B,A] (32-bit color) | color32 | Yes | Yes | |
An object with its own data description, like CTakeDamageInfo . You should usually use DEFINE_EMBEDDED in those cases.
|
N/A | N/A | N/A | |
A type with a special save/restore class containing its read/write/parse functions. (Use DEFINE_CUSTOM_FIELD instead, more information below)
|
N/A | N/A | N/A | |
CBaseEntity pointer. FIELD_EHANDLE is usually more preferable.
|
CBaseEntity* (or any derived class?) |
No | Maybe | |
CHandle or EHANDLE. ![]() FIELD_EHANDLE by searching for an entity with a name that matches the string. ![]()
|
CHandle (EHANDLE) |
No | Yes | |
edict_t pointer. Functionality for this field type exists, but it does not appear to be used anywhere in the public release of Source 2013. | edict_t* | No | No | |
A world coordinate value, which is fixed up across level-transitions automatically. Use FIELD_VECTOR for simple, portable vectors.
|
Vector | Yes | No | |
A floating point time value, which is fixed up across level-transitions automatically. Use this for floats involved in gpGlobals->curtime calculations.
|
float | Yes | No | |
An integer tick count, which is fixed up similarly to FIELD_TIME. Use this for integers involved in gpGlobals->tickcount calculations.
|
int | Yes | No | |
Engine string that is a model name. ![]() |
string_t | Yes | No | |
Engine string that is a sound name. ![]() |
string_t | Yes | No | |
This can be used with inputted data fields derived from CMultiInputVar, but it's also used as an input parameter that can take an input parameter without converting it. The latter is the most common usage. | CMultiInputVar | Maybe | Yes | |
Pointer to a class function defined by DEFINE_FUNCTION, DEFINE_THINKFUNC, etc. (Think, Use, etc.) | Yes | No | No | |
A VMatrix. Output coordinates are NOT worldspace. | VMatrix | Yes | No | |
A VMatrix that maps some localspace to worldspace (translation is fixed up on level-transitions) | VMatrix | Yes | No | |
matrix3x4_t that maps some localspace to worldspace (translation is fixed up on level-transitions) | matrix3x4_t | Yes | No | |
A start and range floating point interval, or interval_t. This is used for ranges like "9,16" producing any number between 9 and 16, seen in systems like Soundscripts. Functionality for this field type exists, and is confirmed to function, but it does not appear to be used anywhere in the public release of Source 2013. | interval_t | No | No | |
A model index | int | No | No | |
A material index (using the material precache string table) | int | No | No | |
A Vector2D, a special version of Vector with only 2 floats. | Vector2D | No | No |
Adding keyfield support to existing field types can be done through the ParseKeyvalue()
function in saverestore_gamedll.cpp
. The addition of entirely new field types has not been tested.
Elements
Most elements in a Datadesc involve member variables and function names. Empty lines between elements have no effect. It is possible to use array elements and even the variables of an embedded class in a Datadesc:
BEGIN_DATADESC( CMyClass ) DEFINE_KEYFIELD( m_iszParams[0], FIELD_STRING, "param0" ), DEFINE_KEYFIELD( m_iszParams[1], FIELD_STRING, "param1" ), DEFINE_KEYFIELD( m_iszParams[2], FIELD_STRING, "param2" ), DEFINE_FIELD( m_iszParams[3], FIELD_STRING ), DEFINE_FIELD( m_MyEmbedded.embeddedstring, FIELD_STRING ), DEFINE_KEYFIELD( m_MyEmbedded.embeddedfloat, FIELD_FLOAT, "EmbeddedFloat" ), END_DATADESC()
You could also use a single variable in more than one element:
BEGIN_DATADESC( CMyClass ) DEFINE_KEYFIELD( m_flRange, FIELD_FLOAT, "range" ), DEFINE_KEYFIELD( m_flRange, FIELD_FLOAT, "distance" ), DEFINE_KEYFIELD( m_flRange, FIELD_FLOAT, "MyRange" ), DEFINE_INPUTFUNC( FIELD_VOID, "MyInput", InputMyInput ), DEFINE_INPUTFUNC( FIELD_VOID, "DoMyInput", InputMyInput ), DEFINE_OUTPUT( m_OnMyInput, "OnMyInput" ), DEFINE_OUTPUT( m_OnMyInput, "OnInput" ), END_DATADESC()
This trick is occasionally used by Valve to make regular fields on base entities turn into keyfields on derived entities, but it is not known if this creates unnecessary data in save/restore.
DEFINE_FIELD
Tells the engine to store a variable in saved games.
DEFINE_FIELD( variable, field_type )
This macro does nothing more than save the variable.
DEFINE_KEYFIELD and DEFINE_KEYFIELD_NOT_SAVED
Both macros link a variable to a FGD keyvalue, allowing it to be configured by mappers in Hammer (i.e., read from the entity lump). Depending on which macro you use, the value will or will not be embedded in saved games. Arguments are the same in either case:
DEFINE_KEYFIELD( variable, field_type, "HammerName" )

There is also KeyValue(), a function which allows programmers to do something when a keyvalue is received, like use a specific kind of parsing or invoke a function. This is a virtual function, but at its root in CBaseEntity::KeyValue()
on baseentity_shared.cpp, keyvalues are handed to the datadesc for finding keyfields to set. Custom save/restore ops also support custom keyfield parsing.

string_t
under FIELD_STRING
and cache it later as a (saved) CHandle/EHANDLE, which is safer and offers more control.DEFINE_CUSTOM_FIELD and DEFINE_CUSTOM_KEYFIELD
Allows custom interpretation of a variable with regards to how it is saved and restored. By passing in the reference to a handler class, the user is able to handle serialization of the data entirely.
The handler class must descend from the CClassPtrSaveRestoreOps
class; it uses the Save()
and Restore()
functions for serialization.
For more information, see uses of CClassPtrSaveRestoreOps
within the code base.
DEFINE_OUTPUT
Links an output event to a named identifier used by Hammer.
DEFINE_OUTPUT( COutputEvent, "HammerOutputName")

m_OutValue
on CMathCounter
for an example.DEFINE_INPUTFUNC
This macro is used to link named inputs from Hammer to functions in the engine. The macro is defined as:
DEFINE_INPUTFUNC( parameterType, "inputName", InputFunction )
The function's parameter must be a single inputdata_t
C++ reference. (e.g. &inputdata
) Any variable name can be used.
Linked functions should be a member function declared inside of the class, but functions from a base class can be linked in a derived class's data description. Functions linked in a base class can also be re-defined by derived classes without any need to add it to their own data descriptions.
Input functions can accept a value, also known as a parameter, that could be specified in Hammer or passed by an output. This value is accessed by inputdata_t's value
variable, which returns a variant_t, a storage class used to hold various types of values for I/O purposes. The type of value actually stored in the variant_t
is controlled by the macro's parameterType
, which only supports certain field types, as specified in the "Field Types" section. Use FIELD_VOID
for functions with no parameter.
The passed value will be converted to the specified field type via variant_t::Convert()
if necessary. Parameter overrides passed in Hammer always start as FIELD_STRING, but some outputs could pass values of a specific type. Using FIELD_INPUT
prevents automatic conversion and always takes the original passed value, which is useful when an input is designed to take multiple data types. (use fieldType
to get a variant_t's data type)
Only FIELD_VOID, FIELD_STRING, and FIELD_INPUT inputs support empty values. Others will print a conversion failure warning in the console and do nothing.
DEFINE_INPUT
Not to be confused with FIELD_INPUT, this macro is a combination of DEFINE_INPUTFUNC
and DEFINE_KEYFIELD. It links both a keyvalue and an input with the specified name to a single variable. The input sets the linked variable to whatever parameter is passed through. This bypasses the need to create an input function whose sole purpose is to set a data member to a passed value. The macro is declared as:
DEFINE_INPUT( variableName, variableType, "keyvalue_and_input_name" )
DEFINE_ARRAY and DEFINE_AUTO_ARRAY
As their names suggest, these macros deal with saving and restoring array values. The number of elements in the array must be declared when using DEFINE_ARRAY, whereas DEFINE_AUTO_ARRAY
will cause the code to automatically determine the size of the array at compile time. Arrays can also be saved individually, but this is usually only useful when you need to link them to keyvalues.
The macros are declared as:
DEFINE_ARRAY( variable, variableType, numElements )
DEFINE_AUTO_ARRAY( variable, variableType )
DEFINE_THINKFUNC
Entities with a custom think function must declare it:
DEFINE_THINKFUNC( MyThink )
Think functions must be void.
DEFINE_ENTITYFUNC
Entities using a custom touch function must declare that function via this macro declaration.
Touch functions must be of the type:
typedef void (CBaseEntity::*ENTITYFUNCPTR)(CBaseEntity *pOther );
DEFINE_USEFUNC
Entities using a custom use function must declare that function via this macro declaration.
Use functions must be of the type:
typedef void (*UseFunc)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
Others
Some variable types are independent from the common fields and must use specific macros to work correctly:
- dlls\simtimer.h
- DEFINE_SIMTIMER - Defines a SimTimer
- game_shared\physics_saverestore.h
- DEFINE_PHYSPTR - Defines a physics pointer
- DEFINE_PHYSPTR_ARRAY - Defines a physics pointer array
- game_shared\saverestore_bitstring.h
- DEFINE_BITSTRING - Defines a CBitStringT, CBitString, or CFixedBitString
- game_shared\saverestore_utlmap.h
- DEFINE_UTLMAP - Defines a CUtlMap
- game_shared\saverestore_utlrbtree.h
- DEFINE_UTLRBTREE - Defines a CUtlRBTree
- game_shared\saverestore_utlvector.h
- DEFINE_UTLVECTOR - Defines a CUtlVector
- game_shared\soundenvelope.h
- DEFINE_SOUNDPATCH - Defines a CSoundPatch
- public\saverestore.h
- DEFINE_ENTITY_FIELD
- DEFINE_ENTITY_GLOBAL_FIELD
- DEFINE_GLOBAL_FIELD
- DEFINE_GLOBAL_KEYFIELD
- DEFINE_AUTO_ARRAY2D
- DEFINE_EMBEDDED_OVERRIDE
- DEFINE_EMBEDDEDBYREF
- DEFINE_EMBEDDED_ARRAY
- DEFINE_EMBEDDED_AUTO_ARRAY
- DEFINE_PRED_TYPEDESCRIPTION
- DEFINE_PRED_TYPEDESCRIPTION_PTR
- DEFINE_PRED_FIELD
- DEFINE_PRED_ARRAY
- DEFINE_FIELD_NAME
- DEFINE_PRED_FIELD_TOL
- DEFINE_PRED_ARRAY_TOL
- DEFINE_FIELD_NAME_TOL
- public\stdstring.h
- DEFINE_STDSTRING - Defines a std::string.