//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: motd: Handles a list of message of the day entries // //============================================================================= #include "cbase.h" #include "motd.h" #include "schemainitutils.h" #include "rtime.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" using namespace GCSDK; //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- CMOTDEntryDefinition::CMOTDEntryDefinition( void ) { m_pKVMOTD = NULL; m_PostTime = 0; m_ChangedTime = 0; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- bool CMOTDEntryDefinition::BInitFromKV( KeyValues *pKVMOTD, CUtlVector *pVecErrors ) { m_pKVMOTD = pKVMOTD->MakeCopy(); const char *pszTime = m_pKVMOTD->GetString( "post_time", NULL ); m_PostTime = (pszTime && pszTime[0]) ? CRTime::RTime32FromString(pszTime) : 0; pszTime = m_pKVMOTD->GetString( "last_changed_time", NULL ); m_ChangedTime = (pszTime && pszTime[0]) ? CRTime::RTime32FromString(pszTime) : 0; return SCHEMA_INIT_SUCCESS(); } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- const char *CMOTDEntryDefinition::GetTitle( ELanguage eLang ) { if ( m_pKVMOTD ) { // See if we have a localised block for the specified language. const char *pszLanguage = GetLanguageShortName( eLang ); if ( pszLanguage && pszLanguage[0] ) { const char *pszText = m_pKVMOTD->GetString( CFmtStr( "title_%s", pszLanguage ), NULL ); if ( pszText && pszText[0] ) return pszText; } // Fall back to english return m_pKVMOTD->GetString( "title_english", "No Title" ); } return "No Title"; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- const char *CMOTDEntryDefinition::GetText( ELanguage eLang ) { if ( m_pKVMOTD ) { // See if we have a localised block for the specified language. const char *pszLanguage = GetLanguageShortName( eLang ); if ( pszLanguage && pszLanguage[0] ) { const char *pszText = m_pKVMOTD->GetString( CFmtStr( "text_%s", pszLanguage ), NULL ); if ( pszText && pszText[0] ) return pszText; } // Fall back to english return m_pKVMOTD->GetString( "text_english", "No text" ); } return "No text"; } //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- const char *CMOTDEntryDefinition::GetHeaderTitle( ELanguage eLang ) { if ( m_pKVMOTD ) { // See if we have a localised block for the specified language. const char *pszLanguage = GetLanguageShortName( eLang ); if ( pszLanguage && pszLanguage[0] ) { const char *pszText = m_pKVMOTD->GetString( CFmtStr( "header_%s", pszLanguage ), NULL ); if ( pszText && pszText[0] ) return pszText; } // Fall back to english return m_pKVMOTD->GetString( "header_english", "News" ); } return "News"; } // Sort by ID int MOTDEntriesListLess( const CMOTDEntryDefinition *pLhs, const CMOTDEntryDefinition *pRhs ) { // This is stupid, sort by the KeyID instead return ( pLhs->GetNameInt() > pRhs->GetNameInt() ); } //----------------------------------------------------------------------------- // Purpose: Initializes the loot lists section of the schema //----------------------------------------------------------------------------- bool CMOTDManager::BInitMOTDEntries( KeyValues *pKVMOTDEntries, CUtlVector *pVecErrors ) { m_vecMOTDEntries.RemoveAll(); RTime32 iPrevTime = 0; if ( NULL != pKVMOTDEntries ) { FOR_EACH_TRUE_SUBKEY( pKVMOTDEntries, pKVEntry ) { const char *listName = pKVEntry->GetName(); SCHEMA_INIT_CHECK( listName != NULL, "All MOTD entries must have titles." ); int idx = m_vecMOTDEntries.AddToTail(); SCHEMA_INIT_SUBSTEP( m_vecMOTDEntries[idx].BInitFromKV( pKVEntry, pVecErrors ) ); // Make sure the dates all move forward SCHEMA_INIT_CHECK( m_vecMOTDEntries[idx].GetPostTime() > iPrevTime , "MOTD entry '%s' occurs prior to the previous entry.", m_vecMOTDEntries[idx].GetName() ); iPrevTime = m_vecMOTDEntries[idx].GetPostTime(); } } // Then sort all the MOTDs in order of their changed times, so we can easily send them m_vecMOTDEntries.Sort( MOTDEntriesListLess ); return SCHEMA_INIT_SUCCESS(); } //----------------------------------------------------------------------------- // Purpose: Returns the number of MOTD entries we've got after the specified time //----------------------------------------------------------------------------- int CMOTDManager::GetNumMOTDAfter( RTime32 iTime ) { FOR_EACH_VEC( m_vecMOTDEntries, i ) { if ( m_vecMOTDEntries[i].GetChangedTime() > iTime ) { // We've hit the first MOTD entry after this time. All following posts are assumed after. return (m_vecMOTDEntries.Count() - i); } } return 0; } //----------------------------------------------------------------------------- // Remove all unused MOTD: Save memory and whatever //----------------------------------------------------------------------------- void CMOTDManager::PurgeUnusedMOTDEntries( KeyValues *pKVMOTDEntries ) { // Find the latest entry name and remove all others int iLargest = -1; FOR_EACH_VEC_BACK( m_vecMOTDEntries, i ) { int iMOTDindex = m_vecMOTDEntries[i].GetNameInt(); if ( iMOTDindex > iLargest ) { iLargest = iMOTDindex; } } FOR_EACH_VEC_BACK( m_vecMOTDEntries, i ) { int iMOTDindex = m_vecMOTDEntries[i].GetNameInt(); if ( iMOTDindex < iLargest ) { if ( pKVMOTDEntries ) { KeyValues *pKey = pKVMOTDEntries->FindKey( m_vecMOTDEntries[i].GetName() ); if ( pKey ) { pKVMOTDEntries->RemoveSubKey( pKey ); } } m_vecMOTDEntries.Remove( i ); } } } //----------------------------------------------------------------------------- // Purpose: Returns the definition for the next blog post after the specified time //----------------------------------------------------------------------------- CMOTDEntryDefinition *CMOTDManager::GetNextMOTDAfter( RTime32 iTime ) { FOR_EACH_VEC( m_vecMOTDEntries, i ) { if ( m_vecMOTDEntries[i].GetChangedTime() > iTime ) return &m_vecMOTDEntries[i]; } return NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CMOTDEntryDefinition *CMOTDManager::GetMOTDByIndex( int iIndex ) { if ( iIndex < 0 || iIndex > m_vecMOTDEntries.Count() ) return NULL; return &m_vecMOTDEntries[iIndex]; } #ifdef GC_DLL //----------------------------------------------------------------------------- // Handle MOTD requests job. //----------------------------------------------------------------------------- class CGCMOTDRequest : public CGCGameBaseJob { public: CGCMOTDRequest( CGCGameBase *pGC ) : CGCGameBaseJob( pGC ) { } bool BYieldingRunJobFromMsg( GCSDK::IMsgNetPacket *pNetPacket ); }; //----------------------------------------------------------------------------- // Purpose: Responds to requests from the client for the current MOTD list //----------------------------------------------------------------------------- bool CGCMOTDRequest::BYieldingRunJobFromMsg( IMsgNetPacket *pNetPacket ) { CGCMsg< MsgGCMOTDRequest_t > msg( pNetPacket ); ELanguage eLang = (ELanguage)msg.Body().m_eLanguage; RTime32 iMOTDTime = msg.Body().m_nLastMOTDRequest; // Send the response to the client GCSDK::CGCMsg msg_response( k_EMsgGCMOTDRequestResponse ); int iEntries = 0; CMOTDEntryDefinition *pMOTD = m_pGCGameBase->GetMOTDManager().GetNextMOTDAfter( iMOTDTime ); while ( pMOTD ) { // Stuff this MOTD into the message. msg_response.AddStrData( pMOTD->GetName() ); msg_response.AddUintData( pMOTD->GetPostTime() ); msg_response.AddStrData( pMOTD->GetTitle( eLang ) ); msg_response.AddStrData( pMOTD->GetText( eLang ) ); msg_response.AddStrData( pMOTD->GetURL() ); msg_response.AddStrData( pMOTD->GetImage() ); msg_response.AddIntData( pMOTD->GetHeaderType() ); msg_response.AddStrData( pMOTD->GetHeaderTitle( eLang ) ); msg_response.AddStrData( pMOTD->GetHeaderIcon() ); iEntries++; // Move on to the next message. iMOTDTime = pMOTD->GetChangedTime(); pMOTD = m_pGCGameBase->GetMOTDManager().GetNextMOTDAfter( iMOTDTime ); } msg_response.Body().m_nEntries = iEntries; GGCEcon()->BSendGCMsgToClient( msg.Hdr().m_ulSteamID, msg_response ); return true; } GC_REG_JOB( CGCGameBase, CGCMOTDRequest, "CGCMOTDRequest", k_EMsgGCMOTDRequest, k_EServerTypeGC ); #endif // GC_DLL