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.

539 lines
18 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Template entities are used by spawners to create copies of entities
  4. // that were configured by the level designer. This allows us to spawn
  5. // entities with arbitrary sets of key/value data and entity I/O
  6. // connections.
  7. //
  8. // Template entities are marked with a special spawnflag which causes
  9. // them not to spawn, but to be saved as a string containing all the
  10. // map data (keyvalues and I/O connections) from the BSP. Template
  11. // entities are looked up by name by the spawner, which copies the
  12. // map data into a local string (that's how the template data is saved
  13. // and restored). Once all the entities in the map have been activated,
  14. // the template database is freed.
  15. //
  16. //=============================================================================//
  17. #include "cbase.h"
  18. #include "igamesystem.h"
  19. #include "mapentities_shared.h"
  20. #include "point_template.h"
  21. #include "eventqueue.h"
  22. #include "TemplateEntities.h"
  23. #include "utldict.h"
  24. #include "entitydefs.h"
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. ConVar template_debug( "template_debug", "0" );
  28. // This is appended to key's values that will need to be unique in template instances
  29. const char *ENTITYIO_FIXUP_STRING = "&0000";
  30. int MapEntity_GetNumKeysInEntity( const char *pEntData );
  31. struct TemplateEntityData_t
  32. {
  33. const char *pszName;
  34. char *pszMapData;
  35. string_t iszMapData;
  36. int iMapDataLength;
  37. bool bNeedsEntityIOFixup; // If true, this template has entity I/O in its mapdata that needs fixup before spawning.
  38. char *pszFixedMapData; // A single copy of this template that we used to fix up the Entity I/O whenever someone wants a fixed version of this template
  39. int m_nHammerID; // Used to update the template in Foundry
  40. DECLARE_SIMPLE_DATADESC();
  41. };
  42. BEGIN_SIMPLE_DATADESC( TemplateEntityData_t )
  43. //DEFINE_FIELD( pszName, FIELD_STRING ), // Saved custom, see below
  44. //DEFINE_FIELD( pszMapData, FIELD_STRING ), // Saved custom, see below
  45. DEFINE_FIELD( iszMapData, FIELD_STRING ),
  46. DEFINE_FIELD( iMapDataLength, FIELD_INTEGER ),
  47. DEFINE_FIELD( bNeedsEntityIOFixup, FIELD_BOOLEAN ),
  48. //DEFINE_FIELD( pszFixedMapData, FIELD_STRING ), // Not saved at all
  49. END_DATADESC()
  50. struct grouptemplate_t
  51. {
  52. CEntityMapData *pMapDataParser;
  53. char pszName[MAPKEY_MAXLENGTH];
  54. int iIndex;
  55. bool bChangeTargetname;
  56. };
  57. static CUtlVector<TemplateEntityData_t *> g_Templates;
  58. int g_iCurrentTemplateInstance;
  59. void Templates_FreeTemplate( TemplateEntityData_t *pTemplate )
  60. {
  61. free((void *)pTemplate->pszName);
  62. free(pTemplate->pszMapData);
  63. if ( pTemplate->pszFixedMapData )
  64. {
  65. free(pTemplate->pszFixedMapData);
  66. }
  67. free(pTemplate);
  68. }
  69. //-----------------------------------------------------------------------------
  70. // Purpose: Saves the given entity's keyvalue data for later use by a spawner.
  71. // Returns the index into the templates.
  72. //-----------------------------------------------------------------------------
  73. int Templates_Add(CBaseEntity *pEntity, const char *pszMapData, int nLen, int nHammerID)
  74. {
  75. const char *pszName = STRING(pEntity->GetEntityName());
  76. if ((!pszName) || (!strlen(pszName)))
  77. {
  78. DevWarning(1, "RegisterTemplateEntity: template entity with no name, class %s\n", pEntity->GetClassname());
  79. return -1;
  80. }
  81. TemplateEntityData_t *pEntData = (TemplateEntityData_t *)malloc(sizeof(TemplateEntityData_t));
  82. pEntData->m_nHammerID = nHammerID;
  83. pEntData->pszName = strdup( pszName );
  84. // We may modify the values of the keys in this mapdata chunk later on to fix Entity I/O
  85. // connections. For this reason, we need to ensure we have enough memory to do that.
  86. int iKeys = MapEntity_GetNumKeysInEntity( pszMapData );
  87. int iExtraSpace = (strlen(ENTITYIO_FIXUP_STRING)+1) * iKeys;
  88. // Extra 1 because the mapdata passed in isn't null terminated
  89. pEntData->iMapDataLength = nLen + iExtraSpace + 1;
  90. pEntData->pszMapData = (char *)malloc( pEntData->iMapDataLength );
  91. memcpy(pEntData->pszMapData, pszMapData, nLen + 1);
  92. pEntData->pszMapData[nLen] = '\0';
  93. // We don't alloc these suckers right now because that gives us no time to
  94. // tweak them for Entity I/O purposes.
  95. pEntData->iszMapData = NULL_STRING;
  96. pEntData->bNeedsEntityIOFixup = false;
  97. pEntData->pszFixedMapData = NULL;
  98. return g_Templates.AddToTail(pEntData);
  99. }
  100. void Templates_RemoveByHammerID( int nHammerID )
  101. {
  102. for ( int i=g_Templates.Count()-1; i >= 0; i-- )
  103. {
  104. if ( g_Templates[i]->m_nHammerID == nHammerID )
  105. {
  106. Templates_FreeTemplate( g_Templates[i] );
  107. g_Templates.Remove( i );
  108. }
  109. }
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Purpose: Returns true if the specified index needs to be fixed up to be unique
  113. // when the template is spawned.
  114. //-----------------------------------------------------------------------------
  115. bool Templates_IndexRequiresEntityIOFixup( int iIndex )
  116. {
  117. Assert( iIndex < g_Templates.Count() );
  118. return g_Templates[iIndex]->bNeedsEntityIOFixup;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose: Looks up a template entity by its index in the templates
  122. // Used by point_templates because they often have multiple templates with the same name
  123. //-----------------------------------------------------------------------------
  124. string_t Templates_FindByIndex( int iIndex )
  125. {
  126. Assert( iIndex < g_Templates.Count() );
  127. // First time through we alloc the mapdata onto the pool.
  128. // It's safe to do it now because this isn't called until post Entity I/O cleanup.
  129. if ( g_Templates[iIndex]->iszMapData == NULL_STRING )
  130. {
  131. g_Templates[iIndex]->iszMapData = AllocPooledString( g_Templates[iIndex]->pszMapData );
  132. }
  133. return g_Templates[iIndex]->iszMapData;
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose:
  137. //-----------------------------------------------------------------------------
  138. int Templates_GetStringSize( int iIndex )
  139. {
  140. Assert( iIndex < g_Templates.Count() );
  141. return g_Templates[iIndex]->iMapDataLength;
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose: Looks up a template entity by name, returning the map data blob as
  145. // a null-terminated string containing key/value pairs.
  146. // NOTE: This can't handle multiple templates with the same targetname.
  147. //-----------------------------------------------------------------------------
  148. string_t Templates_FindByTargetName(const char *pszName)
  149. {
  150. int nCount = g_Templates.Count();
  151. for (int i = 0; i < nCount; i++)
  152. {
  153. TemplateEntityData_t *pTemplate = g_Templates.Element(i);
  154. if ( !stricmp(pTemplate->pszName, pszName) )
  155. return Templates_FindByIndex( i );
  156. }
  157. return NULL_STRING;
  158. }
  159. //-----------------------------------------------------------------------------
  160. // Purpose: A CPointTemplate has asked us to reconnect all the entity I/O links
  161. // inside it's templates. Go through the keys and add look for values
  162. // that match a name within the group's entity names. Append %d to any
  163. // found values, which will later be filled out by a unique identifier
  164. // whenever the template is instanced.
  165. //-----------------------------------------------------------------------------
  166. void Templates_ReconnectIOForGroup( CPointTemplate *pGroup )
  167. {
  168. int iCount = pGroup->GetNumTemplates();
  169. if ( !iCount )
  170. return;
  171. // First assemble a list of the targetnames of all the templates in the group.
  172. // We need to store off the original names here, because we're going to change
  173. // them as we go along.
  174. CUtlVector< grouptemplate_t > GroupTemplates;
  175. int i;
  176. for ( i = 0; i < iCount; i++ )
  177. {
  178. grouptemplate_t newGroupTemplate;
  179. newGroupTemplate.iIndex = pGroup->GetTemplateIndexForTemplate(i);
  180. newGroupTemplate.pMapDataParser = new CEntityMapData( g_Templates[ newGroupTemplate.iIndex ]->pszMapData, g_Templates[ newGroupTemplate.iIndex ]->iMapDataLength );
  181. Assert( newGroupTemplate.pMapDataParser );
  182. newGroupTemplate.pMapDataParser->ExtractValue( "targetname", newGroupTemplate.pszName );
  183. newGroupTemplate.bChangeTargetname = false;
  184. GroupTemplates.AddToTail( newGroupTemplate );
  185. }
  186. if (pGroup->AllowNameFixup())
  187. {
  188. char keyName[MAPKEY_MAXLENGTH];
  189. char value[MAPKEY_MAXLENGTH];
  190. char valueclipped[MAPKEY_MAXLENGTH];
  191. // Now go through all the entities in the group and parse their mapdata keyvalues.
  192. // We're looking for any values that match targetnames of any of the group entities.
  193. for ( i = 0; i < iCount; i++ )
  194. {
  195. // We need to know what instance of each key we're changing.
  196. // Store a table of the count of the keys we've run into.
  197. CUtlDict< int, int > KeyInstanceCount;
  198. CEntityMapData *mapData = GroupTemplates[i].pMapDataParser;
  199. // Loop through our keys
  200. if ( !mapData->GetFirstKey(keyName, value) )
  201. continue;
  202. do
  203. {
  204. // Ignore targetnames
  205. if ( !stricmp( keyName, "targetname" ) )
  206. continue;
  207. // Add to the count for this
  208. int idx = KeyInstanceCount.Find( keyName );
  209. if ( idx == KeyInstanceCount.InvalidIndex() )
  210. {
  211. idx = KeyInstanceCount.Insert( keyName, 0 );
  212. }
  213. KeyInstanceCount[idx]++;
  214. // Entity I/O values are stored as "Targetname,<data>", so we need to see if there's a ',' in the string
  215. char *sValue = value;
  216. // FIXME: This is very brittle. Any key with a , will not be found.
  217. char delimiter = VMF_IOPARAM_STRING_DELIMITER;
  218. if( strchr( value, delimiter ) == NULL )
  219. {
  220. delimiter = ',';
  221. }
  222. char *s = strchr( value, delimiter );
  223. if ( s )
  224. {
  225. // Grab just the targetname of the receiver
  226. Q_strncpy( valueclipped, value, (s - value+1) );
  227. sValue = valueclipped;
  228. }
  229. // Loop through our group templates
  230. for ( int iTName = 0; iTName < iCount; iTName++ )
  231. {
  232. char *pName = GroupTemplates[iTName].pszName;
  233. if ( stricmp( pName, sValue ) )
  234. continue;
  235. if ( template_debug.GetInt() )
  236. {
  237. Msg("Template Connection Found: Key %s (\"%s\") in entity named \"%s\"(%d) matches entity %d's targetname\n", keyName, sValue, GroupTemplates[i].pszName, i, iTName );
  238. }
  239. char newvalue[MAPKEY_MAXLENGTH];
  240. // Get the current key instance. (-1 because it's this one we're changing)
  241. int nKeyInstance = KeyInstanceCount[idx] - 1;
  242. // Add our IO value to the targetname
  243. // We need to append it if this isn't an Entity I/O value, or prepend it to the ',' if it is
  244. if ( s )
  245. {
  246. Q_strncpy( newvalue, valueclipped, MAPKEY_MAXLENGTH );
  247. Q_strncat( newvalue, ENTITYIO_FIXUP_STRING, sizeof(newvalue), COPY_ALL_CHARACTERS );
  248. Q_strncat( newvalue, s, sizeof(newvalue), COPY_ALL_CHARACTERS );
  249. mapData->SetValue( keyName, newvalue, nKeyInstance );
  250. }
  251. else
  252. {
  253. Q_strncpy( newvalue, sValue, MAPKEY_MAXLENGTH );
  254. Q_strncat( newvalue, ENTITYIO_FIXUP_STRING, sizeof(newvalue), COPY_ALL_CHARACTERS );
  255. mapData->SetValue( keyName, newvalue, nKeyInstance );
  256. }
  257. // Remember we changed this targetname
  258. GroupTemplates[iTName].bChangeTargetname = true;
  259. // Set both entity's flags telling them their template needs fixup when it's spawned
  260. g_Templates[ GroupTemplates[i].iIndex ]->bNeedsEntityIOFixup = true;
  261. g_Templates[ GroupTemplates[iTName].iIndex ]->bNeedsEntityIOFixup = true;
  262. }
  263. }
  264. while ( mapData->GetNextKey(keyName, value) );
  265. }
  266. // Now change targetnames for all entities that need them changed
  267. for ( i = 0; i < iCount; i++ )
  268. {
  269. char value[MAPKEY_MAXLENGTH];
  270. if ( GroupTemplates[i].bChangeTargetname )
  271. {
  272. CEntityMapData *mapData = GroupTemplates[i].pMapDataParser;
  273. mapData->ExtractValue( "targetname", value );
  274. Q_strncat( value, ENTITYIO_FIXUP_STRING, sizeof(value), COPY_ALL_CHARACTERS );
  275. mapData->SetValue( "targetname", value );
  276. }
  277. }
  278. }
  279. // Delete our group parsers
  280. for ( i = 0; i < iCount; i++ )
  281. {
  282. delete GroupTemplates[i].pMapDataParser;
  283. }
  284. GroupTemplates.Purge();
  285. }
  286. //-----------------------------------------------------------------------------
  287. // Purpose: Someone's about to start instancing a new group of entities.
  288. // Generate a unique identifier for this group.
  289. //-----------------------------------------------------------------------------
  290. void Templates_StartUniqueInstance( void )
  291. {
  292. g_iCurrentTemplateInstance++;
  293. // Make sure there's enough room to fit it into the string
  294. int iMax = pow(10.0f, (int)((strlen(ENTITYIO_FIXUP_STRING) - 1))); // -1 for the &
  295. if ( g_iCurrentTemplateInstance >= iMax )
  296. {
  297. // We won't hit this.
  298. Assert(0);
  299. // Hopefully there were still be instance number 0 around.
  300. g_iCurrentTemplateInstance = 0;
  301. }
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose: Someone wants to spawn an instance of a template that requires
  305. // entity IO fixup. Fill out the pMapData with a copy of the template
  306. // with unique key/values where the template requires them.
  307. //-----------------------------------------------------------------------------
  308. char *Templates_GetEntityIOFixedMapData( int iIndex )
  309. {
  310. Assert( Templates_IndexRequiresEntityIOFixup( iIndex ) );
  311. // First time through?
  312. if ( !g_Templates[iIndex]->pszFixedMapData )
  313. {
  314. g_Templates[iIndex]->pszFixedMapData = new char[g_Templates[iIndex]->iMapDataLength];
  315. Q_strncpy( g_Templates[iIndex]->pszFixedMapData, g_Templates[iIndex]->pszMapData, g_Templates[iIndex]->iMapDataLength );
  316. }
  317. int iFixupSize = strlen(ENTITYIO_FIXUP_STRING); // strlen("&0000\0") = 5!
  318. char *sOurFixup = new char[iFixupSize+1]; // do alloc room here for the null terminator
  319. Q_snprintf( sOurFixup, iFixupSize+1, "%c%.4d", ENTITYIO_FIXUP_STRING[0], g_iCurrentTemplateInstance );
  320. // Now rip through the map data string and replace any instances of the fixup string with our unique identifier
  321. char *c = g_Templates[iIndex]->pszFixedMapData;
  322. do
  323. {
  324. if ( *c == ENTITYIO_FIXUP_STRING[0] )
  325. {
  326. // Make sure it's our fixup string
  327. bool bValid = true;
  328. for ( int i = 1; i < iFixupSize; i++ )
  329. {
  330. // Look for any number, because we've already used this string
  331. if ( !(*(c+i) >= '0' && *(c+i) <= '9') )
  332. {
  333. // Some other string
  334. bValid = false;
  335. break;
  336. }
  337. }
  338. // Stomp it with our unique string
  339. if ( bValid )
  340. {
  341. memcpy( c, sOurFixup, iFixupSize );
  342. c += iFixupSize;
  343. }
  344. }
  345. c++;
  346. } while (*c);
  347. delete [] sOurFixup;
  348. return g_Templates[iIndex]->pszFixedMapData;
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Purpose: Frees all the template data. Called on level shutdown.
  352. //-----------------------------------------------------------------------------
  353. void Templates_RemoveAll(void)
  354. {
  355. int nCount = g_Templates.Count();
  356. for (int i = 0; i < nCount; i++)
  357. {
  358. TemplateEntityData_t *pTemplate = g_Templates.Element(i);
  359. Templates_FreeTemplate( pTemplate );
  360. }
  361. g_Templates.RemoveAll();
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose: Hooks in the template manager's callbacks.
  365. //-----------------------------------------------------------------------------
  366. class CTemplatesHook : public CAutoGameSystem
  367. {
  368. public:
  369. CTemplatesHook( char const *name ) : CAutoGameSystem( name )
  370. {
  371. }
  372. virtual void LevelShutdownPostEntity( void )
  373. {
  374. Templates_RemoveAll();
  375. }
  376. };
  377. CTemplatesHook g_TemplateEntityHook( "CTemplatesHook" );
  378. //-----------------------------------------------------------------------------
  379. // TEMPLATE SAVE / RESTORE
  380. //-----------------------------------------------------------------------------
  381. static short TEMPLATE_SAVE_RESTORE_VERSION = 1;
  382. class CTemplate_SaveRestoreBlockHandler : public CDefSaveRestoreBlockHandler
  383. {
  384. public:
  385. const char *GetBlockName()
  386. {
  387. return "Templates";
  388. }
  389. //---------------------------------
  390. void Save( ISave *pSave )
  391. {
  392. pSave->WriteInt( &g_iCurrentTemplateInstance );
  393. short nCount = g_Templates.Count();
  394. pSave->WriteShort( &nCount );
  395. for ( int i = 0; i < nCount; i++ )
  396. {
  397. TemplateEntityData_t *pTemplate = g_Templates[i];
  398. pSave->WriteAll( pTemplate );
  399. pSave->WriteString( pTemplate->pszName );
  400. pSave->WriteString( pTemplate->pszMapData );
  401. }
  402. }
  403. //---------------------------------
  404. void WriteSaveHeaders( ISave *pSave )
  405. {
  406. pSave->WriteShort( &TEMPLATE_SAVE_RESTORE_VERSION );
  407. }
  408. //---------------------------------
  409. void ReadRestoreHeaders( IRestore *pRestore )
  410. {
  411. // No reason why any future version shouldn't try to retain backward compatability. The default here is to not do so.
  412. short version;
  413. pRestore->ReadShort( &version );
  414. m_fDoLoad = ( version == TEMPLATE_SAVE_RESTORE_VERSION );
  415. }
  416. //---------------------------------
  417. void Restore( IRestore *pRestore, bool createPlayers )
  418. {
  419. if ( m_fDoLoad )
  420. {
  421. Templates_RemoveAll();
  422. g_Templates.Purge();
  423. g_iCurrentTemplateInstance = pRestore->ReadInt();
  424. int iTemplates = pRestore->ReadShort();
  425. while ( iTemplates-- )
  426. {
  427. TemplateEntityData_t *pNewTemplate = (TemplateEntityData_t *)malloc(sizeof(TemplateEntityData_t));
  428. pRestore->ReadAll( pNewTemplate );
  429. int sizeData = 0;//pRestore->SkipHeader();
  430. char szName[MAPKEY_MAXLENGTH];
  431. pRestore->ReadString( szName, MAPKEY_MAXLENGTH, sizeData );
  432. pNewTemplate->pszName = strdup( szName );
  433. //sizeData = pRestore->SkipHeader();
  434. pNewTemplate->pszMapData = (char *)malloc( pNewTemplate->iMapDataLength );
  435. pRestore->ReadString( pNewTemplate->pszMapData, pNewTemplate->iMapDataLength, sizeData );
  436. // Set this to NULL so it'll be created the first time it gets used
  437. pNewTemplate->pszFixedMapData = NULL;
  438. g_Templates.AddToTail( pNewTemplate );
  439. }
  440. }
  441. }
  442. private:
  443. bool m_fDoLoad;
  444. };
  445. //-----------------------------------------------------------------------------
  446. CTemplate_SaveRestoreBlockHandler g_Template_SaveRestoreBlockHandler;
  447. //-------------------------------------
  448. ISaveRestoreBlockHandler *GetTemplateSaveRestoreBlockHandler()
  449. {
  450. return &g_Template_SaveRestoreBlockHandler;
  451. }