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.

510 lines
17 KiB

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