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.

922 lines
30 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include <stdio.h>
  9. //#define GAME_DLL
  10. #ifdef GAME_DLL
  11. #include "cbase.h"
  12. #endif
  13. #include <stdio.h>
  14. #include "interface.h"
  15. #include "filesystem.h"
  16. #include "engine/iserverplugin.h"
  17. #include "eiface.h"
  18. #include "igameevents.h"
  19. #include "convar.h"
  20. #include "Color.h"
  21. #include "vstdlib/random.h"
  22. #include "engine/IEngineTrace.h"
  23. #include "tier2/tier2.h"
  24. #include "game/server/pluginvariant.h"
  25. #include "game/server/iplayerinfo.h"
  26. #include "game/server/ientityinfo.h"
  27. #include "game/server/igameinfo.h"
  28. //#define SAMPLE_TF2_PLUGIN
  29. #ifdef SAMPLE_TF2_PLUGIN
  30. #include "tf/tf_shareddefs.h"
  31. #endif
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. // Interfaces from the engine
  35. IVEngineServer *engine = NULL; // helper functions (messaging clients, loading content, making entities, running commands, etc)
  36. IGameEventManager *gameeventmanager_ = NULL; // game events interface
  37. #ifndef GAME_DLL
  38. #define gameeventmanager gameeventmanager_
  39. #endif
  40. IPlayerInfoManager *playerinfomanager = NULL; // game dll interface to interact with players
  41. IEntityInfoManager *entityinfomanager = NULL; // game dll interface to interact with all entities (like IPlayerInfo)
  42. IGameInfoManager *gameinfomanager = NULL; // game dll interface to get data from game rules directly
  43. IBotManager *botmanager = NULL; // game dll interface to interact with bots
  44. IServerPluginHelpers *helpers = NULL; // special 3rd party plugin helpers from the engine
  45. IUniformRandomStream *randomStr = NULL;
  46. IEngineTrace *enginetrace = NULL;
  47. CGlobalVars *gpGlobals = NULL;
  48. // function to initialize any cvars/command in this plugin
  49. void Bot_RunAll( void );
  50. // useful helper func
  51. #ifndef GAME_DLL
  52. inline bool FStrEq(const char *sz1, const char *sz2)
  53. {
  54. return(Q_stricmp(sz1, sz2) == 0);
  55. }
  56. #endif
  57. //---------------------------------------------------------------------------------
  58. // Purpose: a sample 3rd party plugin class
  59. //---------------------------------------------------------------------------------
  60. class CEmptyServerPlugin: public IServerPluginCallbacks, public IGameEventListener
  61. {
  62. public:
  63. CEmptyServerPlugin();
  64. ~CEmptyServerPlugin();
  65. // IServerPluginCallbacks methods
  66. virtual bool Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory );
  67. virtual void Unload( void );
  68. virtual void Pause( void );
  69. virtual void UnPause( void );
  70. virtual const char *GetPluginDescription( void );
  71. virtual void LevelInit( char const *pMapName );
  72. virtual void ServerActivate( edict_t *pEdictList, int edictCount, int clientMax );
  73. virtual void GameFrame( bool simulating );
  74. virtual void LevelShutdown( void );
  75. virtual void ClientActive( edict_t *pEntity );
  76. virtual void ClientDisconnect( edict_t *pEntity );
  77. virtual void ClientPutInServer( edict_t *pEntity, char const *playername );
  78. virtual void SetCommandClient( int index );
  79. virtual void ClientSettingsChanged( edict_t *pEdict );
  80. virtual PLUGIN_RESULT ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen );
  81. virtual PLUGIN_RESULT ClientCommand( edict_t *pEntity, const CCommand &args );
  82. virtual PLUGIN_RESULT NetworkIDValidated( const char *pszUserName, const char *pszNetworkID );
  83. virtual void OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue );
  84. virtual void OnEdictAllocated( edict_t *edict );
  85. virtual void OnEdictFreed( const edict_t *edict );
  86. // IGameEventListener Interface
  87. virtual void FireGameEvent( KeyValues * event );
  88. virtual int GetCommandIndex() { return m_iClientCommandIndex; }
  89. private:
  90. int m_iClientCommandIndex;
  91. };
  92. //
  93. // The plugin is a static singleton that is exported as an interface
  94. //
  95. CEmptyServerPlugin g_EmtpyServerPlugin;
  96. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CEmptyServerPlugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, g_EmtpyServerPlugin );
  97. //---------------------------------------------------------------------------------
  98. // Purpose: constructor/destructor
  99. //---------------------------------------------------------------------------------
  100. CEmptyServerPlugin::CEmptyServerPlugin()
  101. {
  102. m_iClientCommandIndex = 0;
  103. }
  104. CEmptyServerPlugin::~CEmptyServerPlugin()
  105. {
  106. }
  107. //---------------------------------------------------------------------------------
  108. // Purpose: called when the plugin is loaded, load the interface we need from the engine
  109. //---------------------------------------------------------------------------------
  110. bool CEmptyServerPlugin::Load( CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory )
  111. {
  112. ConnectTier1Libraries( &interfaceFactory, 1 );
  113. ConnectTier2Libraries( &interfaceFactory, 1 );
  114. entityinfomanager = (IEntityInfoManager *)gameServerFactory(INTERFACEVERSION_ENTITYINFOMANAGER,NULL);
  115. if ( !entityinfomanager )
  116. {
  117. Warning( "Unable to load entityinfomanager, ignoring\n" ); // this isn't fatal, we just won't be able to access entity data
  118. }
  119. playerinfomanager = (IPlayerInfoManager *)gameServerFactory(INTERFACEVERSION_PLAYERINFOMANAGER,NULL);
  120. if ( !playerinfomanager )
  121. {
  122. Warning( "Unable to load playerinfomanager, ignoring\n" ); // this isn't fatal, we just won't be able to access specific player data
  123. }
  124. botmanager = (IBotManager *)gameServerFactory(INTERFACEVERSION_PLAYERBOTMANAGER, NULL);
  125. if ( !botmanager )
  126. {
  127. Warning( "Unable to load botcontroller, ignoring\n" ); // this isn't fatal, we just won't be able to access specific bot functions
  128. }
  129. gameinfomanager = (IGameInfoManager *)gameServerFactory(INTERFACEVERSION_GAMEINFOMANAGER, NULL);
  130. if (!gameinfomanager)
  131. {
  132. Warning( "Unable to load gameinfomanager, ignoring\n" );
  133. }
  134. engine = (IVEngineServer*)interfaceFactory(INTERFACEVERSION_VENGINESERVER, NULL);
  135. gameeventmanager = (IGameEventManager *)interfaceFactory(INTERFACEVERSION_GAMEEVENTSMANAGER,NULL);
  136. helpers = (IServerPluginHelpers*)interfaceFactory(INTERFACEVERSION_ISERVERPLUGINHELPERS, NULL);
  137. enginetrace = (IEngineTrace *)interfaceFactory(INTERFACEVERSION_ENGINETRACE_SERVER,NULL);
  138. randomStr = (IUniformRandomStream *)interfaceFactory(VENGINE_SERVER_RANDOM_INTERFACE_VERSION, NULL);
  139. // get the interfaces we want to use
  140. if( ! ( engine && gameeventmanager && g_pFullFileSystem && helpers && enginetrace && randomStr ) )
  141. {
  142. return false; // we require all these interface to function
  143. }
  144. if ( playerinfomanager )
  145. {
  146. gpGlobals = playerinfomanager->GetGlobalVars();
  147. }
  148. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f );
  149. ConVar_Register( 0 );
  150. return true;
  151. }
  152. //---------------------------------------------------------------------------------
  153. // Purpose: called when the plugin is unloaded (turned off)
  154. //---------------------------------------------------------------------------------
  155. void CEmptyServerPlugin::Unload( void )
  156. {
  157. gameeventmanager->RemoveListener( this ); // make sure we are unloaded from the event system
  158. ConVar_Unregister( );
  159. DisconnectTier2Libraries( );
  160. DisconnectTier1Libraries( );
  161. }
  162. //---------------------------------------------------------------------------------
  163. // Purpose: called when the plugin is paused (i.e should stop running but isn't unloaded)
  164. //---------------------------------------------------------------------------------
  165. void CEmptyServerPlugin::Pause( void )
  166. {
  167. }
  168. //---------------------------------------------------------------------------------
  169. // Purpose: called when the plugin is unpaused (i.e should start executing again)
  170. //---------------------------------------------------------------------------------
  171. void CEmptyServerPlugin::UnPause( void )
  172. {
  173. }
  174. //---------------------------------------------------------------------------------
  175. // Purpose: the name of this plugin, returned in "plugin_print" command
  176. //---------------------------------------------------------------------------------
  177. const char *CEmptyServerPlugin::GetPluginDescription( void )
  178. {
  179. return "Emtpy-Plugin V2, Valve";
  180. }
  181. //---------------------------------------------------------------------------------
  182. // Purpose: called on level start
  183. //---------------------------------------------------------------------------------
  184. void CEmptyServerPlugin::LevelInit( char const *pMapName )
  185. {
  186. Msg( "Level \"%s\" has been loaded\n", pMapName );
  187. gameeventmanager->AddListener( this, true );
  188. }
  189. //---------------------------------------------------------------------------------
  190. // Purpose: called on level start, when the server is ready to accept client connections
  191. // edictCount is the number of entities in the level, clientMax is the max client count
  192. //---------------------------------------------------------------------------------
  193. void CEmptyServerPlugin::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
  194. {
  195. }
  196. //---------------------------------------------------------------------------------
  197. // Purpose: called once per server frame, do recurring work here (like checking for timeouts)
  198. //---------------------------------------------------------------------------------
  199. void CEmptyServerPlugin::GameFrame( bool simulating )
  200. {
  201. if ( simulating )
  202. {
  203. Bot_RunAll();
  204. }
  205. }
  206. //---------------------------------------------------------------------------------
  207. // Purpose: called on level end (as the server is shutting down or going to a new map)
  208. //---------------------------------------------------------------------------------
  209. void CEmptyServerPlugin::LevelShutdown( void ) // !!!!this can get called multiple times per map change
  210. {
  211. gameeventmanager->RemoveListener( this );
  212. }
  213. //---------------------------------------------------------------------------------
  214. // Purpose: called when a client spawns into a server (i.e as they begin to play)
  215. //---------------------------------------------------------------------------------
  216. void CEmptyServerPlugin::ClientActive( edict_t *pEntity )
  217. {
  218. }
  219. //---------------------------------------------------------------------------------
  220. // Purpose: called when a client leaves a server (or is timed out)
  221. //---------------------------------------------------------------------------------
  222. void CEmptyServerPlugin::ClientDisconnect( edict_t *pEntity )
  223. {
  224. }
  225. //---------------------------------------------------------------------------------
  226. // Purpose: called on
  227. //---------------------------------------------------------------------------------
  228. void CEmptyServerPlugin::ClientPutInServer( edict_t *pEntity, char const *playername )
  229. {
  230. KeyValues *kv = new KeyValues( "msg" );
  231. kv->SetString( "title", "Hello" );
  232. kv->SetString( "msg", "Hello there" );
  233. kv->SetColor( "color", Color( 255, 0, 0, 255 ));
  234. kv->SetInt( "level", 5);
  235. kv->SetInt( "time", 10);
  236. helpers->CreateMessage( pEntity, DIALOG_MSG, kv, this );
  237. kv->deleteThis();
  238. }
  239. //---------------------------------------------------------------------------------
  240. // Purpose: called on level start
  241. //---------------------------------------------------------------------------------
  242. void CEmptyServerPlugin::SetCommandClient( int index )
  243. {
  244. m_iClientCommandIndex = index;
  245. }
  246. void ClientPrint( edict_t *pEdict, char *format, ... )
  247. {
  248. va_list argptr;
  249. static char string[1024];
  250. va_start (argptr, format);
  251. Q_vsnprintf(string, sizeof(string), format,argptr);
  252. va_end (argptr);
  253. engine->ClientPrintf( pEdict, string );
  254. }
  255. //---------------------------------------------------------------------------------
  256. // Purpose: called on level start
  257. //---------------------------------------------------------------------------------
  258. void CEmptyServerPlugin::ClientSettingsChanged( edict_t *pEdict )
  259. {
  260. if ( playerinfomanager )
  261. {
  262. IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEdict );
  263. const char * name = engine->GetClientConVarValue( engine->IndexOfEdict(pEdict), "name" );
  264. // CAN'T use Q_stricmp here, this dll is made by 3rd parties and may not link to tier0/vstdlib
  265. if ( playerinfo && name && playerinfo->GetName() &&
  266. stricmp( name, playerinfo->GetName()) ) // playerinfo may be NULL if the MOD doesn't support access to player data
  267. // OR if you are accessing the player before they are fully connected
  268. {
  269. ClientPrint( pEdict, "Your name changed to \"%s\" (from \"%s\"\n", name, playerinfo->GetName() );
  270. // this is the bad way to check this, the better option it to listen for the "player_changename" event in FireGameEvent()
  271. // this is here to give a real example of how to use the playerinfo interface
  272. }
  273. }
  274. }
  275. //---------------------------------------------------------------------------------
  276. // Purpose: called when a client joins a server
  277. //---------------------------------------------------------------------------------
  278. PLUGIN_RESULT CEmptyServerPlugin::ClientConnect( bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
  279. {
  280. return PLUGIN_CONTINUE;
  281. }
  282. CON_COMMAND( DoAskConnect, "Server plugin example of using the ask connect dialog" )
  283. {
  284. if ( args.ArgC() < 2 )
  285. {
  286. Warning ( "DoAskConnect <server IP>\n" );
  287. }
  288. else
  289. {
  290. const char *pServerIP = args.Arg( 1 );
  291. KeyValues *kv = new KeyValues( "menu" );
  292. kv->SetString( "title", pServerIP ); // The IP address of the server to connect to goes in the "title" field.
  293. kv->SetInt( "time", 3 );
  294. for ( int i=1; i < gpGlobals->maxClients; i++ )
  295. {
  296. edict_t *pEdict = engine->PEntityOfEntIndex( i );
  297. if ( pEdict )
  298. {
  299. helpers->CreateMessage( pEdict, DIALOG_ASKCONNECT, kv, &g_EmtpyServerPlugin );
  300. }
  301. }
  302. kv->deleteThis();
  303. }
  304. }
  305. #ifdef SAMPLE_TF2_PLUGIN
  306. const char *classNames[] =
  307. {
  308. "unknown",
  309. "scout",
  310. "sniper",
  311. "soldier",
  312. "demoman",
  313. "medic",
  314. "heavy weapons guy",
  315. "pyro",
  316. "spy",
  317. "engineer",
  318. };
  319. bool TFPlayerHasCondition( int inBits, int condition )
  320. {
  321. Assert( condition >= 0 && condition < TF_COND_LAST );
  322. return ( ( inBits & (1<<condition) ) != 0 );
  323. }
  324. void SentryStatus( edict_t *pEntity )
  325. {
  326. IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
  327. if (!playerinfo)
  328. {
  329. Msg("couldn't get playerinfo\n");
  330. return;
  331. }
  332. Msg("Sentry Status:\n");
  333. pluginvariant value;
  334. pluginvariant emptyVariant;
  335. edict_t *pSentry = NULL;
  336. if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_SENTRY, value, emptyVariant))
  337. {
  338. pSentry = engine->PEntityOfEntIndex( value.Int() );
  339. if (!pSentry)
  340. {
  341. Warning("couldn't attain sentry gun entity\n");
  342. return;
  343. }
  344. }
  345. else
  346. {
  347. Msg("No Sentrygun built.\n");
  348. return;
  349. }
  350. IEntityInfo *entinfo = entityinfomanager->GetEntityInfo( pSentry );
  351. if (!entinfo)
  352. {
  353. Warning("couldn't get entinfo for sentry gun\n");
  354. return;
  355. }
  356. if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_SENTRY, value, emptyVariant))
  357. {
  358. if (value.Bool())
  359. Msg("Sentry Under Construction...\n");
  360. }
  361. if (playerinfo->GetCustomInfo(TFPLAYERINFO_UPGRADING_SENTRY, value, emptyVariant))
  362. {
  363. if (value.Bool())
  364. Msg("Sentry Upgrading...\n");
  365. }
  366. int sentryLevel = 0;
  367. if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_LEVEL, value, emptyVariant))
  368. {
  369. sentryLevel = value.Int();
  370. Msg("Sentry Level: %i\n", sentryLevel );
  371. }
  372. else
  373. Msg("Unable to retrive sentry level\n");
  374. if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_PROGRESS, value, emptyVariant))
  375. {
  376. if (sentryLevel < 3)
  377. {
  378. int iMetal, iRequiredMetal;
  379. iRequiredMetal = value.Int() & 0xFF;
  380. iMetal = (value.Int()>>8) & 0xFF;
  381. Msg("%i / %i Metal Required for Sentry Level %i\n", iMetal, iRequiredMetal, sentryLevel+1);
  382. }
  383. else
  384. Msg("Sentry cannot be upgraded further.\n");
  385. }
  386. Msg("Health: %i\n", entinfo->GetHealth() );
  387. if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_KILLS, value, emptyVariant))
  388. Msg("Kills: %i\n", value.Int() );
  389. else
  390. Msg("Unable to retrieve sentry kills\n");
  391. if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_AMMO_SHELLS, value, emptyVariant))
  392. {
  393. int iShells, iMaxShells;
  394. iMaxShells = value.Int() & 0xFF;
  395. iShells = (value.Int()>>8) & 0xFF;
  396. Msg("Shells: %i / %i\n", iShells, iMaxShells);
  397. }
  398. if (sentryLevel > 2)
  399. {
  400. if (playerinfo->GetCustomInfo(TFPLAYERINFO_SENTRY_AMMO_ROCKETS, value, emptyVariant))
  401. {
  402. int iRockets, iMaxRockets;
  403. iMaxRockets = value.Int() & 0xFF;
  404. iRockets = (value.Int()>>8) & 0xFF;
  405. Msg("Rockets: %i / %i\n", iRockets, iMaxRockets);
  406. }
  407. }
  408. }
  409. void DispenserStatus( edict_t *pEntity )
  410. {
  411. IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
  412. if (!playerinfo)
  413. {
  414. Msg("couldn't get playerinfo\n");
  415. return;
  416. }
  417. Msg("Dispenser Status:\n");
  418. pluginvariant value;
  419. pluginvariant emptyVariant;
  420. edict_t *pDispenser = NULL;
  421. if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_DISPENSER, value, emptyVariant))
  422. {
  423. pDispenser = engine->PEntityOfEntIndex( value.Int() );
  424. if (!pDispenser)
  425. {
  426. Warning("couldn't attain dispenser entity\n");
  427. return;
  428. }
  429. }
  430. else
  431. {
  432. Msg("No dispenser built.\n");
  433. return;
  434. }
  435. IEntityInfo *entinfo = entityinfomanager->GetEntityInfo( pDispenser );
  436. if (!entinfo)
  437. {
  438. Warning("couldn't get entinfo for dispenser\n");
  439. return;
  440. }
  441. if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_DISPENSER, value, emptyVariant))
  442. {
  443. if (value.Bool())
  444. Msg("Dispenser Under Construction...\n");
  445. }
  446. Msg("Health: %i\n", entinfo->GetHealth() );
  447. if (playerinfo->GetCustomInfo(TFPLAYERINFO_DISPENSER_METAL, value, emptyVariant))
  448. Msg("Metal: %i\n", value.Int() );
  449. }
  450. void TeleporterStatus( edict_t *pEntity )
  451. {
  452. IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
  453. if (!playerinfo)
  454. {
  455. Msg("couldn't get playerinfo\n");
  456. return;
  457. }
  458. Msg("Teleporter Status:\n");
  459. pluginvariant value;
  460. pluginvariant emptyVariant;
  461. edict_t *pEntrance = NULL;
  462. edict_t *pExit = NULL;
  463. if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_TELEPORTER_ENTRANCE, value, emptyVariant))
  464. {
  465. pEntrance = engine->PEntityOfEntIndex( value.Int() );
  466. if (!pEntrance)
  467. {
  468. Warning("couldn't attain entrance entity\n");
  469. }
  470. }
  471. else
  472. {
  473. Msg("No Teleporter Entrance built.\n");
  474. }
  475. if (playerinfo->GetCustomInfo(TFPLAYERINFO_ENTINDEX_TELEPORTER_EXIT, value, emptyVariant))
  476. {
  477. pExit = engine->PEntityOfEntIndex( value.Int() );
  478. if (!pExit)
  479. {
  480. Warning("couldn't attain exit entity\n");
  481. }
  482. }
  483. else
  484. {
  485. Msg("No Teleporter Entrance built.\n");
  486. }
  487. IEntityInfo *entranceInfo = entityinfomanager->GetEntityInfo( pEntrance );
  488. if (!entranceInfo)
  489. {
  490. Warning("couldn't get entinfo for teleporter entrance\n");
  491. }
  492. IEntityInfo *exitInfo = entityinfomanager->GetEntityInfo( pExit );
  493. if (!exitInfo)
  494. {
  495. Warning("couldn't get entinfo for teleporter exit\n");
  496. }
  497. if (pEntrance && entranceInfo)
  498. {
  499. if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_TELEPORTER_ENTRANCE, value, emptyVariant))
  500. {
  501. if (value.Bool())
  502. Msg("Entrance Under Construction...\n");
  503. }
  504. Msg("Entrance Health: %i\n", entranceInfo->GetHealth() );
  505. if (playerinfo->GetCustomInfo(TFPLAYERINFO_TELEPORTER_USES, value, emptyVariant))
  506. Msg("Entrance Used %i Times.\n", value.Int() );
  507. }
  508. if (pExit && exitInfo)
  509. {
  510. if (playerinfo->GetCustomInfo(TFPLAYERINFO_BUILDING_TELEPORTER_EXIT, value, emptyVariant))
  511. {
  512. if (value.Bool())
  513. Msg("Exit Under Construction...\n");
  514. }
  515. Msg("Exit Health: %i\n", exitInfo->GetHealth() );
  516. }
  517. }
  518. void ClassStatus( edict_t *pEntity )
  519. {
  520. IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
  521. if (!playerinfo)
  522. {
  523. Msg("couldn't get playerinfo\n");
  524. return;
  525. }
  526. int playerClassId = playerinfo->GetPlayerClassId();
  527. Msg("Player Class: %s\n", playerinfo->GetPlayerClassName());
  528. pluginvariant conditionValue;
  529. pluginvariant emptyVariant;
  530. if (!playerinfo->GetCustomInfo(TFPLAYERINFO_CONDITIONS, conditionValue, emptyVariant))
  531. {
  532. Warning("unable to retrieve conditions!\n");
  533. }
  534. if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_INVULNERABLE ))
  535. Msg("You are Invulnerable!\n");
  536. if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_SELECTED_TO_TELEPORT ))
  537. Msg("You are about to Teleport.\n");
  538. if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_TELEPORTED ))
  539. Msg("You have recently been teleported.\n");
  540. switch(playerClassId)
  541. {
  542. default:
  543. case TF_CLASS_MEDIC:
  544. break;
  545. case TF_CLASS_ENGINEER:
  546. Msg("Building Information:\n");
  547. SentryStatus( pEntity );
  548. DispenserStatus( pEntity );
  549. TeleporterStatus( pEntity );
  550. break;
  551. case TF_CLASS_SPY:
  552. {
  553. int disguiseClass = 0;
  554. pluginvariant value;
  555. if (playerinfo->GetCustomInfo(TFPLAYERINFO_SPY_DISGUISEDAS, value, emptyVariant))
  556. disguiseClass = value.Int();
  557. if ( TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISING ) )
  558. Msg("Disguising..\n");
  559. else if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISED ) )
  560. Msg("Disguised as: %s\n", classNames[disguiseClass] );
  561. if (TFPlayerHasCondition(conditionValue.Int(), TF_COND_STEALTHED ))
  562. Msg("Cloaked!\n");
  563. if (playerinfo->GetCustomInfo(TFPLAYERINFO_SPY_CLOAKCHARGELEVEL, value, emptyVariant))
  564. Msg("Cloak Charge Percent: %d\n", value.Float() );
  565. break;
  566. }
  567. case TF_CLASS_DEMOMAN:
  568. break;
  569. }
  570. }
  571. const char *ctf_flagtype[] =
  572. {
  573. "ctf", //TF_FLAGTYPE_CTF = 0,
  574. "attack / defend", //TF_FLAGTYPE_ATTACK_DEFEND,
  575. "territory control", //TF_FLAGTYPE_TERRITORY_CONTROL,
  576. "invade", //TF_FLAGTYPE_INVADE,
  577. "king of the hill", //TF_FLAGTYPE_KINGOFTHEHILL,
  578. };
  579. const char *ctf_flagstatus[] =
  580. {
  581. "unknown",
  582. "At Home",
  583. "Dropped",
  584. "Stolen",
  585. };
  586. void FlagStatus( edict_t *pPlayer )
  587. {
  588. IPlayerInfo *pInfo = playerinfomanager->GetPlayerInfo( pPlayer );
  589. if (!pInfo)
  590. {
  591. Msg( "couldn't get playerinfo\n" );
  592. return;
  593. }
  594. IGameInfo *gameInfo = gameinfomanager->GetGameInfo();
  595. if (!gameInfo)
  596. {
  597. Msg( "couldn't get gameinfo\n" );
  598. }
  599. int gameType = gameInfo->GetInfo_GameType();
  600. if (gameType != 1)
  601. {
  602. Msg( "Game is not CTF.\n" );
  603. return;
  604. }
  605. Msg( "===============================\n" );
  606. Msg( "Capture The Flag -- Flag Status\n" );
  607. Msg( "===============================\n" );
  608. pluginvariant value, options;
  609. edict_t *pFlag = NULL;
  610. while ( (pFlag = entityinfomanager->FindEntityByClassname(pFlag, "item_teamflag")) != NULL )
  611. {
  612. IEntityInfo *pFlagInfo = entityinfomanager->GetEntityInfo( pFlag );
  613. if (!pFlagInfo)
  614. continue;
  615. Msg( "\nTeam %s's Flag\n", gameInfo->GetInfo_GetTeamName( pFlagInfo->GetTeamIndex() ) );
  616. options.SetInt(engine->IndexOfEdict(pFlag));
  617. if ( gameInfo->GetInfo_Custom( TFGAMEINFO_CTF_FLAG_TYPE, value, options) )
  618. Msg( "Type: %s\n", ctf_flagtype[value.Int()] );
  619. if ( gameInfo->GetInfo_Custom( TFGAMEINFO_CTF_FLAG_STATUS, value, options) )
  620. {
  621. Msg( "Status: %s\n", ctf_flagstatus[value.Int()] );
  622. //Tony; if we're carried, find out who has us.
  623. if (value.Int() == 3)
  624. {
  625. edict_t *pPlayer = pFlagInfo->GetOwner();
  626. if (pPlayer)
  627. {
  628. IPlayerInfo *pPlayerInfo = playerinfomanager->GetPlayerInfo( pPlayer );
  629. if (pPlayerInfo)
  630. Msg( "Carried by: %s\n", pPlayerInfo->GetName() );
  631. }
  632. }
  633. }
  634. }
  635. Msg( "===============================\n" );
  636. }
  637. #endif
  638. //---------------------------------------------------------------------------------
  639. // Purpose: called when a client types in a command (only a subset of commands however, not CON_COMMAND's)
  640. //---------------------------------------------------------------------------------
  641. PLUGIN_RESULT CEmptyServerPlugin::ClientCommand( edict_t *pEntity, const CCommand &args )
  642. {
  643. const char *pcmd = args[0];
  644. if ( !pEntity || pEntity->IsFree() )
  645. {
  646. return PLUGIN_CONTINUE;
  647. }
  648. if ( FStrEq( pcmd, "menu" ) )
  649. {
  650. KeyValues *kv = new KeyValues( "menu" );
  651. kv->SetString( "title", "You've got options, hit ESC" );
  652. kv->SetInt( "level", 1 );
  653. kv->SetColor( "color", Color( 255, 0, 0, 255 ));
  654. kv->SetInt( "time", 20 );
  655. kv->SetString( "msg", "Pick an option\nOr don't." );
  656. for( int i = 1; i < 9; i++ )
  657. {
  658. char num[10], msg[10], cmd[10];
  659. Q_snprintf( num, sizeof(num), "%i", i );
  660. Q_snprintf( msg, sizeof(msg), "Option %i", i );
  661. Q_snprintf( cmd, sizeof(cmd), "option%i", i );
  662. KeyValues *item1 = kv->FindKey( num, true );
  663. item1->SetString( "msg", msg );
  664. item1->SetString( "command", cmd );
  665. }
  666. helpers->CreateMessage( pEntity, DIALOG_MENU, kv, this );
  667. kv->deleteThis();
  668. return PLUGIN_STOP; // we handled this function
  669. }
  670. else if ( FStrEq( pcmd, "rich" ) )
  671. {
  672. KeyValues *kv = new KeyValues( "menu" );
  673. kv->SetString( "title", "A rich message" );
  674. kv->SetInt( "level", 1 );
  675. kv->SetInt( "time", 20 );
  676. kv->SetString( "msg", "This is a long long long text string.\n\nIt also has line breaks." );
  677. helpers->CreateMessage( pEntity, DIALOG_TEXT, kv, this );
  678. kv->deleteThis();
  679. return PLUGIN_STOP; // we handled this function
  680. }
  681. else if ( FStrEq( pcmd, "msg" ) )
  682. {
  683. KeyValues *kv = new KeyValues( "menu" );
  684. kv->SetString( "title", "Just a simple hello" );
  685. kv->SetInt( "level", 1 );
  686. kv->SetInt( "time", 20 );
  687. helpers->CreateMessage( pEntity, DIALOG_MSG, kv, this );
  688. kv->deleteThis();
  689. return PLUGIN_STOP; // we handled this function
  690. }
  691. else if ( FStrEq( pcmd, "entry" ) )
  692. {
  693. KeyValues *kv = new KeyValues( "entry" );
  694. kv->SetString( "title", "Stuff" );
  695. kv->SetString( "msg", "Enter something" );
  696. kv->SetString( "command", "say" ); // anything they enter into the dialog turns into a say command
  697. kv->SetInt( "level", 1 );
  698. kv->SetInt( "time", 20 );
  699. helpers->CreateMessage( pEntity, DIALOG_ENTRY, kv, this );
  700. kv->deleteThis();
  701. return PLUGIN_STOP; // we handled this function
  702. }
  703. #ifdef SAMPLE_TF2_PLUGIN
  704. else if ( FStrEq( pcmd, "gameinfo" ) )
  705. {
  706. IGameInfo *gameInfo = gameinfomanager->GetGameInfo();
  707. if (!gameInfo)
  708. return PLUGIN_STOP;
  709. Msg("=== Game Information ===\n");
  710. Msg("Game Type: %i / %s\n", gameInfo->GetInfo_GameType(), gameInfo->GetInfo_GameTypeName() );
  711. int teamCount = gameInfo->GetInfo_GetTeamCount();
  712. Msg("Num Teams: %i\n", teamCount );
  713. Msg("Player Counts:\n");
  714. for (int i = 0;i<teamCount;i++)
  715. {
  716. //If this failes, we can assume the rest is invalid too.
  717. if (!gameInfo->GetInfo_GetTeamName(i) )
  718. continue;
  719. Msg("Team: %s, Players: %i\n", gameInfo->GetInfo_GetTeamName(i), gameInfo->GetInfo_NumPlayersOnTeam(i) );
  720. }
  721. return PLUGIN_STOP;
  722. }
  723. // Sample to use the new CustomInfo added to TF2 for plugins
  724. else if ( FStrEq( pcmd, "tfcond" ) )
  725. {
  726. IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
  727. if (!playerinfo)
  728. return PLUGIN_STOP;
  729. pluginvariant conditionValue;
  730. pluginvariant emptyVariant;
  731. if (!playerinfo->GetCustomInfo(TFPLAYERINFO_CONDITIONS, conditionValue, emptyVariant))
  732. {
  733. Msg("unable to retrieve conditions!\n");
  734. return PLUGIN_STOP;
  735. }
  736. Msg("Disguising?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISING ) ? "yes" : "no" );
  737. Msg("Disguised?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_DISGUISED ) ? "yes" : "no" );
  738. Msg("Stealthed?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_STEALTHED ) ? "yes" : "no" );
  739. Msg("Invulnerable?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_INVULNERABLE ) ? "yes" : "no" );
  740. Msg("Teleported Recently?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_TELEPORTED ) ? "yes" : "no" );
  741. Msg("Selected for Teleportation?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_SELECTED_TO_TELEPORT ) ? "yes" : "no" );
  742. Msg("On Fire?: %s\n", TFPlayerHasCondition(conditionValue.Int(), TF_COND_BURNING ) ? "yes" : "no" );
  743. return PLUGIN_STOP;
  744. }
  745. else if ( FStrEq( pcmd, "sentry_status" ) )
  746. {
  747. SentryStatus(pEntity);
  748. return PLUGIN_STOP;
  749. }
  750. else if ( FStrEq( pcmd, "class_status" ) )
  751. {
  752. ClassStatus(pEntity);
  753. return PLUGIN_STOP;
  754. }
  755. else if ( FStrEq( pcmd, "flag_status" ) )
  756. {
  757. FlagStatus(pEntity);
  758. return PLUGIN_STOP;
  759. }
  760. #ifdef GAME_DLL
  761. else if ( FStrEq( pcmd, "cbe_test" ) )
  762. {
  763. IPlayerInfo *playerinfo = playerinfomanager->GetPlayerInfo( pEntity );
  764. if (!playerinfo)
  765. return PLUGIN_STOP;
  766. CBaseEntity *pEnt = static_cast< CBaseEntity* >(entityinfomanager->GetEntity( pEntity ));
  767. if (pEnt)
  768. Msg("got a pointer to CBaseEntity..\n");
  769. Msg("attempting to print this entities modelname directly..\n");
  770. Msg("ModelName: %s\n", STRING(pEnt->GetModelName()) );
  771. return PLUGIN_STOP;
  772. }
  773. #endif
  774. #endif
  775. return PLUGIN_CONTINUE;
  776. }
  777. //---------------------------------------------------------------------------------
  778. // Purpose: called when a client is authenticated
  779. //---------------------------------------------------------------------------------
  780. PLUGIN_RESULT CEmptyServerPlugin::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID )
  781. {
  782. return PLUGIN_CONTINUE;
  783. }
  784. //---------------------------------------------------------------------------------
  785. // Purpose: called when a cvar value query is finished
  786. //---------------------------------------------------------------------------------
  787. void CEmptyServerPlugin::OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
  788. {
  789. Msg( "Cvar query (cookie: %d, status: %d) - name: %s, value: %s\n", iCookie, eStatus, pCvarName, pCvarValue );
  790. }
  791. void CEmptyServerPlugin::OnEdictAllocated( edict_t *edict )
  792. {
  793. }
  794. void CEmptyServerPlugin::OnEdictFreed( const edict_t *edict )
  795. {
  796. }
  797. //---------------------------------------------------------------------------------
  798. // Purpose: called when an event is fired
  799. //---------------------------------------------------------------------------------
  800. void CEmptyServerPlugin::FireGameEvent( KeyValues * event )
  801. {
  802. const char * name = event->GetName();
  803. Msg( "CEmptyServerPlugin::FireGameEvent: Got event \"%s\"\n", name );
  804. }
  805. //---------------------------------------------------------------------------------
  806. // Purpose: an example of how to implement a new command
  807. //---------------------------------------------------------------------------------
  808. CON_COMMAND( empty_version, "prints the version of the empty plugin" )
  809. {
  810. Msg( "Version:2.0.0.0\n" );
  811. }
  812. CON_COMMAND( empty_log, "logs the version of the empty plugin" )
  813. {
  814. engine->LogPrint( "Version:2.0.0.0\n" );
  815. }
  816. //---------------------------------------------------------------------------------
  817. // Purpose: an example cvar
  818. //---------------------------------------------------------------------------------
  819. static ConVar empty_cvar("plugin_empty", "0", FCVAR_NOTIFY, "Example plugin cvar");