//========= 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; ideleteThis(); // 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; ieventid = 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 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; jm_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(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(listener->m_pCallback); CGameEvent *pEvent = static_cast(event); pCallback->FireGameEvent( pEvent->m_pDataKeys ); } else { // new system IGameEventListener2 *pCallback = static_cast(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; jm_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(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( 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(); }