Team Fortress 2 Source Code as on 22/4/2020
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.

1650 lines
41 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "host.h"
  8. #include "sysexternal.h"
  9. #include "networkstringtable.h"
  10. #include "utlbuffer.h"
  11. #include "bitbuf.h"
  12. #include "netmessages.h"
  13. #include "net.h"
  14. #include "filesystem_engine.h"
  15. #include "baseclient.h"
  16. #include "vprof.h"
  17. #include <tier1/utlstring.h>
  18. #include <tier1/utlhashtable.h>
  19. #include <tier0/etwprof.h>
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. ConVar sv_dumpstringtables( "sv_dumpstringtables", "0", FCVAR_CHEAT );
  23. ConVar sv_compressstringtablebaselines_threshhold( "sv_compressstringtablebaselines_threshold", "2048", 0, "Minimum size (in bytes) for stringtablebaseline buffer to be compressed." );
  24. #define SUBSTRING_BITS 5
  25. struct StringHistoryEntry
  26. {
  27. char string[ (1<<SUBSTRING_BITS) ];
  28. };
  29. static int CountSimilarCharacters( char const *str1, char const *str2 )
  30. {
  31. int c = 0;
  32. while ( *str1 && *str2 &&
  33. *str1 == *str2 && c < ((1<<SUBSTRING_BITS) -1 ))
  34. {
  35. str1++;
  36. str2++;
  37. c++;
  38. }
  39. return c;
  40. }
  41. static int GetBestPreviousString( CUtlVector< StringHistoryEntry >& history, char const *newstring, int& substringsize )
  42. {
  43. int bestindex = -1;
  44. int bestcount = 0;
  45. int c = history.Count();
  46. for ( int i = 0; i < c; i++ )
  47. {
  48. char const *prev = history[ i ].string;
  49. int similar = CountSimilarCharacters( prev, newstring );
  50. if ( similar < 3 )
  51. continue;
  52. if ( similar > bestcount )
  53. {
  54. bestcount = similar;
  55. bestindex = i;
  56. }
  57. }
  58. substringsize = bestcount;
  59. return bestindex;
  60. }
  61. bool CNetworkStringTable_LessFunc( FileNameHandle_t const &a, FileNameHandle_t const &b )
  62. {
  63. return a < b;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Implementation when dictionary strings are filenames
  67. //-----------------------------------------------------------------------------
  68. class CNetworkStringFilenameDict : public INetworkStringDict
  69. {
  70. public:
  71. CNetworkStringFilenameDict()
  72. {
  73. m_Items.SetLessFunc( CNetworkStringTable_LessFunc );
  74. }
  75. virtual ~CNetworkStringFilenameDict()
  76. {
  77. Purge();
  78. }
  79. unsigned int Count()
  80. {
  81. return m_Items.Count();
  82. }
  83. void Purge()
  84. {
  85. m_Items.Purge();
  86. }
  87. const char *String( int index )
  88. {
  89. char* pString = tmpstr512();
  90. g_pFileSystem->String( m_Items.Key( index ), pString, 512 );
  91. return pString;
  92. }
  93. bool IsValidIndex( int index )
  94. {
  95. return m_Items.IsValidIndex( index );
  96. }
  97. int Insert( const char *pString )
  98. {
  99. FileNameHandle_t fnHandle = g_pFileSystem->FindOrAddFileName( pString );
  100. return m_Items.Insert( fnHandle );
  101. }
  102. int Find( const char *pString )
  103. {
  104. FileNameHandle_t fnHandle = g_pFileSystem->FindFileName( pString );
  105. if ( !fnHandle )
  106. return m_Items.InvalidIndex();
  107. return m_Items.Find( fnHandle );
  108. }
  109. CNetworkStringTableItem &Element( int index )
  110. {
  111. return m_Items.Element( index );
  112. }
  113. const CNetworkStringTableItem &Element( int index ) const
  114. {
  115. return m_Items.Element( index );
  116. }
  117. private:
  118. CUtlMap< FileNameHandle_t, CNetworkStringTableItem > m_Items;
  119. };
  120. //-----------------------------------------------------------------------------
  121. // Implementation for general purpose strings
  122. //-----------------------------------------------------------------------------
  123. class CNetworkStringDict : public INetworkStringDict
  124. {
  125. public:
  126. CNetworkStringDict()
  127. {
  128. }
  129. virtual ~CNetworkStringDict()
  130. {
  131. }
  132. unsigned int Count()
  133. {
  134. return m_Lookup.Count();
  135. }
  136. void Purge()
  137. {
  138. m_Lookup.Purge();
  139. }
  140. const char *String( int index )
  141. {
  142. return m_Lookup.Key( index ).Get();
  143. }
  144. bool IsValidIndex( int index )
  145. {
  146. return m_Lookup.IsValidHandle( index );
  147. }
  148. int Insert( const char *pString )
  149. {
  150. return m_Lookup.Insert( pString );
  151. }
  152. int Find( const char *pString )
  153. {
  154. return pString ? m_Lookup.Find( pString ) : m_Lookup.InvalidHandle();
  155. }
  156. CNetworkStringTableItem &Element( int index )
  157. {
  158. return m_Lookup.Element( index );
  159. }
  160. const CNetworkStringTableItem &Element( int index ) const
  161. {
  162. return m_Lookup.Element( index );
  163. }
  164. private:
  165. CUtlStableHashtable< CUtlConstString, CNetworkStringTableItem, CaselessStringHashFunctor, UTLConstStringCaselessStringEqualFunctor<char> > m_Lookup;
  166. };
  167. //-----------------------------------------------------------------------------
  168. // Purpose:
  169. // Input : id -
  170. // *tableName -
  171. // maxentries -
  172. //-----------------------------------------------------------------------------
  173. CNetworkStringTable::CNetworkStringTable( TABLEID id, const char *tableName, int maxentries, int userdatafixedsize, int userdatanetworkbits, bool bIsFilenames ) :
  174. m_bAllowClientSideAddString( false ),
  175. m_pItemsClientSide( NULL )
  176. {
  177. m_id = id;
  178. int len = strlen( tableName ) + 1;
  179. m_pszTableName = new char[ len ];
  180. Assert( m_pszTableName );
  181. Assert( tableName );
  182. Q_strncpy( m_pszTableName, tableName, len );
  183. m_changeFunc = NULL;
  184. m_pObject = NULL;
  185. m_nTickCount = 0;
  186. m_pMirrorTable = NULL;
  187. m_nLastChangedTick = 0;
  188. m_bChangeHistoryEnabled = false;
  189. m_bLocked = false;
  190. m_nMaxEntries = maxentries;
  191. m_nEntryBits = Q_log2( m_nMaxEntries );
  192. m_bUserDataFixedSize = userdatafixedsize != 0;
  193. m_nUserDataSize = userdatafixedsize;
  194. m_nUserDataSizeBits = userdatanetworkbits;
  195. if ( m_nUserDataSizeBits > CNetworkStringTableItem::MAX_USERDATA_BITS )
  196. {
  197. Host_Error( "String tables user data bits restricted to %i bits, requested %i is too large\n",
  198. CNetworkStringTableItem::MAX_USERDATA_BITS,
  199. m_nUserDataSizeBits );
  200. }
  201. if ( m_nUserDataSize > CNetworkStringTableItem::MAX_USERDATA_SIZE )
  202. {
  203. Host_Error( "String tables user data size restricted to %i bytes, requested %i is too large\n",
  204. CNetworkStringTableItem::MAX_USERDATA_SIZE,
  205. m_nUserDataSize );
  206. }
  207. // Make sure maxentries is power of 2
  208. if ( ( 1 << m_nEntryBits ) != maxentries )
  209. {
  210. Host_Error( "String tables must be powers of two in size!, %i is not a power of 2\n", maxentries );
  211. }
  212. if ( IsXbox() || bIsFilenames )
  213. {
  214. m_bIsFilenames = true;
  215. m_pItems = new CNetworkStringFilenameDict;
  216. }
  217. else
  218. {
  219. m_bIsFilenames = false;
  220. m_pItems = new CNetworkStringDict;
  221. }
  222. }
  223. void CNetworkStringTable::SetAllowClientSideAddString( bool state )
  224. {
  225. if ( state == m_bAllowClientSideAddString )
  226. return;
  227. m_bAllowClientSideAddString = state;
  228. if ( m_pItemsClientSide )
  229. {
  230. delete m_pItemsClientSide;
  231. m_pItemsClientSide = NULL;
  232. }
  233. if ( m_bAllowClientSideAddString )
  234. {
  235. m_pItemsClientSide = new CNetworkStringDict;
  236. m_pItemsClientSide->Insert( "___clientsideitemsplaceholder0___" ); // 0 slot can't be used
  237. m_pItemsClientSide->Insert( "___clientsideitemsplaceholder1___" ); // -1 can't be used since it looks like the "invalid" index from other string lookups
  238. }
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose:
  242. // Output : Returns true on success, false on failure.
  243. //-----------------------------------------------------------------------------
  244. bool CNetworkStringTable::IsUserDataFixedSize() const
  245. {
  246. return m_bUserDataFixedSize;
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose:
  250. // Output : Returns true on success, false on failure.
  251. //-----------------------------------------------------------------------------
  252. bool CNetworkStringTable::HasFileNameStrings() const
  253. {
  254. return m_bIsFilenames;
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose:
  258. //-----------------------------------------------------------------------------
  259. int CNetworkStringTable::GetUserDataSize() const
  260. {
  261. return m_nUserDataSize;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Purpose:
  265. //-----------------------------------------------------------------------------
  266. int CNetworkStringTable::GetUserDataSizeBits() const
  267. {
  268. return m_nUserDataSizeBits;
  269. }
  270. //-----------------------------------------------------------------------------
  271. // Purpose:
  272. //-----------------------------------------------------------------------------
  273. CNetworkStringTable::~CNetworkStringTable( void )
  274. {
  275. delete[] m_pszTableName;
  276. delete m_pItems;
  277. delete m_pItemsClientSide;
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose:
  281. //-----------------------------------------------------------------------------
  282. void CNetworkStringTable::DeleteAllStrings( void )
  283. {
  284. delete m_pItems;
  285. if ( m_bIsFilenames )
  286. {
  287. m_pItems = new CNetworkStringFilenameDict;
  288. }
  289. else
  290. {
  291. m_pItems = new CNetworkStringDict;
  292. }
  293. if ( m_pItemsClientSide )
  294. {
  295. delete m_pItemsClientSide;
  296. m_pItemsClientSide = new CNetworkStringDict;
  297. m_pItemsClientSide->Insert( "___clientsideitemsplaceholder0___" ); // 0 slot can't be used
  298. m_pItemsClientSide->Insert( "___clientsideitemsplaceholder1___" ); // -1 can't be used since it looks like the "invalid" index from other string lookups
  299. }
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Purpose:
  303. // Input : i -
  304. // Output : CNetworkStringTableItem
  305. //-----------------------------------------------------------------------------
  306. CNetworkStringTableItem *CNetworkStringTable::GetItem( int i )
  307. {
  308. if ( i >= 0 )
  309. {
  310. return &m_pItems->Element( i );
  311. }
  312. Assert( m_pItemsClientSide );
  313. return &m_pItemsClientSide->Element( -i );
  314. }
  315. //-----------------------------------------------------------------------------
  316. // Purpose: Returns the table identifier
  317. // Output : TABLEID
  318. //-----------------------------------------------------------------------------
  319. TABLEID CNetworkStringTable::GetTableId( void ) const
  320. {
  321. return m_id;
  322. }
  323. //-----------------------------------------------------------------------------
  324. // Purpose: Returns the max size of the table
  325. // Output : int
  326. //-----------------------------------------------------------------------------
  327. int CNetworkStringTable::GetMaxStrings( void ) const
  328. {
  329. return m_nMaxEntries;
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose: Returns a table, by name
  333. // Output : const char
  334. //-----------------------------------------------------------------------------
  335. const char *CNetworkStringTable::GetTableName( void ) const
  336. {
  337. return m_pszTableName;
  338. }
  339. //-----------------------------------------------------------------------------
  340. // Purpose: Returns the number of bits needed to encode an entry index
  341. // Output : int
  342. //-----------------------------------------------------------------------------
  343. int CNetworkStringTable::GetEntryBits( void ) const
  344. {
  345. return m_nEntryBits;
  346. }
  347. void CNetworkStringTable::SetTick(int tick_count)
  348. {
  349. Assert( tick_count >= m_nTickCount );
  350. m_nTickCount = tick_count;
  351. }
  352. void CNetworkStringTable::Lock( bool bLock )
  353. {
  354. m_bLocked = bLock;
  355. }
  356. pfnStringChanged CNetworkStringTable::GetCallback()
  357. {
  358. return m_changeFunc;
  359. }
  360. #ifndef SHARED_NET_STRING_TABLES
  361. //-----------------------------------------------------------------------------
  362. // Purpose:
  363. //-----------------------------------------------------------------------------
  364. void CNetworkStringTable::EnableRollback()
  365. {
  366. // stringtable must be empty
  367. Assert( m_pItems->Count() == 0);
  368. m_bChangeHistoryEnabled = true;
  369. }
  370. void CNetworkStringTable::SetMirrorTable(INetworkStringTable *table)
  371. {
  372. m_pMirrorTable = table;
  373. }
  374. void CNetworkStringTable::RestoreTick(int tick)
  375. {
  376. // TODO optimize this, most of the time the tables doens't really change
  377. m_nLastChangedTick = 0;
  378. int count = m_pItems->Count();
  379. for ( int i = 0; i < count; i++ )
  380. {
  381. // restore tick in all entries
  382. int tickChanged = m_pItems->Element( i ).RestoreTick( tick );
  383. if ( tickChanged > m_nLastChangedTick )
  384. m_nLastChangedTick = tickChanged;
  385. }
  386. }
  387. //-----------------------------------------------------------------------------
  388. // Purpose: updates the mirror table, if set one
  389. // Output : return true if some entries were updates
  390. //-----------------------------------------------------------------------------
  391. void CNetworkStringTable::UpdateMirrorTable( int tick_ack )
  392. {
  393. if ( !m_pMirrorTable )
  394. return;
  395. m_pMirrorTable->SetTick( m_nTickCount ); // use same tick
  396. int count = m_pItems->Count();
  397. for ( int i = 0; i < count; i++ )
  398. {
  399. CNetworkStringTableItem *p = &m_pItems->Element( i );
  400. // mirror is up to date
  401. if ( p->GetTickChanged() <= tick_ack )
  402. continue;
  403. const void *pUserData = p->GetUserData();
  404. int nBytes = p->GetUserDataLength();
  405. if ( !nBytes || !pUserData )
  406. {
  407. nBytes = 0;
  408. pUserData = NULL;
  409. }
  410. // Check if we are updating an old entry or adding a new one
  411. if ( i < m_pMirrorTable->GetNumStrings() )
  412. {
  413. m_pMirrorTable->SetStringUserData( i, nBytes, pUserData );
  414. }
  415. else
  416. {
  417. // Grow the table (entryindex must be the next empty slot)
  418. Assert( i == m_pMirrorTable->GetNumStrings() );
  419. char const *pName = m_pItems->String( i );
  420. m_pMirrorTable->AddString( true, pName, nBytes, pUserData );
  421. }
  422. }
  423. }
  424. int CNetworkStringTable::WriteUpdate( CBaseClient *client, bf_write &buf, int tick_ack )
  425. {
  426. CUtlVector< StringHistoryEntry > history;
  427. int entriesUpdated = 0;
  428. int lastEntry = -1;
  429. int nTableStartBit = buf.GetNumBitsWritten();
  430. int count = m_pItems->Count();
  431. for ( int i = 0; i < count; i++ )
  432. {
  433. CNetworkStringTableItem *p = &m_pItems->Element( i );
  434. // Client is up to date
  435. if ( p->GetTickChanged() <= tick_ack )
  436. continue;
  437. int nStartBit = buf.GetNumBitsWritten();
  438. // Write Entry index
  439. if ( (lastEntry+1) == i )
  440. {
  441. buf.WriteOneBit( 1 );
  442. }
  443. else
  444. {
  445. buf.WriteOneBit( 0 );
  446. buf.WriteUBitLong( i, m_nEntryBits );
  447. }
  448. // check if string can use older string as base eg "models/weapons/gun1" & "models/weapons/gun2"
  449. char const *pEntry = m_pItems->String( i );
  450. if ( p->GetTickCreated() > tick_ack )
  451. {
  452. // this item has just been created, send string itself
  453. buf.WriteOneBit( 1 );
  454. int substringsize = 0;
  455. int bestprevious = GetBestPreviousString( history, pEntry, substringsize );
  456. if ( bestprevious != -1 )
  457. {
  458. buf.WriteOneBit( 1 );
  459. buf.WriteUBitLong( bestprevious, 5 ); // history never has more than 32 entries
  460. buf.WriteUBitLong( substringsize, SUBSTRING_BITS );
  461. buf.WriteString( pEntry + substringsize );
  462. }
  463. else
  464. {
  465. buf.WriteOneBit( 0 );
  466. buf.WriteString( pEntry );
  467. }
  468. }
  469. else
  470. {
  471. buf.WriteOneBit( 0 );
  472. }
  473. // Write the item's user data.
  474. int len;
  475. const void *pUserData = GetStringUserData( i, &len );
  476. if ( pUserData && len > 0 )
  477. {
  478. buf.WriteOneBit( 1 );
  479. if ( IsUserDataFixedSize() )
  480. {
  481. // Don't have to send length, it was sent as part of the table definition
  482. buf.WriteBits( pUserData, GetUserDataSizeBits() );
  483. }
  484. else
  485. {
  486. buf.WriteUBitLong( len, CNetworkStringTableItem::MAX_USERDATA_BITS );
  487. buf.WriteBits( pUserData, len*8 );
  488. }
  489. }
  490. else
  491. {
  492. buf.WriteOneBit( 0 );
  493. }
  494. // limit string history to 32 entries
  495. if ( history.Count() > 31 )
  496. {
  497. history.Remove( 0 );
  498. }
  499. // add string to string history
  500. StringHistoryEntry she;
  501. Q_strncpy( she.string, pEntry, sizeof( she.string ) );
  502. history.AddToTail( she );
  503. entriesUpdated++;
  504. lastEntry = i;
  505. if ( client && client->IsTracing() )
  506. {
  507. int nBits = buf.GetNumBitsWritten() - nStartBit;
  508. client->TraceNetworkMsg( nBits, " [%s] %d:%s ", GetTableName(), i, GetString( i ) );
  509. }
  510. }
  511. ETWMark2I( GetTableName(), entriesUpdated, buf.GetNumBitsWritten() - nTableStartBit );
  512. return entriesUpdated;
  513. }
  514. //-----------------------------------------------------------------------------
  515. // Purpose: Parse string update
  516. //-----------------------------------------------------------------------------
  517. void CNetworkStringTable::ParseUpdate( bf_read &buf, int entries )
  518. {
  519. int lastEntry = -1;
  520. CUtlVector< StringHistoryEntry > history;
  521. for (int i=0; i<entries; i++)
  522. {
  523. int entryIndex = lastEntry + 1;
  524. if ( !buf.ReadOneBit() )
  525. {
  526. entryIndex = buf.ReadUBitLong( GetEntryBits() );
  527. }
  528. lastEntry = entryIndex;
  529. if ( entryIndex < 0 || entryIndex >= GetMaxStrings() )
  530. {
  531. Host_Error( "Server sent bogus string index %i for table %s\n", entryIndex, GetTableName() );
  532. }
  533. const char *pEntry = NULL;
  534. char entry[ 1024 ];
  535. char substr[ 1024 ];
  536. if ( buf.ReadOneBit() )
  537. {
  538. bool substringcheck = buf.ReadOneBit() ? true : false;
  539. if ( substringcheck )
  540. {
  541. unsigned int index = buf.ReadUBitLong( 5 );
  542. unsigned int bytestocopy = buf.ReadUBitLong( SUBSTRING_BITS );
  543. if ( index >= (unsigned int)history.Count() )
  544. {
  545. Host_Error( "Server sent bogus substring index %i for table %s\n",
  546. entryIndex, GetTableName() );
  547. }
  548. Q_strncpy( entry, history[ index ].string, Min( sizeof( entry ), (size_t)bytestocopy + 1 ) );
  549. buf.ReadString( substr, sizeof(substr) );
  550. Q_strncat( entry, substr, sizeof(entry), COPY_ALL_CHARACTERS );
  551. }
  552. else
  553. {
  554. buf.ReadString( entry, sizeof( entry ) );
  555. }
  556. pEntry = entry;
  557. }
  558. // Read in the user data.
  559. unsigned char tempbuf[ CNetworkStringTableItem::MAX_USERDATA_SIZE ];
  560. memset( tempbuf, 0, sizeof( tempbuf ) );
  561. const void *pUserData = NULL;
  562. int nBytes = 0;
  563. if ( buf.ReadOneBit() )
  564. {
  565. if ( IsUserDataFixedSize() )
  566. {
  567. // Don't need to read length, it's fixed length and the length was networked down already.
  568. nBytes = GetUserDataSize();
  569. Assert( nBytes > 0 );
  570. tempbuf[nBytes-1] = 0; // be safe, clear last byte
  571. buf.ReadBits( tempbuf, GetUserDataSizeBits() );
  572. }
  573. else
  574. {
  575. nBytes = buf.ReadUBitLong( CNetworkStringTableItem::MAX_USERDATA_BITS );
  576. ErrorIfNot( nBytes <= sizeof( tempbuf ),
  577. ("CNetworkStringTableClient::ParseUpdate: message too large (%d bytes).", nBytes)
  578. );
  579. buf.ReadBytes( tempbuf, nBytes );
  580. }
  581. pUserData = tempbuf;
  582. }
  583. // Check if we are updating an old entry or adding a new one
  584. if ( entryIndex < GetNumStrings() )
  585. {
  586. SetStringUserData( entryIndex, nBytes, pUserData );
  587. #ifdef _DEBUG
  588. if ( pEntry )
  589. {
  590. Assert( !Q_strcmp( pEntry, GetString( entryIndex ) ) ); // make sure string didn't change
  591. }
  592. #endif
  593. pEntry = GetString( entryIndex ); // string didn't change
  594. }
  595. else
  596. {
  597. // Grow the table (entryindex must be the next empty slot)
  598. Assert( (entryIndex == GetNumStrings()) && (pEntry != NULL) );
  599. if ( pEntry == NULL )
  600. {
  601. Msg("CNetworkStringTable::ParseUpdate: NULL pEntry, table %s, index %i\n", GetTableName(), entryIndex );
  602. pEntry = "";// avoid crash because of NULL strings
  603. }
  604. AddString( true, pEntry, nBytes, pUserData );
  605. }
  606. if ( history.Count() > 31 )
  607. {
  608. history.Remove( 0 );
  609. }
  610. StringHistoryEntry she;
  611. Q_strncpy( she.string, pEntry, sizeof( she.string ) );
  612. history.AddToTail( she );
  613. }
  614. }
  615. void CNetworkStringTable::CopyStringTable(CNetworkStringTable * table)
  616. {
  617. Assert (m_pItems->Count() == 0); // table must be empty before coping
  618. for ( unsigned int i = 0; i < table->m_pItems->Count() ; ++i )
  619. {
  620. CNetworkStringTableItem *item = &table->m_pItems->Element( i );
  621. m_nTickCount = item->m_nTickChanged;
  622. AddString( true, table->GetString( i ), item->m_nUserDataLength, item->m_pUserData );
  623. }
  624. }
  625. #endif
  626. void CNetworkStringTable::TriggerCallbacks( int tick_ack )
  627. {
  628. if ( m_changeFunc == NULL )
  629. return;
  630. COM_TimestampedLog( "Change(%s):Start", GetTableName() );
  631. int count = m_pItems->Count();
  632. for ( int i = 0; i < count; i++ )
  633. {
  634. CNetworkStringTableItem *pItem = &m_pItems->Element( i );
  635. // mirror is up to date
  636. if ( pItem->GetTickChanged() <= tick_ack )
  637. continue;
  638. int userDataSize;
  639. const void *pUserData = pItem->GetUserData( &userDataSize );
  640. // fire the callback function
  641. ( *m_changeFunc )( m_pObject, this, i, GetString( i ), pUserData );
  642. }
  643. COM_TimestampedLog( "Change(%s):End", GetTableName() );
  644. }
  645. //-----------------------------------------------------------------------------
  646. // Purpose:
  647. // Input : changeFunc -
  648. //-----------------------------------------------------------------------------
  649. void CNetworkStringTable::SetStringChangedCallback( void *object, pfnStringChanged changeFunc )
  650. {
  651. m_changeFunc = changeFunc;
  652. m_pObject = object;
  653. }
  654. //-----------------------------------------------------------------------------
  655. // Purpose:
  656. // Input : *client -
  657. // Output : Returns true on success, false on failure.
  658. //-----------------------------------------------------------------------------
  659. bool CNetworkStringTable::ChangedSinceTick( int tick ) const
  660. {
  661. return ( m_nLastChangedTick > tick );
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose:
  665. // Input : *value -
  666. // Output : int
  667. //-----------------------------------------------------------------------------
  668. int CNetworkStringTable::AddString( bool bIsServer, const char *string, int length /*= -1*/, const void *userdata /*= NULL*/ )
  669. {
  670. bool bHasChanged;
  671. CNetworkStringTableItem *item;
  672. if ( !string )
  673. {
  674. Assert( string );
  675. ConMsg( "Warning: Can't add NULL string to table %s\n", GetTableName() );
  676. return INVALID_STRING_INDEX;
  677. }
  678. #ifdef _DEBUG
  679. if ( m_bLocked )
  680. {
  681. DevMsg("Warning! CNetworkStringTable::AddString: adding '%s' while locked.\n", string );
  682. }
  683. #endif
  684. int i = m_pItems->Find( string );
  685. if ( !bIsServer )
  686. {
  687. if ( m_pItems->IsValidIndex( i ) && !m_pItemsClientSide )
  688. {
  689. bIsServer = true;
  690. }
  691. }
  692. if ( !bIsServer && m_pItemsClientSide )
  693. {
  694. i = m_pItemsClientSide->Find( string );
  695. if ( !m_pItemsClientSide->IsValidIndex( i ) )
  696. {
  697. // not in list yet, create it now
  698. if ( m_pItemsClientSide->Count() >= (unsigned int)GetMaxStrings() )
  699. {
  700. // Too many strings, FIXME: Print warning message
  701. ConMsg( "Warning: Table %s is full, can't add %s\n", GetTableName(), string );
  702. return INVALID_STRING_INDEX;
  703. }
  704. // create new item
  705. {
  706. MEM_ALLOC_CREDIT();
  707. i = m_pItemsClientSide->Insert( string );
  708. }
  709. item = &m_pItemsClientSide->Element( i );
  710. // set changed ticks
  711. item->m_nTickChanged = m_nTickCount;
  712. #ifndef SHARED_NET_STRING_TABLES
  713. item->m_nTickCreated = m_nTickCount;
  714. if ( m_bChangeHistoryEnabled )
  715. {
  716. item->EnableChangeHistory();
  717. }
  718. #endif
  719. bHasChanged = true;
  720. }
  721. else
  722. {
  723. item = &m_pItemsClientSide->Element( i ); // item already exists
  724. bHasChanged = false; // not changed yet
  725. }
  726. if ( length > -1 )
  727. {
  728. if ( item->SetUserData( m_nTickCount, length, userdata ) )
  729. {
  730. bHasChanged = true;
  731. }
  732. }
  733. if ( bHasChanged && !m_bChangeHistoryEnabled )
  734. {
  735. DataChanged( -i, item );
  736. }
  737. // Negate i for returning to client
  738. i = -i;
  739. }
  740. else
  741. {
  742. // See if it's already there
  743. i = m_pItems->Find( string );
  744. if ( !m_pItems->IsValidIndex( i ) )
  745. {
  746. // not in list yet, create it now
  747. if ( m_pItems->Count() >= (unsigned int)GetMaxStrings() )
  748. {
  749. // Too many strings, FIXME: Print warning message
  750. ConMsg( "Warning: Table %s is full, can't add %s\n", GetTableName(), string );
  751. return INVALID_STRING_INDEX;
  752. }
  753. // create new item
  754. {
  755. MEM_ALLOC_CREDIT();
  756. i = m_pItems->Insert( string );
  757. }
  758. item = &m_pItems->Element( i );
  759. // set changed ticks
  760. item->m_nTickChanged = m_nTickCount;
  761. #ifndef SHARED_NET_STRING_TABLES
  762. item->m_nTickCreated = m_nTickCount;
  763. if ( m_bChangeHistoryEnabled )
  764. {
  765. item->EnableChangeHistory();
  766. }
  767. #endif
  768. bHasChanged = true;
  769. }
  770. else
  771. {
  772. item = &m_pItems->Element( i ); // item already exists
  773. bHasChanged = false; // not changed yet
  774. }
  775. if ( length > -1 )
  776. {
  777. if ( item->SetUserData( m_nTickCount, length, userdata ) )
  778. {
  779. bHasChanged = true;
  780. }
  781. }
  782. if ( bHasChanged && !m_bChangeHistoryEnabled )
  783. {
  784. DataChanged( i, item );
  785. }
  786. }
  787. return i;
  788. }
  789. //-----------------------------------------------------------------------------
  790. // Purpose:
  791. // Input : stringNumber -
  792. // Output : const char
  793. //-----------------------------------------------------------------------------
  794. const char *CNetworkStringTable::GetString( int stringNumber )
  795. {
  796. INetworkStringDict *dict = m_pItems;
  797. if ( m_pItemsClientSide && stringNumber < -1 )
  798. {
  799. dict = m_pItemsClientSide;
  800. stringNumber = -stringNumber;
  801. }
  802. Assert( dict->IsValidIndex( stringNumber ) );
  803. if ( dict->IsValidIndex( stringNumber ) )
  804. {
  805. return dict->String( stringNumber );
  806. }
  807. return NULL;
  808. }
  809. //-----------------------------------------------------------------------------
  810. // Purpose:
  811. // Input : stringNumber -
  812. // length -
  813. // *userdata -
  814. //-----------------------------------------------------------------------------
  815. void CNetworkStringTable::SetStringUserData( int stringNumber, int length /*=0*/, const void *userdata /*= 0*/ )
  816. {
  817. #ifdef _DEBUG
  818. if ( m_bLocked )
  819. {
  820. DevMsg("Warning! CNetworkStringTable::SetStringUserData (%s): changing entry %i while locked.\n", GetTableName(), stringNumber );
  821. }
  822. #endif
  823. INetworkStringDict *dict = m_pItems;
  824. int saveStringNumber = stringNumber;
  825. if ( m_pItemsClientSide && stringNumber < -1 )
  826. {
  827. dict = m_pItemsClientSide;
  828. stringNumber = -stringNumber;
  829. }
  830. Assert( (length == 0 && userdata == NULL) || ( length > 0 && userdata != NULL) );
  831. Assert( dict->IsValidIndex( stringNumber ) );
  832. CNetworkStringTableItem *p = &dict->Element( stringNumber );
  833. Assert( p );
  834. if ( p->SetUserData( m_nTickCount, length, userdata ) )
  835. {
  836. // Mark changed
  837. DataChanged( saveStringNumber, p );
  838. }
  839. }
  840. //-----------------------------------------------------------------------------
  841. // Purpose:
  842. // Input : *item -
  843. //-----------------------------------------------------------------------------
  844. void CNetworkStringTable::DataChanged( int stringNumber, CNetworkStringTableItem *item )
  845. {
  846. Assert( item );
  847. if ( !item )
  848. return;
  849. // Mark table as changed
  850. m_nLastChangedTick = m_nTickCount;
  851. // Invoke callback if one was installed
  852. #ifndef SHARED_NET_STRING_TABLES // but not if client & server share the same containers, we trigger that later
  853. if ( m_changeFunc != NULL )
  854. {
  855. int userDataSize;
  856. const void *pUserData = item->GetUserData( &userDataSize );
  857. ( *m_changeFunc )( m_pObject, this, stringNumber, GetString( stringNumber ), pUserData );
  858. }
  859. #endif
  860. }
  861. #ifndef SHARED_NET_STRING_TABLES
  862. void CNetworkStringTable::WriteStringTable( bf_write& buf )
  863. {
  864. int numstrings = m_pItems->Count();
  865. buf.WriteWord( numstrings );
  866. for ( int i = 0 ; i < numstrings; i++ )
  867. {
  868. buf.WriteString( GetString( i ) );
  869. int userDataSize;
  870. const void *pUserData = GetStringUserData( i, &userDataSize );
  871. if ( userDataSize > 0 )
  872. {
  873. buf.WriteOneBit( 1 );
  874. buf.WriteWord( (short)userDataSize );
  875. buf.WriteBytes( pUserData, userDataSize );
  876. }
  877. else
  878. {
  879. buf.WriteOneBit( 0 );
  880. }
  881. }
  882. if ( m_pItemsClientSide )
  883. {
  884. buf.WriteOneBit( 1 );
  885. numstrings = m_pItemsClientSide->Count();
  886. buf.WriteWord( numstrings );
  887. for ( int i = 0 ; i < numstrings; i++ )
  888. {
  889. buf.WriteString( m_pItemsClientSide->String( i ) );
  890. int userDataSize;
  891. const void *pUserData = m_pItemsClientSide->Element( i ).GetUserData( &userDataSize );
  892. if ( userDataSize > 0 )
  893. {
  894. buf.WriteOneBit( 1 );
  895. buf.WriteWord( (short)userDataSize );
  896. buf.WriteBytes( pUserData, userDataSize );
  897. }
  898. else
  899. {
  900. buf.WriteOneBit( 0 );
  901. }
  902. }
  903. }
  904. else
  905. {
  906. buf.WriteOneBit( 0 );
  907. }
  908. }
  909. bool CNetworkStringTable::ReadStringTable( bf_read& buf )
  910. {
  911. DeleteAllStrings();
  912. int numstrings = buf.ReadWord();
  913. for ( int i = 0 ; i < numstrings; i++ )
  914. {
  915. char stringname[4096];
  916. buf.ReadString( stringname, sizeof( stringname ) );
  917. if ( buf.ReadOneBit() == 1 )
  918. {
  919. int userDataSize = (int)buf.ReadWord();
  920. Assert( userDataSize > 0 );
  921. byte *data = new byte[ userDataSize + 4 ];
  922. Assert( data );
  923. buf.ReadBytes( data, userDataSize );
  924. AddString( true, stringname, userDataSize, data );
  925. delete[] data;
  926. }
  927. else
  928. {
  929. AddString( true, stringname );
  930. }
  931. }
  932. // Client side stuff
  933. if ( buf.ReadOneBit() == 1 )
  934. {
  935. numstrings = buf.ReadWord();
  936. for ( int i = 0 ; i < numstrings; i++ )
  937. {
  938. char stringname[4096];
  939. buf.ReadString( stringname, sizeof( stringname ) );
  940. if ( buf.ReadOneBit() == 1 )
  941. {
  942. int userDataSize = (int)buf.ReadWord();
  943. Assert( userDataSize > 0 );
  944. byte *data = new byte[ userDataSize + 4 ];
  945. Assert( data );
  946. buf.ReadBytes( data, userDataSize );
  947. if ( i >= 2 )
  948. {
  949. AddString( false, stringname, userDataSize, data );
  950. }
  951. delete[] data;
  952. }
  953. else
  954. {
  955. if ( i >= 2 )
  956. {
  957. AddString( false, stringname );
  958. }
  959. }
  960. }
  961. }
  962. return true;
  963. }
  964. #endif
  965. //-----------------------------------------------------------------------------
  966. // Purpose:
  967. // Input : stringNumber -
  968. // length -
  969. // Output : const void
  970. //-----------------------------------------------------------------------------
  971. const void *CNetworkStringTable::GetStringUserData( int stringNumber, int *length )
  972. {
  973. INetworkStringDict *dict = m_pItems;
  974. if ( m_pItemsClientSide && stringNumber < -1 )
  975. {
  976. dict = m_pItemsClientSide;
  977. stringNumber = -stringNumber;
  978. }
  979. CNetworkStringTableItem *p;
  980. Assert( dict->IsValidIndex( stringNumber ) );
  981. p = &dict->Element( stringNumber );
  982. Assert( p );
  983. return p->GetUserData( length );
  984. }
  985. //-----------------------------------------------------------------------------
  986. // Purpose:
  987. // Output : int
  988. //-----------------------------------------------------------------------------
  989. int CNetworkStringTable::GetNumStrings( void ) const
  990. {
  991. return m_pItems->Count();
  992. }
  993. //-----------------------------------------------------------------------------
  994. // Purpose:
  995. // Input : stringTable -
  996. // *string -
  997. // Output : int
  998. //-----------------------------------------------------------------------------
  999. int CNetworkStringTable::FindStringIndex( char const *string )
  1000. {
  1001. if ( !string )
  1002. return INVALID_STRING_INDEX;
  1003. int i = m_pItems->Find( string );
  1004. if ( m_pItems->IsValidIndex( i ) )
  1005. return i;
  1006. return INVALID_STRING_INDEX;
  1007. }
  1008. //-----------------------------------------------------------------------------
  1009. // Purpose:
  1010. //-----------------------------------------------------------------------------
  1011. void CNetworkStringTable::Dump( void )
  1012. {
  1013. ConMsg( "Table %s\n", GetTableName() );
  1014. ConMsg( " %i/%i items\n", GetNumStrings(), GetMaxStrings() );
  1015. for ( int i = 0; i < GetNumStrings() ; i++ )
  1016. {
  1017. ConMsg( " %i : %s\n", i, GetString( i ) );
  1018. }
  1019. if ( m_pItemsClientSide )
  1020. {
  1021. for ( int i = 0; i < (int)m_pItemsClientSide->Count() ; i++ )
  1022. {
  1023. ConMsg( " (c)%i : %s\n", i, m_pItemsClientSide->String( i ) );
  1024. }
  1025. }
  1026. ConMsg( "\n" );
  1027. }
  1028. #ifndef SHARED_NET_STRING_TABLES
  1029. bool CNetworkStringTable::WriteBaselines( SVC_CreateStringTable &msg, char *msg_buffer, int msg_buffer_size )
  1030. {
  1031. VPROF_BUDGET( "CNetworkStringTable::WriteBaselines", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  1032. msg.m_DataOut.StartWriting( msg_buffer, msg_buffer_size );
  1033. msg.m_bIsFilenames = m_bIsFilenames;
  1034. msg.m_szTableName = GetTableName();
  1035. msg.m_nMaxEntries = GetMaxStrings();
  1036. msg.m_nNumEntries = GetNumStrings();
  1037. msg.m_bUserDataFixedSize = IsUserDataFixedSize();
  1038. msg.m_nUserDataSize = GetUserDataSize();
  1039. msg.m_nUserDataSizeBits = GetUserDataSizeBits();
  1040. // tick = -1 ensures that all entries are updated = baseline
  1041. int entries = WriteUpdate( NULL, msg.m_DataOut, -1 );
  1042. return entries == msg.m_nNumEntries;
  1043. }
  1044. #endif
  1045. //-----------------------------------------------------------------------------
  1046. // Purpose:
  1047. //-----------------------------------------------------------------------------
  1048. CNetworkStringTableContainer::CNetworkStringTableContainer( void )
  1049. {
  1050. m_bAllowCreation = false;
  1051. m_bLocked = true;
  1052. m_nTickCount = 0;
  1053. m_bEnableRollback = false;
  1054. }
  1055. //-----------------------------------------------------------------------------
  1056. // Purpose:
  1057. //-----------------------------------------------------------------------------
  1058. CNetworkStringTableContainer::~CNetworkStringTableContainer( void )
  1059. {
  1060. RemoveAllTables();
  1061. }
  1062. //-----------------------------------------------------------------------------
  1063. // Purpose:
  1064. //-----------------------------------------------------------------------------
  1065. void CNetworkStringTableContainer::AllowCreation( bool state )
  1066. {
  1067. m_bAllowCreation = state;
  1068. }
  1069. bool CNetworkStringTableContainer::Lock( bool bLock )
  1070. {
  1071. bool oldLock = m_bLocked;
  1072. m_bLocked = bLock;
  1073. // Determine if an update is needed
  1074. for ( int i = 0; i < m_Tables.Count(); i++ )
  1075. {
  1076. CNetworkStringTable *table = (CNetworkStringTable*) GetTable( i );
  1077. table->Lock( bLock );
  1078. }
  1079. return oldLock;
  1080. }
  1081. void CNetworkStringTableContainer::SetAllowClientSideAddString( INetworkStringTable *table, bool bAllowClientSideAddString )
  1082. {
  1083. for ( int i = 0; i < m_Tables.Count(); i++ )
  1084. {
  1085. CNetworkStringTable *t = (CNetworkStringTable*) GetTable( i );
  1086. if ( t == table )
  1087. {
  1088. t->SetAllowClientSideAddString( bAllowClientSideAddString );
  1089. return;
  1090. }
  1091. }
  1092. }
  1093. //-----------------------------------------------------------------------------
  1094. // Purpose:
  1095. // Input : *tableName -
  1096. // maxentries -
  1097. // Output : TABLEID
  1098. //-----------------------------------------------------------------------------
  1099. INetworkStringTable *CNetworkStringTableContainer::CreateStringTableEx( const char *tableName, int maxentries, int userdatafixedsize /*= 0*/, int userdatanetworkbits /*= 0*/, bool bIsFilenames /*= false */ )
  1100. {
  1101. if ( !m_bAllowCreation )
  1102. {
  1103. Sys_Error( "Tried to create string table '%s' at wrong time\n", tableName );
  1104. return NULL;
  1105. }
  1106. CNetworkStringTable *pTable = (CNetworkStringTable*) FindTable( tableName );
  1107. if ( pTable != NULL )
  1108. {
  1109. Sys_Error( "Tried to create string table '%s' twice\n", tableName );
  1110. return NULL;
  1111. }
  1112. if ( m_Tables.Count() >= MAX_TABLES )
  1113. {
  1114. Sys_Error( "Only %i string tables allowed, can't create'%s'", MAX_TABLES, tableName);
  1115. return NULL;
  1116. }
  1117. TABLEID id = m_Tables.Count();
  1118. pTable = new CNetworkStringTable( id, tableName, maxentries, userdatafixedsize, userdatanetworkbits, bIsFilenames );
  1119. Assert( pTable );
  1120. #ifndef SHARED_NET_STRING_TABLES
  1121. if ( m_bEnableRollback )
  1122. {
  1123. pTable->EnableRollback();
  1124. }
  1125. #endif
  1126. pTable->SetTick( m_nTickCount );
  1127. m_Tables.AddToTail( pTable );
  1128. return pTable;
  1129. }
  1130. //-----------------------------------------------------------------------------
  1131. // Purpose:
  1132. // Input : *tableName -
  1133. //-----------------------------------------------------------------------------
  1134. INetworkStringTable *CNetworkStringTableContainer::FindTable( const char *tableName ) const
  1135. {
  1136. for ( int i = 0; i < m_Tables.Count(); i++ )
  1137. {
  1138. if ( !Q_stricmp( tableName, m_Tables[ i ]->GetTableName() ) )
  1139. return m_Tables[i];
  1140. }
  1141. return NULL;
  1142. }
  1143. //-----------------------------------------------------------------------------
  1144. // Purpose:
  1145. // Input : stringTable -
  1146. // Output : CNetworkStringTableServer
  1147. //-----------------------------------------------------------------------------
  1148. INetworkStringTable *CNetworkStringTableContainer::GetTable( TABLEID stringTable ) const
  1149. {
  1150. if ( stringTable < 0 || stringTable >= m_Tables.Count() )
  1151. return NULL;
  1152. return m_Tables[ stringTable ];
  1153. }
  1154. int CNetworkStringTableContainer::GetNumTables( void ) const
  1155. {
  1156. return m_Tables.Count();
  1157. }
  1158. #ifndef SHARED_NET_STRING_TABLES
  1159. //-----------------------------------------------------------------------------
  1160. // Purpose:
  1161. //-----------------------------------------------------------------------------
  1162. void CNetworkStringTableContainer::WriteBaselines( bf_write &buf )
  1163. {
  1164. VPROF_BUDGET( "CNetworkStringTableContainer::WriteBaselines", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  1165. SVC_CreateStringTable msg;
  1166. size_t msg_buffer_size = 2 * NET_MAX_PAYLOAD;
  1167. char *msg_buffer = new char[ msg_buffer_size ];
  1168. if ( !msg_buffer )
  1169. {
  1170. Host_Error( "Failed to allocate %llu bytes of memory in CNetworkStringTableContainer::WriteBaselines\n", (uint64)msg_buffer_size );
  1171. }
  1172. for ( int i = 0 ; i < m_Tables.Count() ; i++ )
  1173. {
  1174. CNetworkStringTable *table = (CNetworkStringTable*) GetTable( i );
  1175. int before = buf.GetNumBytesWritten();
  1176. if ( !table->WriteBaselines( msg, msg_buffer, msg_buffer_size ) )
  1177. {
  1178. Host_Error( "Index error writing string table baseline %s\n", table->GetTableName() );
  1179. }
  1180. if ( msg.m_DataOut.IsOverflowed() )
  1181. {
  1182. Warning( "Warning: Overflowed writing uncompressed string table data for %s\n", table->GetTableName() );
  1183. }
  1184. msg.m_bDataCompressed = false;
  1185. if ( msg.m_DataOut.GetNumBytesWritten() >= sv_compressstringtablebaselines_threshhold.GetInt() )
  1186. {
  1187. CFastTimer compressTimer;
  1188. compressTimer.Start();
  1189. // TERROR: bzip-compress the stringtable before adding it to the packet. Yes, the whole packet will be bzip'd,
  1190. // but the uncompressed data also has to be under the NET_MAX_PAYLOAD limit.
  1191. unsigned int numBytes = msg.m_DataOut.GetNumBytesWritten();
  1192. unsigned int compressedSize = (unsigned int)numBytes;
  1193. char *compressedData = new char[numBytes];
  1194. if ( COM_BufferToBufferCompress_Snappy( compressedData, &compressedSize, (char *)msg.m_DataOut.GetData(), numBytes ) )
  1195. {
  1196. msg.m_bDataCompressed = true;
  1197. msg.m_DataOut.Reset();
  1198. msg.m_DataOut.WriteLong( numBytes ); // uncompressed size
  1199. msg.m_DataOut.WriteLong( compressedSize ); // compressed size
  1200. msg.m_DataOut.WriteBits( compressedData, compressedSize * 8 ); // compressed data
  1201. // if ( compressstringtablbaselines > 1 )
  1202. {
  1203. compressTimer.End();
  1204. DevMsg( "Stringtable %s compression: %d -> %d bytes: %.2fms\n",
  1205. table->GetTableName(), numBytes, compressedSize, compressTimer.GetDuration().GetMillisecondsF() );
  1206. }
  1207. }
  1208. delete [] compressedData;
  1209. }
  1210. if ( !msg.WriteToBuffer( buf ) )
  1211. {
  1212. Host_Error( "Overflow error writing string table baseline %s\n", table->GetTableName() );
  1213. }
  1214. int after = buf.GetNumBytesWritten();
  1215. if ( sv_dumpstringtables.GetBool() )
  1216. {
  1217. DevMsg( "CNetworkStringTableContainer::WriteBaselines wrote %d bytes for table %s [space remaining %d bytes]\n", after - before, table->GetTableName(), buf.GetNumBytesLeft() );
  1218. }
  1219. }
  1220. delete[] msg_buffer;
  1221. }
  1222. void CNetworkStringTableContainer::WriteStringTables( bf_write& buf )
  1223. {
  1224. int numTables = m_Tables.Size();
  1225. buf.WriteByte( numTables );
  1226. for ( int i = 0; i < numTables; i++ )
  1227. {
  1228. CNetworkStringTable *table = m_Tables[ i ];
  1229. buf.WriteString( table->GetTableName() );
  1230. table->WriteStringTable( buf );
  1231. }
  1232. }
  1233. bool CNetworkStringTableContainer::ReadStringTables( bf_read& buf )
  1234. {
  1235. int numTables = buf.ReadByte();
  1236. for ( int i = 0 ; i < numTables; i++ )
  1237. {
  1238. char tablename[ 256 ];
  1239. buf.ReadString( tablename, sizeof( tablename ) );
  1240. // Find this table by name
  1241. CNetworkStringTable *table = (CNetworkStringTable*)FindTable( tablename );
  1242. Assert( table );
  1243. // Now read the data for the table
  1244. if ( table && !table->ReadStringTable( buf ) )
  1245. {
  1246. Host_Error( "Error reading string table %s\n", tablename );
  1247. }
  1248. else
  1249. {
  1250. Warning( "Could not find table \"%s\"\n", tablename );
  1251. }
  1252. }
  1253. return true;
  1254. }
  1255. //-----------------------------------------------------------------------------
  1256. // Purpose:
  1257. // Input : *cl -
  1258. // *msg -
  1259. //-----------------------------------------------------------------------------
  1260. void CNetworkStringTableContainer::WriteUpdateMessage( CBaseClient *client, int tick_ack, bf_write &buf )
  1261. {
  1262. VPROF_BUDGET( "CNetworkStringTableContainer::WriteUpdateMessage", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  1263. char buffer[NET_MAX_PAYLOAD];
  1264. // Determine if an update is needed
  1265. for ( int i = 0; i < m_Tables.Count(); i++ )
  1266. {
  1267. CNetworkStringTable *table = (CNetworkStringTable*) GetTable( i );
  1268. if ( !table )
  1269. continue;
  1270. if ( !table->ChangedSinceTick( tick_ack ) )
  1271. continue;
  1272. SVC_UpdateStringTable msg;
  1273. msg.m_DataOut.StartWriting( buffer, NET_MAX_PAYLOAD );
  1274. msg.m_nTableID = table->GetTableId();
  1275. msg.m_nChangedEntries = table->WriteUpdate( client, msg.m_DataOut, tick_ack );
  1276. Assert( msg.m_nChangedEntries > 0 ); // don't send unnecessary empty updates
  1277. msg.WriteToBuffer( buf );
  1278. if ( client &&
  1279. client->IsTracing() )
  1280. {
  1281. client->TraceNetworkData( buf, "StringTable %s", table->GetTableName() );
  1282. }
  1283. }
  1284. }
  1285. //-----------------------------------------------------------------------------
  1286. // Purpose:
  1287. // Input : *cl -
  1288. // *msg -
  1289. //-----------------------------------------------------------------------------
  1290. void CNetworkStringTableContainer::DirectUpdate( int tick_ack )
  1291. {
  1292. VPROF_BUDGET( "CNetworkStringTableContainer::DirectUpdate", VPROF_BUDGETGROUP_OTHER_NETWORKING );
  1293. // Determine if an update is needed
  1294. for ( int i = 0; i < m_Tables.Count(); i++ )
  1295. {
  1296. CNetworkStringTable *table = (CNetworkStringTable*) GetTable( i );
  1297. Assert( table );
  1298. if ( !table->ChangedSinceTick( tick_ack ) )
  1299. continue;
  1300. table->UpdateMirrorTable( tick_ack );
  1301. }
  1302. }
  1303. void CNetworkStringTableContainer::EnableRollback( bool bState )
  1304. {
  1305. // we can't dis/enable rollback if we already created tabled
  1306. Assert( m_Tables.Count() == 0 );
  1307. m_bEnableRollback = bState;
  1308. }
  1309. void CNetworkStringTableContainer::RestoreTick( int tick )
  1310. {
  1311. for ( int i = 0; i < m_Tables.Count(); i++ )
  1312. {
  1313. CNetworkStringTable *table = (CNetworkStringTable*) GetTable( i );
  1314. Assert( table );
  1315. table->RestoreTick( tick );
  1316. }
  1317. }
  1318. #endif
  1319. void CNetworkStringTableContainer::TriggerCallbacks( int tick_ack )
  1320. {
  1321. // Determine if an update is needed
  1322. for ( int i = 0; i < m_Tables.Count(); i++ )
  1323. {
  1324. CNetworkStringTable *table = (CNetworkStringTable*) GetTable( i );
  1325. Assert( table );
  1326. if ( !table->ChangedSinceTick( tick_ack ) )
  1327. continue;
  1328. table->TriggerCallbacks( tick_ack );
  1329. }
  1330. }
  1331. void CNetworkStringTableContainer::SetTick( int tick_count)
  1332. {
  1333. Assert( tick_count > 0 );
  1334. m_nTickCount = tick_count;
  1335. // Determine if an update is needed
  1336. for ( int i = 0; i < m_Tables.Count(); i++ )
  1337. {
  1338. CNetworkStringTable *table = (CNetworkStringTable*) GetTable( i );
  1339. Assert( table );
  1340. table->SetTick( tick_count );
  1341. }
  1342. }
  1343. //-----------------------------------------------------------------------------
  1344. // Purpose:
  1345. //-----------------------------------------------------------------------------
  1346. void CNetworkStringTableContainer::RemoveAllTables( void )
  1347. {
  1348. while ( m_Tables.Count() > 0 )
  1349. {
  1350. CNetworkStringTable *table = m_Tables[ 0 ];
  1351. m_Tables.Remove( 0 );
  1352. delete table;
  1353. }
  1354. }
  1355. //-----------------------------------------------------------------------------
  1356. // Purpose:
  1357. //-----------------------------------------------------------------------------
  1358. void CNetworkStringTableContainer::Dump( void )
  1359. {
  1360. for ( int i = 0; i < m_Tables.Count(); i++ )
  1361. {
  1362. m_Tables[ i ]->Dump();
  1363. }
  1364. }