Temporary Entity

From Valve Developer Community
Jump to: navigation, search

Temporary entities are used by the server to create short-lived or one-off effects on clients. They are different to standard entities in that they are 'fire and forget'; once one has been created, the server has nothing more to do with it. TEs have no edict or entity index, and do not count toward the entity limit.

TEs are unreliable and get dropped if too many are created at once. The maximum per update is 32 in multiplayer and 255 in single player. To do: What effect does MakeReliable() have here?

Implementation

On the server:

  • Derive from CBaseTempEntity in basetempentity.h
  • Create a constructor that accepts const char *name, and that constructs CBaseTempEntity with the same parameter (see the example below for syntax)
  • Create a global 'singleton' instance to spawn further instances (see g_TEMyEffect in example)
  • Create a global function that fills in any requisite data then calls Create( IRecipientFilter filter, float delay )

On the client:

  • Derive from C_BaseTempEntity in c_basetempentity.h
  • Create void PostDataUpdate( DataUpdateType_t updateType ), which is called whenever an instance spawns

Shared/predicted TEs

Temp entities are used quite often, also in shared game code (same code that is compiled into server.dll and client.dll). For shared code we need a shared interface that allows creating temp entities the same way on the server as on the client. This common interface is ITempEntsSystem that provides member functions to create any temp entity. The ITempEntsSystem interface is implemented server-side by class CTempEntsSystem and class C_TempEntsSystem in client code. For new temp entity classes, this interface has to be extended as well as both implementations.

For shared predicted code these classes also handle effect suppression for clients that already spawned the effect in their own prediction code. E.g.: A client fires a predicted weapon and creates instantly a local, predicted impact effect. When the server runs the same weapon code again, this impact effect must be sent to all other clients, but not to the shooting client. Filtering these temp entities that are spawned by predicted code is handled by function SuppressTE().

Example

Server

#include "cbase.h"
#include "basetempentity.h"

class CTEMyEffect : public CBaseTempEntity
{
	DECLARE_CLASS( CTEMyEffect, CBaseTempEntity );
	DECLARE_SERVERCLASS();

	// Constructor
	CTEMyEffect( const char *name ) : CBaseTempEntity( name ) {}

	CNetworkVector( m_vecPosition );
};

// Declare server send table
IMPLEMENT_SERVERCLASS_ST(CTEMyEffect, DT_TEMyEffect)
	SendPropVector( SENDINFO(m_vecPosition), -1, SPROP_COORD),
END_SEND_TABLE()

// Singleton to fire TEMyEffect objects
static CTEMyEffect g_TEMyEffect ( "MyEffect's name" );

// global function to spawn an instance
void TE_MyEffect( IRecipientFilter& filter, float delay, const Vector* position )
{
	// set effect data
	g_TEMyEffect.m_vecPosition = *position;

	// Send it over the wire
	g_TEMyEffect.Create( filter, delay );
}

Client

#include "cbase.h"
#include "c_basetempentity.h"

class C_TEMyEffect : public C_BaseTempEntity
{
	DECLARE_CLASS( C_TEMyEffect, C_BaseTempEntity );
	DECLARE_CLIENTCLASS();

	void PostDataUpdate( DataUpdateType_t updateType )
	{
		// temp entity has been spawned, do something
		Msg("Created effect at position %.1f,%.1f,%.1f\n", m_vecPosition[0], m_vecPosition[1], m_vecPosition[2] );
	}

	Vector	m_vecPosition;
};


// declare the client receive table
IMPLEMENT_CLIENTCLASS_EVENT_DT(C_TEMyEffect, DT_TEMyEffect, CTEMyEffect)
	RecvPropVector( RECVINFO(m_vecPosition)),
END_RECV_TABLE()

See also