You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1234 lines
32 KiB
1234 lines
32 KiB
//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
// GameEventManager.cpp: implementation of the CGameEventManager class.
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "GameEventManager.h"
|
|
#include "filesystem_engine.h"
|
|
#include "server.h"
|
|
#include "client.h"
|
|
#include "tier0/vprof.h"
|
|
#include "cl_splitscreen.h"
|
|
#include "tier1/tokenset.h"
|
|
#include "gameeventtransmitter.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#define NETWORKED_TYPE_BITS 4
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
static CGameEventManager s_GameEventManager;
|
|
CGameEventManager &g_GameEventManager = s_GameEventManager;
|
|
|
|
static const tokenset_t< int > s_GameListenerTypeMap[] =
|
|
{
|
|
{ "SERVERSIDE", CGameEventManager::SERVERSIDE }, // this is a server side listener, event logger etc
|
|
{ "CLIENTSIDE", CGameEventManager::CLIENTSIDE }, // this is a client side listenet, HUD element etc
|
|
{ "CLIENTSTUB", CGameEventManager::CLIENTSTUB }, // this is a serverside stub for a remote client listener (used by engine only)
|
|
{ "SERVERSIDE_OLD", CGameEventManager::SERVERSIDE_OLD }, // legacy support for old server event listeners
|
|
{ "CLIENTSIDE_OLD", CGameEventManager::CLIENTSIDE_OLD }, // legecy support for old client event listeners
|
|
{ NULL, -1 }
|
|
};
|
|
|
|
static const tokenset_t< int > s_GameEventTypesMap[] =
|
|
{
|
|
{ "local", CGameEventManager::TYPE_LOCAL }, // 0 : don't network this field
|
|
{ "string", CGameEventManager::TYPE_STRING }, // 1 : zero terminated ASCII string
|
|
{ "float", CGameEventManager::TYPE_FLOAT }, // 2 : float 32 bit
|
|
{ "long", CGameEventManager::TYPE_LONG }, // 3 : signed int 32 bit
|
|
{ "short", CGameEventManager::TYPE_SHORT }, // 4 : signed int 16 bit
|
|
{ "byte", CGameEventManager::TYPE_BYTE }, // 5 : unsigned int 8 bit
|
|
{ "bool", CGameEventManager::TYPE_BOOL }, // 6 : unsigned int 1 bit
|
|
{ "uint64", CGameEventManager::TYPE_UINT64 }, // 7 : unsigned int 64 bit
|
|
{ "wstring", CGameEventManager::TYPE_WSTRING }, // 8 : zero terminated wide char string
|
|
{ NULL, -1 }
|
|
};
|
|
|
|
static ConVar net_showevents( "net_showevents", "0", 0, "Dump game events to console (1=client only, 2=all)." );
|
|
static ConVar net_showeventlisteners( "net_showeventlisteners", "0", 0, "Show listening addition/removals" );
|
|
|
|
// Expose CVEngineServer to the engine.
|
|
|
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CGameEventManager, IGameEventManager2, INTERFACEVERSION_GAMEEVENTSMANAGER2, s_GameEventManager );
|
|
|
|
CGameEvent::CGameEvent( CGameEventDescriptor *descriptor, const char *name )
|
|
{
|
|
Assert( descriptor );
|
|
|
|
m_pDescriptor = descriptor;
|
|
m_pDataKeys = new KeyValues( name );
|
|
m_pDataKeys->SetInt( "splitscreenplayer", GET_ACTIVE_SPLITSCREEN_SLOT() );
|
|
}
|
|
|
|
CGameEvent::~CGameEvent()
|
|
{
|
|
m_pDataKeys->deleteThis();
|
|
}
|
|
|
|
bool CGameEvent::GetBool( const char *keyName, bool defaultValue) const
|
|
{
|
|
return m_pDataKeys->GetInt( keyName, defaultValue ) != 0;
|
|
}
|
|
|
|
int CGameEvent::GetInt( const char *keyName, int defaultValue) const
|
|
{
|
|
return m_pDataKeys->GetInt( keyName, defaultValue );
|
|
}
|
|
|
|
uint64 CGameEvent::GetUint64( const char *keyName, uint64 defaultValue) const
|
|
{
|
|
return m_pDataKeys->GetUint64( keyName, defaultValue );
|
|
}
|
|
|
|
float CGameEvent::GetFloat( const char *keyName, float defaultValue ) const
|
|
{
|
|
return m_pDataKeys->GetFloat( keyName, defaultValue );
|
|
}
|
|
|
|
const char *CGameEvent::GetString( const char *keyName, const char *defaultValue ) const
|
|
{
|
|
return m_pDataKeys->GetString( keyName, defaultValue );
|
|
}
|
|
|
|
const void *CGameEvent::GetPtr( const char *keyName ) const
|
|
{
|
|
Assert( IsLocal() );
|
|
return m_pDataKeys->GetPtr( keyName );
|
|
}
|
|
|
|
const wchar_t *CGameEvent::GetWString( const char *keyName, const wchar_t *defaultValue ) const
|
|
{
|
|
return m_pDataKeys->GetWString( keyName, defaultValue );
|
|
}
|
|
|
|
|
|
void CGameEvent::SetBool( const char *keyName, bool value )
|
|
{
|
|
m_pDataKeys->SetInt( keyName, value?1:0 );
|
|
}
|
|
|
|
void CGameEvent::SetInt( const char *keyName, int value )
|
|
{
|
|
m_pDataKeys->SetInt( keyName, value );
|
|
}
|
|
|
|
void CGameEvent::SetUint64( const char *keyName, uint64 value )
|
|
{
|
|
m_pDataKeys->SetUint64( keyName, value );
|
|
}
|
|
|
|
void CGameEvent::SetFloat( const char *keyName, float value )
|
|
{
|
|
m_pDataKeys->SetFloat( keyName, value );
|
|
}
|
|
|
|
void CGameEvent::SetString( const char *keyName, const char *value )
|
|
{
|
|
m_pDataKeys->SetString( keyName, value );
|
|
}
|
|
|
|
void CGameEvent::SetPtr( const char *keyName, const void *value )
|
|
{
|
|
Assert( IsLocal() );
|
|
m_pDataKeys->SetPtr( keyName, const_cast< void *>( value ) );
|
|
}
|
|
|
|
void CGameEvent::SetWString( const char* keyName, const wchar_t* value )
|
|
{
|
|
m_pDataKeys->SetWString( keyName, value );
|
|
}
|
|
|
|
bool CGameEvent::IsEmpty( const char *keyName ) const
|
|
{
|
|
return m_pDataKeys->IsEmpty( keyName );
|
|
}
|
|
|
|
const char *CGameEvent::GetName() const
|
|
{
|
|
return m_pDataKeys->GetName();
|
|
}
|
|
|
|
bool CGameEvent::IsLocal() const
|
|
{
|
|
return m_pDescriptor->local;
|
|
}
|
|
|
|
bool CGameEvent::IsReliable() const
|
|
{
|
|
return m_pDescriptor->reliable;
|
|
}
|
|
|
|
bool CGameEvent::ForEventData( IGameEventVisitor2* visitor ) const
|
|
{
|
|
CGameEventDescriptor *descriptor = m_pDescriptor;
|
|
|
|
bool iterate = true;
|
|
for ( KeyValues* key = descriptor->keys->GetFirstSubKey(); key && iterate; key = key->GetNextKey() )
|
|
{
|
|
const char * keyName = key->GetName();
|
|
|
|
int type = key->GetInt();
|
|
|
|
// see s_GameEventTypesMap for index
|
|
switch ( type )
|
|
{
|
|
case CGameEventManager::TYPE_LOCAL: iterate = visitor->VisitLocal( keyName, GetPtr( keyName ) ); break;
|
|
case CGameEventManager::TYPE_STRING: iterate = visitor->VisitString( keyName, GetString( keyName, "" ) ); break;
|
|
case CGameEventManager::TYPE_FLOAT: iterate = visitor->VisitFloat( keyName, GetFloat( keyName, 0.0f ) ); break;
|
|
case CGameEventManager::TYPE_LONG: iterate = visitor->VisitInt( keyName, GetInt( keyName, 0 ) ); break;
|
|
case CGameEventManager::TYPE_SHORT: iterate = visitor->VisitInt( keyName, GetInt( keyName, 0 ) ); break;
|
|
case CGameEventManager::TYPE_BYTE: iterate = visitor->VisitInt( keyName, GetInt( keyName, 0 ) ); break;
|
|
case CGameEventManager::TYPE_BOOL: iterate = visitor->VisitBool( keyName, GetBool( keyName, false ) ); break;
|
|
case CGameEventManager::TYPE_UINT64: iterate = visitor->VisitUint64( keyName, GetUint64( keyName, 0 ) ); break;
|
|
case CGameEventManager::TYPE_WSTRING: iterate = visitor->VisitWString( keyName, GetWString( keyName, L"" ) ); break;
|
|
}
|
|
}
|
|
|
|
return iterate;
|
|
}
|
|
|
|
CGameEventManager::CGameEventManager()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
CGameEventManager::~CGameEventManager()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
bool CGameEventManager::Init()
|
|
{
|
|
Reset();
|
|
|
|
LoadEventsFromFile( "resource/serverevents.res" );
|
|
|
|
g_GameEventTransmitter.Init();
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGameEventManager::Shutdown()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void CGameEventManager::Reset()
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
int number = m_GameEvents.Count();
|
|
|
|
for (int i = 0; i<number; i++)
|
|
{
|
|
CGameEventDescriptor &e = m_GameEvents.Element( i );
|
|
|
|
if ( e.keys )
|
|
{
|
|
e.keys->deleteThis(); // free the value keys
|
|
e.keys = NULL;
|
|
}
|
|
|
|
e.listeners.Purge(); // remove listeners
|
|
}
|
|
|
|
m_GameEvents.Purge();
|
|
m_Listeners.PurgeAndDeleteElements();
|
|
m_EventFiles.RemoveAll();
|
|
m_EventFileNames.RemoveAll();
|
|
m_EventMap.Purge();
|
|
m_bClientListenersChanged = true;
|
|
|
|
Assert( m_GameEvents.Count() == 0 );
|
|
}
|
|
|
|
bool CGameEventManager::HasClientListenersChanged( bool bReset /* = true */)
|
|
{
|
|
if ( !m_bClientListenersChanged )
|
|
return false;
|
|
|
|
if ( bReset )
|
|
m_bClientListenersChanged = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGameEventManager::WriteEventList(CSVCMsg_GameEventList *msg)
|
|
{
|
|
for (int i=0; i < m_GameEvents.Count(); i++ )
|
|
{
|
|
CGameEventDescriptor &descriptor = m_GameEvents[i];
|
|
|
|
if ( descriptor.local )
|
|
continue;
|
|
|
|
Assert( descriptor.eventid >= 0 && descriptor.eventid < MAX_EVENT_NUMBER );
|
|
|
|
CSVCMsg_GameEventList::descriptor_t *pDescriptor = msg->add_descriptors();
|
|
const char *pName = m_EventMap.GetElementName( descriptor.elementIndex );
|
|
|
|
pDescriptor->set_eventid( descriptor.eventid );
|
|
pDescriptor->set_name( pName );
|
|
|
|
KeyValues *key = descriptor.keys->GetFirstSubKey();
|
|
|
|
while ( key )
|
|
{
|
|
int type = key->GetInt();
|
|
|
|
if ( type != TYPE_LOCAL )
|
|
{
|
|
CSVCMsg_GameEventList::key_t *pKey = pDescriptor->add_keys();
|
|
|
|
pKey->set_type( type );
|
|
pKey->set_name( key->GetName() );
|
|
}
|
|
|
|
key = key->GetNextKey();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CGameEventManager::ParseEventList(const CSVCMsg_GameEventList& msg)
|
|
{
|
|
int i;
|
|
AUTO_LOCK_FM( m_mutex );
|
|
|
|
// reset eventids to -1 first
|
|
for ( i=0; i < m_GameEvents.Count(); i++ )
|
|
{
|
|
CGameEventDescriptor &descriptor = m_GameEvents[i];
|
|
descriptor.eventid = -1;
|
|
}
|
|
|
|
// map server event IDs
|
|
int nNumEvents = msg.descriptors_size();
|
|
for (i = 0; i<nNumEvents; i++)
|
|
{
|
|
const CSVCMsg_GameEventList::descriptor_t& EventDescriptor = msg.descriptors( i );
|
|
|
|
const char *name = EventDescriptor.name().c_str();
|
|
|
|
CGameEventDescriptor *descriptor = GetEventDescriptor( name );
|
|
|
|
// if event is known to client...
|
|
if ( descriptor )
|
|
{
|
|
descriptor->eventid = EventDescriptor.eventid();
|
|
|
|
// remove old definition list
|
|
if ( descriptor->keys )
|
|
descriptor->keys->deleteThis();
|
|
|
|
descriptor->keys = new KeyValues("descriptor");
|
|
|
|
int nNumKeys = EventDescriptor.keys_size();
|
|
for (int key = 0; key < nNumKeys; key++)
|
|
{
|
|
const CSVCMsg_GameEventList::key_t &Key = EventDescriptor.keys( key );
|
|
|
|
descriptor->keys->SetInt( Key.name().c_str(), Key.type() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// force client to answer what events he listens to
|
|
m_bClientListenersChanged = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGameEventManager::WriteListenEventList(CCLCMsg_ListenEvents *msg)
|
|
{
|
|
CBitVec<MAX_EVENT_NUMBER> EventArray;
|
|
|
|
EventArray.ClearAll();
|
|
|
|
// and know tell the server what events we want to listen to
|
|
for (int i=0; i < m_GameEvents.Count(); i++ )
|
|
{
|
|
CGameEventDescriptor &descriptor = m_GameEvents[i];
|
|
|
|
if ( descriptor.local )
|
|
{
|
|
continue; // event isn't networked
|
|
}
|
|
|
|
bool bHasClientListener = false;
|
|
|
|
for ( int j=0; j<descriptor.listeners.Count(); j++ )
|
|
{
|
|
CGameEventCallback *listener = descriptor.listeners[j];
|
|
|
|
if ( listener->m_nListenerType == CGameEventManager::CLIENTSIDE ||
|
|
listener->m_nListenerType == CGameEventManager::CLIENTSIDE_OLD )
|
|
{
|
|
// if we have a client side listener and server knows this event, add it
|
|
bHasClientListener = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !bHasClientListener )
|
|
continue;
|
|
|
|
if ( descriptor.eventid == -1 )
|
|
{
|
|
const char *pName = m_EventMap.GetElementName(descriptor.elementIndex);
|
|
DevMsg("Warning! Client listens to event '%s' unknown by server.\n", pName );
|
|
continue;
|
|
}
|
|
|
|
EventArray.Set( descriptor.eventid );
|
|
}
|
|
|
|
int count = ( m_GameEvents.Count() + 31 ) / 32;
|
|
for( int i = 0; i < count; i++ )
|
|
{
|
|
msg->add_event_mask( EventArray.GetDWord( i ) );
|
|
}
|
|
}
|
|
|
|
IGameEvent *CGameEventManager::CreateEvent( CGameEventDescriptor *descriptor, const char *name )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
return new CGameEvent ( descriptor, name );
|
|
}
|
|
|
|
IGameEvent *CGameEventManager::CreateEvent( const char *name, bool bForce, int *pCookie )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
if ( !name || !name[0] )
|
|
return NULL;
|
|
|
|
CGameEventDescriptor *descriptor = GetEventDescriptor( name, pCookie );
|
|
|
|
// check if this event name is known
|
|
if ( !descriptor )
|
|
{
|
|
DevMsg( "CreateEvent: event '%s' not registered.\n", name );
|
|
return NULL;
|
|
}
|
|
|
|
// event is known but no one listen to it
|
|
if ( descriptor->listeners.Count() == 0 && !bForce )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// create & return the new event
|
|
return new CGameEvent ( descriptor, name );
|
|
}
|
|
|
|
ConVar display_game_events("display_game_events", "0", FCVAR_CHEAT );
|
|
bool CGameEventManager::FireEvent( IGameEvent *event, bool bServerOnly )
|
|
{
|
|
if( display_game_events.GetBool() )
|
|
{
|
|
Msg("Game Event Fired: %s\n", event->GetName() );
|
|
}
|
|
|
|
return FireEventIntern( event, bServerOnly, false );
|
|
}
|
|
|
|
bool CGameEventManager::FireEventClientSide( IGameEvent *event )
|
|
{
|
|
return FireEventIntern( event, false, true );
|
|
}
|
|
|
|
IGameEvent *CGameEventManager::DuplicateEvent( IGameEvent *event )
|
|
{
|
|
CGameEvent *gameEvent = dynamic_cast<CGameEvent*>(event);
|
|
|
|
if ( !gameEvent )
|
|
return NULL;
|
|
|
|
// create new instance
|
|
const char *pName = m_EventMap.GetElementName(gameEvent->m_pDescriptor->elementIndex );
|
|
CGameEvent *newEvent = new CGameEvent ( gameEvent->m_pDescriptor, pName );
|
|
|
|
// free keys
|
|
newEvent->m_pDataKeys->deleteThis();
|
|
|
|
// and make copy
|
|
newEvent->m_pDataKeys = gameEvent->m_pDataKeys->MakeCopy();
|
|
|
|
return newEvent;
|
|
}
|
|
|
|
void CGameEventManager::ConPrintEvent( IGameEvent *event)
|
|
{
|
|
CGameEventDescriptor *descriptor = GetEventDescriptor( event );
|
|
|
|
if ( !descriptor )
|
|
return;
|
|
|
|
KeyValues *key = descriptor->keys->GetFirstSubKey();
|
|
|
|
while ( key )
|
|
{
|
|
const char * keyName = key->GetName();
|
|
|
|
int type = key->GetInt();
|
|
|
|
switch ( type )
|
|
{
|
|
case TYPE_LOCAL : ConMsg( "- \"%s\" = \"%s\" (local)\n", keyName, event->GetString(keyName) ); break;
|
|
case TYPE_STRING : ConMsg( "- \"%s\" = \"%s\"\n", keyName, event->GetString(keyName) ); break;
|
|
case TYPE_WSTRING : ConMsg( "- \"%s\" = \"" PRI_WS_FOR_S "\"\n", keyName, event->GetWString(keyName) ); break;
|
|
case TYPE_FLOAT : ConMsg( "- \"%s\" = \"%.2f\"\n", keyName, event->GetFloat(keyName) ); break;
|
|
default: ConMsg( "- \"%s\" = \"%i\"\n", keyName, event->GetInt(keyName) ); break;
|
|
}
|
|
key = key->GetNextKey();
|
|
}
|
|
}
|
|
|
|
bool CGameEventManager::FireEventIntern( IGameEvent *event, bool bServerOnly, bool bClientOnly )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
if ( event == NULL )
|
|
return false;
|
|
|
|
Assert( !(bServerOnly && bClientOnly) ); // it can't be both
|
|
|
|
VPROF_("CGameEventManager::FireEvent", 1, VPROF_BUDGETGROUP_OTHER_UNACCOUNTED, false,
|
|
bClientOnly ? BUDGETFLAG_CLIENT : ( bServerOnly ? BUDGETFLAG_SERVER : BUDGETFLAG_OTHER ) );
|
|
|
|
CGameEventDescriptor *descriptor = GetEventDescriptor( event );
|
|
|
|
if ( descriptor == NULL )
|
|
{
|
|
DevMsg( "FireEvent: event '%s' not registered.\n", event->GetName() );
|
|
FreeEvent( event );
|
|
return false;
|
|
}
|
|
|
|
// show game events in console
|
|
if ( net_showevents.GetInt() > 0 )
|
|
{
|
|
if ( bClientOnly )
|
|
{
|
|
#ifndef DEDICATED
|
|
const char *pName = m_EventMap.GetElementName(descriptor->elementIndex);
|
|
ConMsg( "Game event \"%s\", Tick %i:\n", pName, GetBaseLocalClient().GetClientTickCount() );
|
|
ConPrintEvent( event );
|
|
#endif
|
|
}
|
|
else if ( net_showevents.GetInt() > 1 )
|
|
{
|
|
const char *pName = m_EventMap.GetElementName(descriptor->elementIndex);
|
|
ConMsg( "Server event \"%s\", Tick %i:\n", pName, sv.GetTick() );
|
|
ConPrintEvent( event );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
for ( int i = 0; i < descriptor->listeners.Count(); i++ )
|
|
{
|
|
CGameEventCallback *listener = descriptor->listeners.Element( i );
|
|
|
|
Assert ( listener );
|
|
|
|
// don't trigger server listners for clientside only events
|
|
if ( ( listener->m_nListenerType == SERVERSIDE ||
|
|
listener->m_nListenerType == SERVERSIDE_OLD ) &&
|
|
bClientOnly )
|
|
continue;
|
|
|
|
// don't trigger clientside events, if not explicit a clientside event
|
|
if ( ( listener->m_nListenerType == CLIENTSIDE ||
|
|
listener->m_nListenerType == CLIENTSIDE_OLD ) &&
|
|
!bClientOnly )
|
|
continue;
|
|
|
|
// don't broadcast events if server side only
|
|
if ( listener->m_nListenerType == CLIENTSTUB && (bServerOnly || bClientOnly) )
|
|
continue;
|
|
|
|
// if the event is local, don't tell clients about it
|
|
if ( listener->m_nListenerType == CLIENTSTUB && descriptor->local )
|
|
continue;
|
|
|
|
// TODO optimized the serialize event for clients, call only once and not per client
|
|
|
|
// fire event in this listener module
|
|
if ( listener->m_nListenerType == CLIENTSIDE_OLD ||
|
|
listener->m_nListenerType == SERVERSIDE_OLD )
|
|
{
|
|
// legacy support for old system
|
|
IGameEventListener *pCallback = static_cast<IGameEventListener*>(listener->m_pCallback);
|
|
CGameEvent *pEvent = static_cast<CGameEvent*>(event);
|
|
|
|
pCallback->FireGameEvent( pEvent->m_pDataKeys );
|
|
}
|
|
else
|
|
{
|
|
// new system
|
|
IGameEventListener2 *pCallback = static_cast<IGameEventListener2*>(listener->m_pCallback);
|
|
Assert( pCallback );
|
|
if ( pCallback )
|
|
{
|
|
if ( pCallback->GetEventDebugID() != EVENT_DEBUG_ID_INIT )
|
|
{
|
|
Msg( "GameEventListener2 callback in list that should NOT be - %s!\n", event->GetName() );
|
|
Assert( 0 );
|
|
}
|
|
else
|
|
{
|
|
pCallback->FireGameEvent( event );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Warning( "Callback for event \"%s\" is NULL!!!\n", event->GetName() );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bClientOnly || ( !Q_stricmp( "portal2", COM_GetModDirectory() ) ) )
|
|
{
|
|
// Pass all client only events to the game event transmiter
|
|
g_GameEventTransmitter.TransmitGameEvent( event );
|
|
}
|
|
|
|
// free event resources
|
|
FreeEvent( event );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CGameEventManager::SerializeEvent( IGameEvent *event, CSVCMsg_GameEvent *eventMsg )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
CGameEventDescriptor *descriptor = GetEventDescriptor( event );
|
|
|
|
Assert( descriptor );
|
|
|
|
eventMsg->set_eventid( descriptor->eventid );
|
|
|
|
// now iterate trough all fields described in gameevents.res and put them in the buffer
|
|
|
|
KeyValues * key = descriptor->keys->GetFirstSubKey();
|
|
|
|
if ( net_showevents.GetInt() > 2 )
|
|
{
|
|
const char *pName = m_EventMap.GetElementName(descriptor->elementIndex);
|
|
DevMsg("Serializing event '%s' (%i):\n", pName, descriptor->eventid );
|
|
}
|
|
|
|
while ( key )
|
|
{
|
|
const char * keyName = key->GetName();
|
|
|
|
int type = key->GetInt();
|
|
|
|
if ( net_showevents.GetInt() > 2 )
|
|
{
|
|
DevMsg(" - %s (%s)\n", keyName, s_GameEventTypesMap->GetNameByToken( type ) );
|
|
}
|
|
|
|
if( type != TYPE_LOCAL )
|
|
{
|
|
CSVCMsg_GameEvent::key_t *pKey = eventMsg->add_keys();
|
|
|
|
//Make sure every key is used in the event
|
|
// Assert( event->FindKey(keyName) && "GameEvent field not found in passed KeyValues" );
|
|
|
|
pKey->set_type( type );
|
|
|
|
// see s_GameEventTypesMap for index
|
|
switch ( type )
|
|
{
|
|
case TYPE_STRING: pKey->set_val_string( event->GetString( keyName, "") ); break;
|
|
case TYPE_FLOAT : pKey->set_val_float( event->GetFloat( keyName, 0.0f) ); break;
|
|
case TYPE_LONG : pKey->set_val_long( event->GetInt( keyName, 0) ); break;
|
|
case TYPE_SHORT : pKey->set_val_short( event->GetInt( keyName, 0) ); break;
|
|
case TYPE_BYTE : pKey->set_val_byte( event->GetInt( keyName, 0) ); break;
|
|
case TYPE_BOOL : pKey->set_val_bool( !!event->GetInt( keyName, 0) ); break;
|
|
case TYPE_UINT64: pKey->set_val_uint64( event->GetUint64( keyName, 0) ); break;
|
|
case TYPE_WSTRING:
|
|
{
|
|
const wchar_t *pStr = event->GetWString( keyName, L"");
|
|
pKey->set_val_wstring( pStr, wcslen( pStr ) + 1 );
|
|
}
|
|
break;
|
|
default: DevMsg(1, "CGameEventManager: unknown type %i for key '%s'.\n", type, key->GetName() ); break;
|
|
}
|
|
}
|
|
|
|
key = key->GetNextKey();
|
|
}
|
|
|
|
if ( net_showevents.GetInt() > 2 )
|
|
{
|
|
int nBytes = eventMsg->ByteSize();
|
|
Msg( " took %d bits, %d bytes\n", nBytes * 8, nBytes );
|
|
}
|
|
|
|
descriptor->numSerialized++;
|
|
descriptor->totalSerializedBits += eventMsg->ByteSize() * 8;
|
|
|
|
return true;
|
|
}
|
|
|
|
IGameEvent *CGameEventManager::UnserializeEvent( const CSVCMsg_GameEvent& eventMsg )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
|
|
// read event id
|
|
|
|
int eventid = eventMsg.eventid();
|
|
|
|
// get event description
|
|
CGameEventDescriptor *descriptor = GetEventDescriptor( eventid );
|
|
|
|
if ( descriptor == NULL )
|
|
{
|
|
DevMsg( "CGameEventManager::UnserializeEvent:: unknown event id %i.\n", eventid );
|
|
return NULL;
|
|
}
|
|
|
|
// create new event
|
|
const char *pName = m_EventMap.GetElementName(descriptor->elementIndex);
|
|
IGameEvent *event = CreateEvent( descriptor, pName );
|
|
|
|
if ( !event )
|
|
{
|
|
DevMsg( "CGameEventManager::UnserializeEvent:: failed to create event %s.\n", pName );
|
|
return NULL;
|
|
}
|
|
|
|
int nNumKeys = eventMsg.keys_size();
|
|
KeyValues * key = descriptor->keys->GetFirstSubKey();
|
|
|
|
for( int i = 0; key && ( i < nNumKeys ); i++, key = key->GetNextKey() )
|
|
{
|
|
const CSVCMsg_GameEvent::key_t &KeyEvent = eventMsg.keys( i );
|
|
const char * keyName = key->GetName();
|
|
|
|
int type = KeyEvent.type();
|
|
|
|
switch ( type )
|
|
{
|
|
case TYPE_LOCAL : break; // ignore
|
|
case TYPE_STRING : event->SetString( keyName, KeyEvent.val_string().c_str() ); break;
|
|
case TYPE_FLOAT : event->SetFloat( keyName, KeyEvent.val_float() ); break;
|
|
case TYPE_LONG : event->SetInt( keyName, KeyEvent.val_long() ); break;
|
|
case TYPE_SHORT : event->SetInt( keyName, KeyEvent.val_short() ); break;
|
|
case TYPE_BYTE : event->SetInt( keyName, KeyEvent.val_byte() ); break;
|
|
case TYPE_BOOL : event->SetInt( keyName, KeyEvent.val_bool() ); break;
|
|
case TYPE_UINT64 : event->SetUint64( keyName, KeyEvent.val_uint64() ); break;
|
|
case TYPE_WSTRING : event->SetWString( keyName, (wchar_t*)KeyEvent.val_wstring().data() ); break;
|
|
|
|
default: DevMsg(1, "CGameEventManager: unknown type %i for key '%s' [%s].\n", type, key->GetName(), pName ); break;
|
|
}
|
|
}
|
|
|
|
descriptor->numUnSerialized++;
|
|
descriptor->totalUnserializedBits += eventMsg.ByteSize() * 8;
|
|
|
|
return event;
|
|
}
|
|
|
|
KeyValues* CGameEventManager::GetEventDataTypes( IGameEvent* event )
|
|
{
|
|
if ( event == nullptr )
|
|
return nullptr;
|
|
|
|
CGameEventDescriptor* descriptor = GetEventDescriptor( event );
|
|
if ( descriptor == nullptr )
|
|
return nullptr;
|
|
|
|
return descriptor->keys;
|
|
}
|
|
|
|
// returns true if this listener is listens to given event
|
|
bool CGameEventManager::FindListener( IGameEventListener2 *listener, const char *name )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
CGameEventDescriptor *pDescriptor = GetEventDescriptor( name );
|
|
|
|
if ( !pDescriptor )
|
|
return false; // event is unknown
|
|
|
|
CGameEventCallback *pCallback = FindEventListener( listener );
|
|
|
|
if ( !pCallback )
|
|
return false; // listener is unknown
|
|
|
|
// see if listener is in the list for this event
|
|
return pDescriptor->listeners.IsValidIndex( pDescriptor->listeners.Find( pCallback ) );
|
|
}
|
|
|
|
CGameEventCallback* CGameEventManager::FindEventListener( void* pCallback )
|
|
{
|
|
for (int i=0; i < m_Listeners.Count(); i++ )
|
|
{
|
|
CGameEventCallback *listener = m_Listeners.Element(i);
|
|
|
|
if ( listener->m_pCallback == pCallback )
|
|
{
|
|
return listener;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CGameEventManager::RemoveListener(IGameEventListener2 *listener)
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
CGameEventCallback *pCallback = FindEventListener( listener );
|
|
|
|
if ( pCallback == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// remove reference from events
|
|
for (int i=0; i < m_GameEvents.Count(); i++ )
|
|
{
|
|
CGameEventDescriptor &et = m_GameEvents.Element( i );
|
|
et.listeners.FindAndRemove( pCallback );
|
|
}
|
|
|
|
// and from global list
|
|
m_Listeners.FindAndRemove( pCallback );
|
|
|
|
if ( pCallback->m_nListenerType == CLIENTSIDE )
|
|
{
|
|
m_bClientListenersChanged = true;
|
|
}
|
|
|
|
delete pCallback;
|
|
}
|
|
|
|
bool CGameEventManager::AddListenerGlobal( IGameEventListener2 *listener, bool bServerSide )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
|
|
if ( !listener )
|
|
return false;
|
|
|
|
for ( int i = 0; i < m_GameEvents.Count(); i++ )
|
|
{
|
|
CGameEventDescriptor *descriptor = &m_GameEvents[ i ];
|
|
|
|
AddListener( listener, descriptor, bServerSide ? SERVERSIDE : CLIENTSIDE );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int CGameEventManager::LoadEventsFromFile( const char * filename )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
if ( UTL_INVAL_SYMBOL == m_EventFiles.Find( filename ) )
|
|
{
|
|
CUtlSymbol id = m_EventFiles.AddString( filename );
|
|
m_EventFileNames.AddToTail( id );
|
|
}
|
|
|
|
KeyValues * key = new KeyValues(filename);
|
|
KeyValues::AutoDelete autodelete_key( key );
|
|
|
|
if ( !key->LoadFromFile( g_pFileSystem, filename, "GAME" ) )
|
|
return false;
|
|
|
|
int count = 0; // number new events
|
|
|
|
KeyValues * subkey = key->GetFirstSubKey();
|
|
|
|
while ( subkey )
|
|
{
|
|
if ( subkey->GetDataType() == KeyValues::TYPE_NONE )
|
|
{
|
|
RegisterEvent( subkey );
|
|
count++;
|
|
}
|
|
|
|
subkey = subkey->GetNextKey();
|
|
}
|
|
|
|
if ( net_showevents.GetBool() )
|
|
DevMsg( "Event System loaded %i events from file %s.\n", m_GameEvents.Count(), filename );
|
|
|
|
return m_GameEvents.Count();
|
|
}
|
|
|
|
void CGameEventManager::ReloadEventDefinitions()
|
|
{
|
|
for ( int i=0; i< m_EventFileNames.Count(); i++ )
|
|
{
|
|
const char *filename = m_EventFiles.String( m_EventFileNames[i] );
|
|
LoadEventsFromFile( filename );
|
|
}
|
|
|
|
// we are the server, build string table now
|
|
int number = m_GameEvents.Count();
|
|
|
|
for (int j = 0; j<number; j++)
|
|
{
|
|
m_GameEvents[j].eventid = j;
|
|
}
|
|
}
|
|
|
|
bool CGameEventManager::AddListener( IGameEventListener2 *listener, const char *event, bool bServerSide )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
if ( !event )
|
|
return false;
|
|
|
|
// look for the event descriptor
|
|
CGameEventDescriptor *descriptor = GetEventDescriptor( event );
|
|
|
|
if ( !descriptor )
|
|
{
|
|
Warning( "CGameEventManager::AddListener: event '%s' unknown.\n", event );
|
|
return false; // that should not happen
|
|
}
|
|
|
|
return AddListener( listener, descriptor, bServerSide ? SERVERSIDE : CLIENTSIDE );
|
|
}
|
|
|
|
bool CGameEventManager::AddListener( void *listener, CGameEventDescriptor *descriptor, int nListenerType )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
if ( !listener || !descriptor )
|
|
return false; // bahh
|
|
|
|
// check if we already know this listener
|
|
CGameEventCallback *pCallback = FindEventListener( listener );
|
|
|
|
if ( pCallback == NULL )
|
|
{
|
|
// add new callback
|
|
pCallback = new CGameEventCallback;
|
|
m_Listeners.AddToTail( pCallback );
|
|
|
|
pCallback->m_nListenerType = nListenerType;
|
|
pCallback->m_pCallback = listener;
|
|
}
|
|
else
|
|
{
|
|
// make sure that it hasn't changed:
|
|
Assert( pCallback->m_nListenerType == nListenerType );
|
|
Assert( pCallback->m_pCallback == listener );
|
|
}
|
|
|
|
// add to event listeners list if not already in there
|
|
if ( descriptor->listeners.Find( pCallback ) == descriptor->listeners.InvalidIndex() )
|
|
{
|
|
descriptor->listeners.AddToTail( pCallback );
|
|
|
|
if ( net_showeventlisteners.GetBool() )
|
|
{
|
|
const char *name = m_EventMap.GetElementName( descriptor->elementIndex );
|
|
Msg("[GAMEEVENT] Event '%s' added %s listener %p\n",
|
|
name ? name : "UNKNOWN",
|
|
s_GameListenerTypeMap->GetNameByToken( nListenerType ),
|
|
listener );
|
|
}
|
|
|
|
if ( nListenerType == CLIENTSIDE || nListenerType == CLIENTSIDE_OLD )
|
|
m_bClientListenersChanged = true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CGameEventManager::RegisterEvent( KeyValues * event)
|
|
{
|
|
if ( event == NULL )
|
|
return false;
|
|
|
|
AUTO_LOCK_FM( m_mutex );
|
|
if ( m_GameEvents.Count() == MAX_EVENT_NUMBER )
|
|
{
|
|
DevMsg( "CGameEventManager: couldn't register event '%s', limit reached (%i).\n",
|
|
event->GetName(), MAX_EVENT_NUMBER );
|
|
return false;
|
|
}
|
|
|
|
const char *name = event->GetName();
|
|
|
|
CGameEventDescriptor *descriptor = GetEventDescriptor( name );
|
|
|
|
if ( !descriptor )
|
|
{
|
|
// event not known yet, create new one
|
|
int index = m_GameEvents.AddToTail();
|
|
descriptor = &m_GameEvents.Element(index);
|
|
|
|
descriptor->elementIndex = m_EventMap.Insert( event->GetName(), index );
|
|
}
|
|
else
|
|
{
|
|
// descriptor already know, but delete old definitions
|
|
descriptor->keys->deleteThis();
|
|
}
|
|
|
|
// create new descriptor keys
|
|
descriptor->keys = new KeyValues("descriptor");
|
|
|
|
KeyValues *subkey = event->GetFirstSubKey();
|
|
|
|
// interate through subkeys
|
|
|
|
while ( subkey )
|
|
{
|
|
const char *keyName = subkey->GetName();
|
|
|
|
// ok, check it's data type
|
|
const char * type = subkey->GetString();
|
|
|
|
if ( !Q_strcmp( "local", keyName) )
|
|
{
|
|
descriptor->local = Q_atoi( type ) != 0;
|
|
}
|
|
else if ( !Q_strcmp( "reliable", keyName) )
|
|
{
|
|
descriptor->reliable = Q_atoi( type ) != 0;
|
|
}
|
|
else
|
|
{
|
|
int i = s_GameEventTypesMap->GetToken( type );
|
|
|
|
if ( i < 0 )
|
|
{
|
|
descriptor->keys->SetInt( keyName, 0 ); // unknown
|
|
DevMsg( "CGameEventManager:: unknown type '%s' for key '%s' [%s].\n", type, subkey->GetName(), name );
|
|
}
|
|
else
|
|
{
|
|
// set data type
|
|
descriptor->keys->SetInt( keyName, i ); // set data type
|
|
}
|
|
}
|
|
|
|
subkey = subkey->GetNextKey();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CGameEventDescriptor *CGameEventManager::GetEventDescriptor(IGameEvent *event)
|
|
{
|
|
CGameEvent *gameevent = dynamic_cast<CGameEvent*>(event);
|
|
|
|
if ( !gameevent )
|
|
return NULL;
|
|
|
|
return gameevent->m_pDescriptor;
|
|
}
|
|
|
|
CGameEventDescriptor *CGameEventManager::GetEventDescriptor(int eventid) // returns event name or NULL
|
|
{
|
|
if ( eventid < 0 )
|
|
return NULL;
|
|
|
|
for ( int i = 0; i < m_GameEvents.Count(); i++ )
|
|
{
|
|
CGameEventDescriptor *descriptor = &m_GameEvents[i];
|
|
|
|
if ( descriptor->eventid == eventid )
|
|
return descriptor;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CGameEventManager::FreeEvent( IGameEvent *event )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
if ( !event )
|
|
return;
|
|
|
|
delete event;
|
|
}
|
|
|
|
CGameEventDescriptor *CGameEventManager::GetEventDescriptor(const char * name, int *pCookie)
|
|
{
|
|
const uint32 cookieBit = 0x80000000;
|
|
const uint32 cookieMask = ~cookieBit;
|
|
|
|
if ( !name || !name[0] )
|
|
return NULL;
|
|
|
|
if ( pCookie && *pCookie )
|
|
{
|
|
int gameEventIndex = uint32(*pCookie) & cookieMask;
|
|
CGameEventDescriptor *pDescriptor = &m_GameEvents[gameEventIndex];
|
|
if ( !V_stricmp( m_EventMap.GetElementName(pDescriptor->elementIndex), name ) )
|
|
{
|
|
return pDescriptor;
|
|
}
|
|
}
|
|
int eventMapIndex = m_EventMap.Find( name );
|
|
if ( eventMapIndex == m_EventMap.InvalidIndex() )
|
|
return NULL;
|
|
int gameEventIndex = m_EventMap[eventMapIndex];
|
|
if ( pCookie )
|
|
{
|
|
*pCookie = cookieBit | gameEventIndex;
|
|
}
|
|
return &m_GameEvents[gameEventIndex];
|
|
}
|
|
|
|
bool CGameEventManager::AddListenerAll( void *listener, int nListenerType )
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
if ( !listener )
|
|
return false;
|
|
|
|
for (int i=0; i < m_GameEvents.Count(); i++ )
|
|
{
|
|
CGameEventDescriptor *descriptor = &m_GameEvents[i];
|
|
|
|
AddListener( listener, descriptor, nListenerType );
|
|
}
|
|
|
|
DevMsg("Warning! Game event listener registerd for all events. Use newer game event interface.\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGameEventManager::RemoveListenerOld( void *listener)
|
|
{
|
|
AUTO_LOCK_FM( m_mutex );
|
|
CGameEventCallback *pCallback = FindEventListener( listener );
|
|
|
|
if ( pCallback == NULL )
|
|
{
|
|
DevMsg("RemoveListenerOld: couldn't find listener\n");
|
|
return;
|
|
}
|
|
|
|
// remove reference from events
|
|
for (int i=0; i < m_GameEvents.Count(); i++ )
|
|
{
|
|
CGameEventDescriptor &et = m_GameEvents.Element( i );
|
|
et.listeners.FindAndRemove( pCallback );
|
|
}
|
|
|
|
// and from global list
|
|
m_Listeners.FindAndRemove( pCallback );
|
|
|
|
if ( pCallback->m_nListenerType == CLIENTSIDE_OLD )
|
|
{
|
|
m_bClientListenersChanged = true;
|
|
}
|
|
|
|
delete pCallback;
|
|
}
|
|
|
|
void CGameEventManager::VerifyListenerList( void )
|
|
{
|
|
int nGameEventCount = m_GameEvents.Count();
|
|
for ( int iEvent = 0; iEvent < nGameEventCount; ++iEvent )
|
|
{
|
|
CGameEventDescriptor *pEvent = &m_GameEvents.Element( iEvent );
|
|
const char *pName = m_EventMap.GetElementName( iEvent );
|
|
if ( !pEvent )
|
|
{
|
|
Msg( "VerifyListenerList-Bug: Bad event in list! (%d)\n", iEvent);
|
|
continue;
|
|
}
|
|
|
|
int nListenerCount = pEvent->listeners.Count();
|
|
for ( int iListen = 0; iListen < nListenerCount; ++iListen )
|
|
{
|
|
CGameEventCallback *pListener = pEvent->listeners.Element( iListen );
|
|
if ( !pListener)
|
|
{
|
|
Msg( "VerifyListenerList-Bug: Bad listener in list! (%s)\n", pName );
|
|
continue;
|
|
}
|
|
|
|
IGameEventListener2 *pCallback = static_cast<IGameEventListener2*>( pListener->m_pCallback );
|
|
if ( pCallback->GetEventDebugID() != EVENT_DEBUG_ID_INIT )
|
|
{
|
|
Msg( "VerifyListenerList-Bug: Bad callback in list! (%s)\n", pName );
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CGameEventManager::DumpEventNetworkStats( void )
|
|
{
|
|
// find longest name
|
|
int len = 0;
|
|
for ( int i = 0; i < m_GameEvents.Count(); ++i )
|
|
{
|
|
CGameEventDescriptor &e = m_GameEvents.Element( i );
|
|
const char *name = m_EventMap.GetElementName( e.elementIndex );
|
|
if ( !name )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int l = Q_strlen( name );
|
|
if ( l > len )
|
|
{
|
|
len = l;
|
|
}
|
|
}
|
|
|
|
// ----- ----- ------- ------- ------- -------
|
|
Msg( "%*s Out In OutBits InBits OutSize InSize Notes\n", len, "Name" );
|
|
// %5d %5d %7d %7d %7d %7d
|
|
|
|
|
|
for ( int i = 0; i < m_GameEvents.Count(); ++i )
|
|
{
|
|
CGameEventDescriptor &e = m_GameEvents.Element( i );
|
|
const char *name = m_EventMap.GetElementName( e.elementIndex );
|
|
if ( !name )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( !e.numSerialized && !e.numUnSerialized )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Msg( "%*s %5d %5d %7d %7d %7d %7d",
|
|
len,
|
|
name,
|
|
e.numSerialized,
|
|
e.numUnSerialized,
|
|
e.totalSerializedBits,
|
|
e.totalUnserializedBits,
|
|
e.numSerialized ? e.totalSerializedBits / e.numSerialized : 0,
|
|
e.numUnSerialized ? e.totalUnserializedBits / e.numUnSerialized : 0 );
|
|
if ( e.local )
|
|
{
|
|
Msg( " local" );
|
|
}
|
|
if ( e.reliable )
|
|
{
|
|
Msg( " reliable" );
|
|
}
|
|
Msg( "\n" );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
CON_COMMAND_F( net_dumpeventstats, "Dumps out a report of game event network usage", 0 )
|
|
{
|
|
s_GameEventManager.DumpEventNetworkStats();
|
|
}
|