Data Descriptions

From Valve Developer Community
Jump to: navigation, search
Русский

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.

Note:The Preprocessor directive 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.

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!

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. Here is a list of all field types and how they can be used in the various elements in a data description:

Name Description Can be saved and transition Can be used as a keyfield Can be used as an input
FIELD_VOID
No type or value. Used for functions without parameters. N/A N/A Yes
FIELD_FLOAT
Any floating point value. Yes Yes Yes
FIELD_STRING
A string_t. Cannot use char or any other form of text directly. (return from ALLOC_STRING) Yes Yes Yes
FIELD_VECTOR
Any vector, QAngle, or AngularImpulse. Yes Yes Yes
FIELD_QUATERNION
A quaternion value. Few references have been found relating to this field type. Not known Not known Not known
FIELD_INTEGER
Any integer or enum value. Yes Yes Yes
FIELD_BOOLEAN
Boolean value (represented as an integer) Yes Yes Yes
FIELD_SHORT
2 byte integer, or short Yes Maybe Maybe
FIELD_CHARACTER
One byte Yes Maybe Maybe
FIELD_COLOR32
8-bit per channel [R,G,B,A] (32-bit color) Yes Yes Yes
FIELD_EMBEDDED
class/structure based on an additional type description. Use DEFINE_EMBEDDED with this. N/A N/A N/A
FIELD_CUSTOM
A special type that contains function pointers to its read/write/parse functions (Use DEFINE_CUSTOM_FIELD instead) N/A N/A N/A
FIELD_CLASSPTR
CBaseEntity pointer. FIELD_EHANDLE is usually more preferable. Yes No Maybe
FIELD_EHANDLE
CHandle or EHANDLE.
Tip:Input functions and keyfields automatically convert strings to FIELD_EHANDLE by searching for an entity with a name that matches the string. Unfortunately, they don't support !activator or !caller.
Yes Yes Yes
FIELD_EDICT
edict_t pointer. Yes No No
FIELD_POSITION_VECTOR
A world coordinate value, which is fixed up across level-transitions automatically. Use FIELD_VECTOR for simple, portable vectors. Yes Yes No
FIELD_TIME
A floating point time value, which is fixed up across level-transitions automatically. Use this for floats used in gpGlobals->curtime calculations. Yes Yes No
FIELD_TICK
An integer tick count, which is fixed up similarly to FIELD_TIME. Yes Yes No
FIELD_MODELNAME
Engine string that is a model name.
Confirm:Is this precached automatically?
Yes Yes No
FIELD_SOUNDNAME
Engine string that is a sound name.
Confirm:Is this precached automatically?
Yes Yes No
FIELD_INPUT
This can be used with inputted data fields derived from CMultiInputVar, but it's usually used to take an input parameter without converting it. With CMultiInputVar Maybe Yes
FIELD_FUNCTION
A pointer to a class function. (Think, Use, etc.) Yes No No
FIELD_VMATRIX
Output coordinates are NOT worldspace. Need more information. Yes Yes No
FIELD_VMATRIX_WORLDSPACE
A VMatrix that maps some localspace to worldspace (translation is fixed up on level-transitions) Yes Yes No
FIELD_MATRIX3X4_WORLDSPACE
matrix3x4_t that maps some localspace to worldspace (translation is fixed up on level-transitions) Yes Yes No
FIELD_INTERVAL
A start and range floating point interval ( e.g., 3.2->3.6 == 3.2 and 0.4 ) Yes No No
FIELD_MODELINDEX
A model index Yes No No
FIELD_MATERIALINDEX
A material index (using the material precache string table) Yes No No

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()

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" )
Note:HammerName is case insensitive.

You could also look into KeyValue(), which allows programmers to do something--like use a specific kind of parsing or invoke a function--when a keyvalue is received. For example, CBaseEntity::KeyValue() in baseentity_shared.cpp is where these keyfields are handled in the first place. You could also look into custom save/restore, which supports custom keyfield parsing.

Tip:If you have a keyfield that specifies an entity, you could just use FIELD_EHANDLE and any string passed to it in Hammer will automatically search for an entity by that name and set it as an entity handle. However, you could also store the name in a 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")
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 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, 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
  • game_shared\physics_saverestore.h
    • DEFINE_PHYSPTR - Defines a physics pointer
    • DEFINE_PHYSPTR_ARRAY - Defines a physics pointer array
  • game_shared\saverestore_bitstring.h
  • game_shared\saverestore_utlmap.h
  • game_shared\saverestore_utlrbtree.h
  • game_shared\saverestore_utlvector.h
  • game_shared\soundenvelope.h
  • 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