Counter Strike : Global Offensive Source Code
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.

470 lines
14 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "rrbase.h"
  9. #include "utlmap.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include <tier0/memdbgon.h>
  12. using namespace ResponseRules;
  13. //-----------------------------------------------------------------------------
  14. // Case-insensitive criteria symbol table
  15. //-----------------------------------------------------------------------------
  16. CUtlSymbolTable CriteriaSet::sm_CriteriaSymbols( 1024, 1024, true );
  17. //-----------------------------------------------------------------------------
  18. // Purpose:
  19. // Input : *raw -
  20. // *key -
  21. // keylen -
  22. // *value -
  23. // valuelen -
  24. // *duration -
  25. // Output : static bool
  26. //-----------------------------------------------------------------------------
  27. const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration, const char *entireContext )
  28. {
  29. char *colon1 = Q_strstr( raw, ":" );
  30. if ( !colon1 )
  31. {
  32. DevMsg( "SplitContext: warning, ignoring context '%s', missing colon separator!\n", raw );
  33. *key = *value = 0;
  34. return NULL;
  35. }
  36. int len = colon1 - raw;
  37. Q_strncpy( key, raw, MIN( len + 1, keylen ) );
  38. key[ MIN( len, keylen - 1 ) ] = 0;
  39. bool last = false;
  40. char *end = Q_strstr( colon1 + 1, "," );
  41. if ( !end )
  42. {
  43. int remaining = Q_strlen( colon1 + 1 );
  44. end = colon1 + 1 + remaining;
  45. last = true;
  46. }
  47. char *colon2 = Q_strstr( colon1 + 1, ":" );
  48. if ( colon2 && ( colon2 < end ) )
  49. {
  50. if ( duration )
  51. *duration = atof( colon2 + 1 );
  52. char durationStartChar = *(colon2 + 1);
  53. if ( durationStartChar < '0' || durationStartChar > '9' )
  54. {
  55. DevMsg( "SplitContext: warning, ignoring context '%s', missing comma separator! Entire context was '%s'.\n", raw, entireContext );
  56. *key = *value = 0;
  57. return NULL;
  58. }
  59. len = MIN( colon2 - ( colon1 + 1 ), valuelen - 1 );
  60. Q_strncpy( value, colon1 + 1, len + 1 );
  61. value[ len ] = 0;
  62. }
  63. else
  64. {
  65. if ( duration )
  66. *duration = 0.0;
  67. len = MIN( end - ( colon1 + 1 ), valuelen - 1 );
  68. Q_strncpy( value, colon1 + 1, len + 1 );
  69. value[ len ] = 0;
  70. }
  71. return last ? NULL : end + 1;
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. //-----------------------------------------------------------------------------
  76. CriteriaSet::CriteriaSet() : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_bOverrideOnAppend(true),
  77. m_nNumPrefixedContexts(0)
  78. {
  79. }
  80. //-----------------------------------------------------------------------------
  81. // Purpose:
  82. //-----------------------------------------------------------------------------
  83. CriteriaSet::CriteriaSet( const CriteriaSet& src ) : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_nNumPrefixedContexts(src.m_nNumPrefixedContexts)
  84. {
  85. m_Lookup.EnsureCapacity( src.m_Lookup.Count() );
  86. for ( short i = src.m_Lookup.FirstInorder();
  87. i != src.m_Lookup.InvalidIndex();
  88. i = src.m_Lookup.NextInorder( i ) )
  89. {
  90. m_Lookup.Insert( src.m_Lookup[ i ] );
  91. }
  92. }
  93. CriteriaSet::CriteriaSet( const char *criteria, const char *value ) : m_Lookup( 0, 0, CritEntry_t::LessFunc ), m_bOverrideOnAppend(true)
  94. {
  95. AppendCriteria(criteria,value);
  96. }
  97. //-----------------------------------------------------------------------------
  98. // Computes a symbol for the criteria
  99. //-----------------------------------------------------------------------------
  100. CriteriaSet::CritSymbol_t CriteriaSet::ComputeCriteriaSymbol( const char *criteria )
  101. {
  102. return sm_CriteriaSymbols.AddString( criteria );
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Computes a symbol for the criteria
  106. //-----------------------------------------------------------------------------
  107. void CriteriaSet::AppendCriteria( CriteriaSet::CritSymbol_t criteria, const char *value, float weight )
  108. {
  109. int idx = FindCriterionIndex( criteria );
  110. if ( idx == -1 )
  111. {
  112. CritEntry_t entry;
  113. entry.criterianame = criteria;
  114. MEM_ALLOC_CREDIT();
  115. idx = m_Lookup.Insert( entry );
  116. if ( sm_CriteriaSymbols.String(criteria)[0] == kAPPLYTOWORLDPREFIX )
  117. {
  118. m_nNumPrefixedContexts += 1;
  119. }
  120. }
  121. else // criteria already existed
  122. {
  123. // bail out if override existing criteria is not allowed
  124. if ( !m_bOverrideOnAppend )
  125. return;
  126. }
  127. CritEntry_t *entry = &m_Lookup[ idx ];
  128. entry->SetValue( value );
  129. entry->weight = weight;
  130. }
  131. //-----------------------------------------------------------------------------
  132. // Purpose:
  133. // Input : *criteria -
  134. // "" -
  135. // 1.0f -
  136. //-----------------------------------------------------------------------------
  137. void CriteriaSet::AppendCriteria( const char *pCriteriaName, const char *value /*= ""*/, float weight /*= 1.0f*/ )
  138. {
  139. CUtlSymbol criteria = ComputeCriteriaSymbol( pCriteriaName );
  140. AppendCriteria( criteria, value, weight );
  141. }
  142. //-----------------------------------------------------------------------------
  143. // Purpose:
  144. // Input : *criteria -
  145. // "" -
  146. // 1.0f -
  147. //-----------------------------------------------------------------------------
  148. void CriteriaSet::AppendCriteria( const char *criteria, float value, float weight /*= 1.0f*/ )
  149. {
  150. char buf[32];
  151. V_snprintf( buf, 32, "%f", value );
  152. AppendCriteria( criteria, buf, weight );
  153. }
  154. //-----------------------------------------------------------------------------
  155. // Removes criteria in a set
  156. //-----------------------------------------------------------------------------
  157. void CriteriaSet::RemoveCriteria( const char *criteria )
  158. {
  159. const int idx = FindCriterionIndex( criteria );
  160. if ( idx == -1 )
  161. return;
  162. if ( criteria[0] == kAPPLYTOWORLDPREFIX )
  163. {
  164. Assert( m_nNumPrefixedContexts > 0 );
  165. m_nNumPrefixedContexts = isel( m_nNumPrefixedContexts - 1, m_nNumPrefixedContexts - 1, 0 );
  166. }
  167. RemoveCriteria( idx, false );
  168. }
  169. // bTestForIndex tells us whether the calling function has already checked for a
  170. // $ prefix and decremented m_nNumPrefixedContexts appropriately (false),
  171. // or if this function should do that (true).
  172. void CriteriaSet::RemoveCriteria( int idx, bool bTestForPrefix )
  173. {
  174. Assert( m_Lookup.IsValidIndex(idx) );
  175. if ( bTestForPrefix )
  176. {
  177. if ( sm_CriteriaSymbols.String( m_Lookup[idx].criterianame )[0] == kAPPLYTOWORLDPREFIX )
  178. {
  179. Assert( m_nNumPrefixedContexts > 0 );
  180. m_nNumPrefixedContexts = isel( m_nNumPrefixedContexts - 1, m_nNumPrefixedContexts - 1, 0 );
  181. }
  182. }
  183. m_Lookup.RemoveAt( idx );
  184. }
  185. //-----------------------------------------------------------------------------
  186. // Purpose:
  187. // Output : int
  188. //-----------------------------------------------------------------------------
  189. int CriteriaSet::GetCount() const
  190. {
  191. return m_Lookup.Count();
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Purpose:
  195. // Input : *name -
  196. // Output : int
  197. //-----------------------------------------------------------------------------
  198. int CriteriaSet::FindCriterionIndex( CritSymbol_t criteria ) const
  199. {
  200. CritEntry_t search;
  201. search.criterianame = criteria;
  202. int idx = m_Lookup.Find( search );
  203. return ( idx == m_Lookup.InvalidIndex() ) ? -1 : idx;
  204. }
  205. int CriteriaSet::FindCriterionIndex( const char *name ) const
  206. {
  207. CUtlSymbol criteria = ComputeCriteriaSymbol( name );
  208. return FindCriterionIndex( criteria );
  209. }
  210. //-----------------------------------------------------------------------------
  211. // Returns the name symbol
  212. //-----------------------------------------------------------------------------
  213. CriteriaSet::CritSymbol_t CriteriaSet::GetNameSymbol( int nIndex ) const
  214. {
  215. if ( nIndex < 0 || nIndex >= (int)m_Lookup.Count() )
  216. return UTL_INVAL_SYMBOL;
  217. const CritEntry_t *entry = &m_Lookup[ nIndex ];
  218. return entry->criterianame;
  219. }
  220. //-----------------------------------------------------------------------------
  221. // Purpose:
  222. // Input : index -
  223. // Output : char const
  224. //-----------------------------------------------------------------------------
  225. const char *CriteriaSet::GetName( int index ) const
  226. {
  227. if ( index < 0 || index >= (int)m_Lookup.Count() )
  228. return "";
  229. else
  230. {
  231. const char *pCriteriaName = sm_CriteriaSymbols.String( m_Lookup[ index ].criterianame );
  232. return pCriteriaName ? pCriteriaName : "";
  233. }
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose:
  237. // Input : index -
  238. // Output : char const
  239. //-----------------------------------------------------------------------------
  240. const char *CriteriaSet::GetValue( int index ) const
  241. {
  242. if ( index < 0 || index >= (int)m_Lookup.Count() )
  243. return "";
  244. const CritEntry_t *entry = &m_Lookup[ index ];
  245. return entry->value ? entry->value : "";
  246. }
  247. //-----------------------------------------------------------------------------
  248. // Purpose:
  249. // Input : index -
  250. // Output : float
  251. //-----------------------------------------------------------------------------
  252. float CriteriaSet::GetWeight( int index ) const
  253. {
  254. if ( index < 0 || index >= (int)m_Lookup.Count() )
  255. return 1.0f;
  256. const CritEntry_t *entry = &m_Lookup[ index ];
  257. return entry->weight;
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose: Merge another criteria set into this one.
  261. //-----------------------------------------------------------------------------
  262. void CriteriaSet::Merge( const CriteriaSet * RESTRICT otherCriteria )
  263. {
  264. Assert(otherCriteria);
  265. if (!otherCriteria)
  266. return;
  267. // for now, just duplicate everything.
  268. int count = otherCriteria->GetCount();
  269. EnsureCapacity( count + GetCount() );
  270. for ( int i = 0 ; i < count ; ++i )
  271. {
  272. AppendCriteria( otherCriteria->GetNameSymbol(i), otherCriteria->GetValue(i), otherCriteria->GetWeight(i) );
  273. }
  274. }
  275. void CriteriaSet::Merge( const char *modifiers ) // add criteria parsed from a text string
  276. {
  277. // Always include any optional modifiers
  278. if ( modifiers == NULL )
  279. return;
  280. char copy_modifiers[ 255 ];
  281. const char *pCopy;
  282. char key[ 128 ] = { 0 };
  283. char value[ 128 ] = { 0 };
  284. Q_strncpy( copy_modifiers, modifiers, sizeof( copy_modifiers ) );
  285. pCopy = copy_modifiers;
  286. while( pCopy )
  287. {
  288. pCopy = SplitContext( pCopy, key, sizeof( key ), value, sizeof( value ), NULL, modifiers );
  289. if( *key && *value )
  290. {
  291. AppendCriteria( key, value, 1 );
  292. }
  293. }
  294. }
  295. //-----------------------------------------------------------------------------
  296. // Purpose:
  297. //-----------------------------------------------------------------------------
  298. void CriteriaSet::Describe() const
  299. {
  300. // build an alphabetized representation of the set for printing
  301. typedef CUtlMap<const char *, const CritEntry_t *> tMap;
  302. tMap m_TempMap( 0, m_Lookup.Count(), CaselessStringLessThan );
  303. for ( short i = m_Lookup.FirstInorder(); i != m_Lookup.InvalidIndex(); i = m_Lookup.NextInorder( i ) )
  304. {
  305. const CritEntry_t *entry = &m_Lookup[ i ];
  306. m_TempMap.Insert( sm_CriteriaSymbols.String( entry->criterianame ), entry );
  307. }
  308. for ( tMap::IndexType_t i = m_TempMap.FirstInorder(); i != m_TempMap.InvalidIndex(); i = m_TempMap.NextInorder( i ) )
  309. {
  310. // const CritEntry_t *entry = &m_TempMap[ i ];
  311. // const char *pCriteriaName = sm_CriteriaSymbols.String( entry->criterianame );
  312. const char *name;
  313. name = m_TempMap.Key( i );
  314. const CritEntry_t *entry = m_TempMap.Element( i );
  315. if ( entry->weight != 1.0f )
  316. {
  317. DevMsg( " %20s = '%s' (weight %f)\n", name, entry->value ? entry->value : "", entry->weight );
  318. }
  319. else
  320. {
  321. DevMsg( " %20s = '%s'\n", name, entry->value ? entry->value : "" );
  322. }
  323. }
  324. /*
  325. for ( short i = m_Lookup.FirstInorder(); i != m_Lookup.InvalidIndex(); i = m_Lookup.NextInorder( i ) )
  326. {
  327. const CritEntry_t *entry = &m_Lookup[ i ];
  328. const char *pCriteriaName = sm_CriteriaSymbols.String( entry->criterianame );
  329. if ( entry->weight != 1.0f )
  330. {
  331. DevMsg( " %20s = '%s' (weight %f)\n", pCriteriaName, entry->value ? entry->value : "", entry->weight );
  332. }
  333. else
  334. {
  335. DevMsg( " %20s = '%s'\n", pCriteriaName, entry->value ? entry->value : "" );
  336. }
  337. }
  338. */
  339. }
  340. void CriteriaSet::Reset()
  341. {
  342. m_Lookup.Purge();
  343. }
  344. void CriteriaSet::WriteToEntity( CBaseEntity *pEntity )
  345. {
  346. #if 0
  347. if ( GetCount() < 1 )
  348. return;
  349. for ( int i = Head() ; IsValidIndex(i); i = Next(i) )
  350. {
  351. pEntity->AddContext( GetName(i), GetValue(i), 0 );
  352. }
  353. #else
  354. AssertMsg( false, "CriteriaSet::WriteToEntity has not been ported from l4d2.\n" );
  355. #endif
  356. }
  357. int CriteriaSet::InterceptWorldSetContexts( CriteriaSet * RESTRICT pFrom, CriteriaSet * RESTRICT pSetOnWorld )
  358. {
  359. // Assert( pFrom ); Assert( pTo ); Assert( pSetOnWorld );
  360. Assert( pSetOnWorld != pFrom );
  361. Assert( pSetOnWorld->GetCount() == 0 );
  362. if ( pFrom->m_nNumPrefixedContexts == 0 )
  363. {
  364. // nothing needs to be done to it.
  365. return 0;
  366. }
  367. #ifdef DEBUG
  368. // save this off for later error checking.
  369. const int nPrefixedContexts = pFrom->m_nNumPrefixedContexts;
  370. #endif
  371. // make enough space for the expected output quantity.
  372. pSetOnWorld->EnsureCapacity( pFrom->m_nNumPrefixedContexts );
  373. // initialize a buffer with the "world" prefix (so we can use strncpy instead of snprintf and be much faster)
  374. char buf[80] = { 'w', 'o', 'r', 'l', 'd', '\0' };
  375. const unsigned int PREFIXLEN = 5; // strlen("world")
  376. // create a second tree that has the appropriately renamed criteria,
  377. // then swap it into pFrom
  378. CriteriaSet rewrite;
  379. rewrite.EnsureCapacity( pFrom->GetCount() + 1 );
  380. for ( int i = pFrom->Head(); pFrom->IsValidIndex(i); i = pFrom->Next(i) )
  381. {
  382. const char *pszName = pFrom->GetName( i );
  383. if ( pszName[0] == CriteriaSet::kAPPLYTOWORLDPREFIX )
  384. { // redirect to the world contexts
  385. V_strncpy( buf+PREFIXLEN, pszName+1, sizeof(buf) - PREFIXLEN );
  386. rewrite.AppendCriteria( buf, pFrom->GetValue(i), pFrom->GetWeight(i) );
  387. pSetOnWorld->AppendCriteria( pszName+1, pFrom->GetValue(i), pFrom->GetWeight(i) );
  388. buf[PREFIXLEN] = 0;
  389. }
  390. else
  391. { // does not need to be fiddled; do not write back to world
  392. rewrite.AppendCriteria( pFrom->GetNameSymbol(i), pFrom->GetValue(i), pFrom->GetWeight(i) );
  393. }
  394. }
  395. AssertMsg2( pSetOnWorld->GetCount() == nPrefixedContexts, "Count of $ persistent RR contexts is inconsistent (%d vs %d)! Call Elan.",
  396. pSetOnWorld->GetCount(), nPrefixedContexts );
  397. pFrom->m_nNumPrefixedContexts = 0;
  398. pFrom->m_Lookup.Swap(rewrite.m_Lookup);
  399. return pSetOnWorld->GetCount();
  400. }