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.

757 lines
20 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "server_pch.h"
  9. #include "sv_plugin.h"
  10. #include "filesystem.h"
  11. #include "filesystem_engine.h"
  12. #include "eiface.h"
  13. #include "sys.h"
  14. #include "client.h"
  15. #include "sys_dll.h"
  16. #include "pr_edict.h"
  17. #include "networkstringtable.h"
  18. #include "networkstringtableserver.h"
  19. #include "tier0/etwprof.h"
  20. extern CreateInterfaceFn g_AppSystemFactory;
  21. extern CSysModule *g_GameDLL;
  22. CServerPlugin s_ServerPlugin;
  23. CServerPlugin *g_pServerPluginHandler = &s_ServerPlugin;
  24. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CServerPlugin, IServerPluginHelpers, INTERFACEVERSION_ISERVERPLUGINHELPERS, s_ServerPlugin );
  25. // Ascending value so they have a unique cookie to relate queries to the responses.
  26. static int g_iQueryCvarCookie = 1;
  27. QueryCvarCookie_t SendCvarValueQueryToClient( IClient *client, const char *pCvarName, bool bPluginQuery )
  28. {
  29. // Send a message to the client asking for the value.
  30. SVC_GetCvarValue msg;
  31. msg.m_iCookie = g_iQueryCvarCookie++;
  32. msg.m_szCvarName = pCvarName;
  33. // If the query came from the game DLL instead of from a plugin, then we negate the cookie
  34. // so it knows who to callback on when the value arrives back from the client.
  35. if ( !bPluginQuery )
  36. msg.m_iCookie = -msg.m_iCookie;
  37. client->SendNetMsg( msg );
  38. return msg.m_iCookie;
  39. }
  40. //---------------------------------------------------------------------------------
  41. // Purpose: constructor/destructor
  42. //---------------------------------------------------------------------------------
  43. CPlugin::CPlugin()
  44. {
  45. m_pPlugin = NULL;
  46. m_pPluginModule = NULL;
  47. m_bDisable = false;
  48. m_szName[0] = 0;
  49. }
  50. CPlugin::~CPlugin()
  51. {
  52. if ( m_pPlugin )
  53. {
  54. Unload();
  55. }
  56. m_pPlugin = NULL;
  57. if ( m_pPluginModule )
  58. {
  59. g_pFileSystem->UnloadModule( m_pPluginModule );
  60. }
  61. m_pPluginModule = NULL;
  62. }
  63. //---------------------------------------------------------------------------------
  64. // Purpose: loads and initializes a plugin
  65. //---------------------------------------------------------------------------------
  66. bool CPlugin::Load( const char *fileName )
  67. {
  68. if ( IsX360() )
  69. {
  70. return false;
  71. }
  72. char fixedFileName[ MAX_PATH ];
  73. Q_strncpy( fixedFileName, fileName, sizeof(fixedFileName) );
  74. Q_FixSlashes( fixedFileName );
  75. #if defined ( OSX ) || defined( LINUX )
  76. // Linux doesn't check signatures, so in that case disable plugins on the client completely unless -insecure is specified
  77. if ( !sv.IsDedicated() && Host_IsSecureServerAllowed() )
  78. return false;
  79. #endif
  80. // Only allow unsigned plugins in -insecure mode
  81. if ( !Host_AllowLoadModule( fixedFileName, "GAME", false ) )
  82. return false;
  83. m_pPluginModule = g_pFileSystem->LoadModule( fixedFileName, "GAME", false );
  84. if ( m_pPluginModule )
  85. {
  86. CreateInterfaceFn pluginFactory = Sys_GetFactory( m_pPluginModule );
  87. if ( pluginFactory )
  88. {
  89. m_iPluginInterfaceVersion = 3;
  90. m_bDisable = true;
  91. m_pPlugin = (IServerPluginCallbacks *)pluginFactory( INTERFACEVERSION_ISERVERPLUGINCALLBACKS, NULL );
  92. if ( !m_pPlugin )
  93. {
  94. m_iPluginInterfaceVersion = 2;
  95. m_pPlugin = (IServerPluginCallbacks *)pluginFactory( INTERFACEVERSION_ISERVERPLUGINCALLBACKS_VERSION_2, NULL );
  96. if ( !m_pPlugin )
  97. {
  98. m_iPluginInterfaceVersion = 1;
  99. m_pPlugin = (IServerPluginCallbacks *)pluginFactory( INTERFACEVERSION_ISERVERPLUGINCALLBACKS_VERSION_1, NULL );
  100. if ( !m_pPlugin )
  101. {
  102. Warning( "Could not get IServerPluginCallbacks interface from plugin \"%s\"", fileName );
  103. return false;
  104. }
  105. }
  106. }
  107. CreateInterfaceFn gameServerFactory = Sys_GetFactory( g_GameDLL );
  108. if ( !m_pPlugin->Load( g_AppSystemFactory, gameServerFactory ) )
  109. {
  110. Warning( "Failed to load plugin \"%s\"\n", fileName );
  111. return false;
  112. }
  113. SetName( m_pPlugin->GetPluginDescription() );
  114. m_bDisable = false;
  115. }
  116. }
  117. else
  118. {
  119. Warning( "Unable to load plugin \"%s\"\n", fileName );
  120. return false;
  121. }
  122. return true;
  123. }
  124. //---------------------------------------------------------------------------------
  125. // Purpose: unloads and cleans up a module
  126. //---------------------------------------------------------------------------------
  127. void CPlugin::Unload()
  128. {
  129. if ( m_pPlugin )
  130. {
  131. m_pPlugin->Unload();
  132. }
  133. m_pPlugin = NULL;
  134. m_bDisable = true;
  135. g_pFileSystem->UnloadModule( m_pPluginModule );
  136. m_pPluginModule = NULL;
  137. }
  138. //---------------------------------------------------------------------------------
  139. // Purpose: sets the name of the plugin
  140. //---------------------------------------------------------------------------------
  141. void CPlugin::SetName( const char *name )
  142. {
  143. Q_strncpy( m_szName, name, sizeof(m_szName) );
  144. }
  145. //---------------------------------------------------------------------------------
  146. // Purpose: returns the name of the plugin
  147. //---------------------------------------------------------------------------------
  148. const char *CPlugin::GetName()
  149. {
  150. return m_szName;
  151. }
  152. //---------------------------------------------------------------------------------
  153. // Purpose: returns the callback interface of a module
  154. //---------------------------------------------------------------------------------
  155. IServerPluginCallbacks *CPlugin::GetCallback()
  156. {
  157. Assert( m_pPlugin );
  158. if ( m_pPlugin )
  159. {
  160. return m_pPlugin;
  161. }
  162. else
  163. {
  164. Assert( !"Unable to get plugin callback interface" );
  165. Warning( "Unable to get callback interface for \"%s\"\n", GetName() );
  166. return NULL;
  167. }
  168. }
  169. //---------------------------------------------------------------------------------
  170. // Purpose: enables or disabled a plugin (i.e stops it running)
  171. //---------------------------------------------------------------------------------
  172. void CPlugin::Disable( bool state )
  173. {
  174. Assert( m_pPlugin );
  175. if ( state )
  176. {
  177. m_pPlugin->Pause();
  178. }
  179. else
  180. {
  181. m_pPlugin->UnPause();
  182. }
  183. m_bDisable = state;
  184. }
  185. //---------------------------------------------------------------------------------
  186. // Purpose: constructor/destructor
  187. //---------------------------------------------------------------------------------
  188. CServerPlugin::CServerPlugin()
  189. {
  190. m_PluginHelperCheck = NULL;
  191. }
  192. CServerPlugin::~CServerPlugin()
  193. {
  194. }
  195. //---------------------------------------------------------------------------------
  196. // Purpose: loads all plugins
  197. //---------------------------------------------------------------------------------
  198. void CServerPlugin::LoadPlugins()
  199. {
  200. if ( IsX360() )
  201. {
  202. return;
  203. }
  204. m_Plugins.PurgeAndDeleteElements();
  205. char const *findfn = Sys_FindFirst( "addons/*.vdf", NULL, 0 );
  206. while ( findfn )
  207. {
  208. DevMsg( "Plugins: found file \"%s\"\n", findfn );
  209. if ( !g_pFileSystem->FileExists( va("addons/%s", findfn), "MOD" ) ) // verify its in the mods directory
  210. {
  211. findfn = Sys_FindNext( NULL, 0 );
  212. continue;
  213. }
  214. KeyValues *pluginsFile = new KeyValues("Plugins");
  215. pluginsFile->LoadFromFile( g_pFileSystem, va("addons/%s", findfn), "MOD" );
  216. if ( pluginsFile->GetString("file", NULL) )
  217. {
  218. LoadPlugin(pluginsFile->GetString("file"));
  219. }
  220. pluginsFile->deleteThis();
  221. // move to next item
  222. findfn = Sys_FindNext( NULL, 0 );
  223. }
  224. Sys_FindClose();
  225. CreateInterfaceFn gameServerFactory = Sys_GetFactory( g_GameDLL );
  226. m_PluginHelperCheck = (IPluginHelpersCheck *)gameServerFactory( INTERFACEVERSION_PLUGINHELPERSCHECK, NULL );
  227. }
  228. //---------------------------------------------------------------------------------
  229. // Purpose: unloads all plugins
  230. //---------------------------------------------------------------------------------
  231. void CServerPlugin::UnloadPlugins()
  232. {
  233. for ( int i = m_Plugins.Count() - 1; i >= 0; --i )
  234. {
  235. m_Plugins[i]->Unload();
  236. m_Plugins.Remove(i);
  237. }
  238. }
  239. //---------------------------------------------------------------------------------
  240. // Purpose: unload a single plugin
  241. //---------------------------------------------------------------------------------
  242. bool CServerPlugin::UnloadPlugin( int index )
  243. {
  244. if ( m_Plugins.IsValidIndex( index ) )
  245. {
  246. m_Plugins[index]->Unload();
  247. m_Plugins.Remove(index);
  248. return true;
  249. }
  250. return false;
  251. }
  252. //---------------------------------------------------------------------------------
  253. // Purpose: loads a particular dll
  254. //---------------------------------------------------------------------------------
  255. bool CServerPlugin::LoadPlugin( const char *fileName )
  256. {
  257. CPlugin *plugin = new CPlugin();
  258. if ( plugin->Load( fileName ) )
  259. {
  260. m_Plugins.AddToTail( plugin );
  261. return true;
  262. }
  263. else
  264. {
  265. delete plugin;
  266. return false;
  267. }
  268. }
  269. //---------------------------------------------------------------------------------
  270. // Purpose: stop all plugins from running
  271. //---------------------------------------------------------------------------------
  272. void CServerPlugin::DisablePlugins()
  273. {
  274. for ( int i = 0; i < m_Plugins.Count(); i++ )
  275. {
  276. m_Plugins[i]->Disable(true);
  277. }
  278. }
  279. //---------------------------------------------------------------------------------
  280. // Purpose: turns all plugins back on
  281. //---------------------------------------------------------------------------------
  282. void CServerPlugin::EnablePlugins()
  283. {
  284. for ( int i = 0; i < m_Plugins.Count(); i++ )
  285. {
  286. m_Plugins[i]->Disable(false);
  287. }
  288. }
  289. //---------------------------------------------------------------------------------
  290. // Purpose: stops a single plugin
  291. //---------------------------------------------------------------------------------
  292. void CServerPlugin::DisablePlugin( int index )
  293. {
  294. if ( m_Plugins.IsValidIndex( index ) )
  295. {
  296. m_Plugins[index]->Disable(true);
  297. }
  298. }
  299. //---------------------------------------------------------------------------------
  300. // Purpose: stops a single plugin
  301. //---------------------------------------------------------------------------------
  302. void CServerPlugin::EnablePlugin( int index )
  303. {
  304. if ( m_Plugins.IsValidIndex( index ) )
  305. {
  306. m_Plugins[index]->Disable(false);
  307. }
  308. }
  309. //---------------------------------------------------------------------------------
  310. // Purpose: prints info about loaded plugins and their state
  311. //---------------------------------------------------------------------------------
  312. void CServerPlugin::PrintDetails()
  313. {
  314. ConMsg( "Loaded plugins:\n");
  315. ConMsg( "---------------------\n" );
  316. for ( int i = 0; i < m_Plugins.Count(); i++ )
  317. {
  318. ConMsg( "%i:\t\"%s\"%s\n", i, m_Plugins[i]->GetName(), m_Plugins[i]->IsDisabled() ? " (disabled)": "" );
  319. }
  320. ConMsg( "---------------------\n" );
  321. }
  322. // helper macro to stop this being typed for every passthrough
  323. #define FORALL_PLUGINS for( int i = 0; i < m_Plugins.Count(); i++ )
  324. #define CALL_PLUGIN_IF_ENABLED(call) \
  325. do { \
  326. CPlugin *plugin = m_Plugins[i]; \
  327. if ( ! plugin->IsDisabled() ) \
  328. { \
  329. IServerPluginCallbacks *callbacks = plugin->GetCallback(); \
  330. if ( callbacks ) \
  331. { \
  332. callbacks-> call ; \
  333. } \
  334. } \
  335. } while (0)
  336. extern CNetworkStringTableContainer *networkStringTableContainerServer;
  337. //---------------------------------------------------------------------------------
  338. // Purpose: pass through functions for the 3rd party API
  339. //---------------------------------------------------------------------------------
  340. void CServerPlugin::LevelInit( char const *pMapName,
  341. char const *pMapEntities, char const *pOldLevel,
  342. char const *pLandmarkName, bool loadGame, bool background )
  343. {
  344. CETWScope timer( "CServerPlugin::LevelInit" );
  345. MDLCACHE_COARSE_LOCK_(g_pMDLCache);
  346. FORALL_PLUGINS
  347. {
  348. CALL_PLUGIN_IF_ENABLED( LevelInit( pMapName ) );
  349. }
  350. bool bPrevState = networkStringTableContainerServer->Lock( false );
  351. serverGameDLL->LevelInit( pMapName, pMapEntities, pOldLevel, pLandmarkName, loadGame, background );
  352. networkStringTableContainerServer->Lock( bPrevState );
  353. }
  354. void CServerPlugin::ServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
  355. {
  356. MDLCACHE_COARSE_LOCK_(g_pMDLCache);
  357. FORALL_PLUGINS
  358. {
  359. CALL_PLUGIN_IF_ENABLED( ServerActivate( pEdictList, edictCount, clientMax ) );
  360. }
  361. serverGameDLL->ServerActivate( pEdictList, edictCount, clientMax );
  362. }
  363. void CServerPlugin::GameFrame( bool simulating )
  364. {
  365. FORALL_PLUGINS
  366. {
  367. CALL_PLUGIN_IF_ENABLED( GameFrame( simulating ) );
  368. }
  369. serverGameDLL->GameFrame( simulating );
  370. }
  371. void CServerPlugin::LevelShutdown( void )
  372. {
  373. MDLCACHE_COARSE_LOCK_(g_pMDLCache);
  374. FORALL_PLUGINS
  375. {
  376. CALL_PLUGIN_IF_ENABLED( LevelShutdown() );
  377. }
  378. serverGameDLL->LevelShutdown();
  379. }
  380. void CServerPlugin::ClientActive( edict_t *pEntity, bool bLoadGame )
  381. {
  382. FORALL_PLUGINS
  383. {
  384. CALL_PLUGIN_IF_ENABLED( ClientActive( pEntity ) );
  385. }
  386. serverGameClients->ClientActive( pEntity, bLoadGame );
  387. }
  388. void CServerPlugin::ClientDisconnect( edict_t *pEntity )
  389. {
  390. FORALL_PLUGINS
  391. {
  392. CALL_PLUGIN_IF_ENABLED( ClientDisconnect( pEntity ) );
  393. }
  394. serverGameClients->ClientDisconnect( pEntity );
  395. }
  396. void CServerPlugin::ClientPutInServer( edict_t *pEntity, char const *playername )
  397. {
  398. FORALL_PLUGINS
  399. {
  400. CALL_PLUGIN_IF_ENABLED( ClientPutInServer( pEntity, playername ) );
  401. }
  402. serverGameClients->ClientPutInServer( pEntity, playername );
  403. }
  404. void CServerPlugin::SetCommandClient( int index )
  405. {
  406. FORALL_PLUGINS
  407. {
  408. CALL_PLUGIN_IF_ENABLED( SetCommandClient( index ) );
  409. }
  410. serverGameClients->SetCommandClient( index );
  411. }
  412. void CServerPlugin::ClientSettingsChanged( edict_t *pEdict )
  413. {
  414. FORALL_PLUGINS
  415. {
  416. CALL_PLUGIN_IF_ENABLED( ClientSettingsChanged( pEdict ) );
  417. }
  418. serverGameClients->ClientSettingsChanged( pEdict );
  419. }
  420. bool CServerPlugin::ClientConnect( edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
  421. {
  422. PLUGIN_RESULT result = PLUGIN_CONTINUE;
  423. bool bAllowConnect = true, bSavedRetVal = true, bRetValOverridden = false;
  424. FORALL_PLUGINS
  425. {
  426. if ( ! m_Plugins[i]->IsDisabled() )
  427. {
  428. result = m_Plugins[i]->GetCallback()->ClientConnect( &bAllowConnect, pEntity, pszName, pszAddress, reject, maxrejectlen );
  429. if ( result == PLUGIN_STOP ) // stop executing right away
  430. {
  431. Assert( bAllowConnect == false );
  432. return bAllowConnect;
  433. }
  434. else if ( result == PLUGIN_OVERRIDE && bRetValOverridden == false ) // only the first PLUGIN_OVERRIDE return set the retval
  435. {
  436. bSavedRetVal = bAllowConnect;
  437. bRetValOverridden = true;
  438. }
  439. }
  440. }
  441. bAllowConnect = serverGameClients->ClientConnect( pEntity, pszName, pszAddress, reject, maxrejectlen );
  442. return bRetValOverridden ? bSavedRetVal : bAllowConnect;
  443. }
  444. void CServerPlugin::ClientCommand( edict_t *pEntity, const CCommand &args )
  445. {
  446. PLUGIN_RESULT result = PLUGIN_CONTINUE;
  447. FORALL_PLUGINS
  448. {
  449. if ( !m_Plugins[i]->IsDisabled() )
  450. {
  451. result = m_Plugins[i]->GetCallback()->ClientCommand( pEntity, args );
  452. if ( result == PLUGIN_STOP ) // stop executing right away
  453. return;
  454. }
  455. }
  456. serverGameClients->ClientCommand( pEntity, args );
  457. }
  458. QueryCvarCookie_t CServerPlugin::StartQueryCvarValue( edict_t *pEntity, const char *pCvarName )
  459. {
  460. // Figure out which client they're talking about.
  461. int clientnum = NUM_FOR_EDICT( pEntity );
  462. if (clientnum < 1 || clientnum > sv.GetClientCount() )
  463. {
  464. Warning( "StartQueryCvarValue: Invalid entity\n" );
  465. return InvalidQueryCvarCookie;
  466. }
  467. IClient *client = sv.Client( clientnum-1 );
  468. return SendCvarValueQueryToClient( client, pCvarName, true );
  469. }
  470. void CServerPlugin::NetworkIDValidated( const char *pszUserName, const char *pszNetworkID )
  471. {
  472. PLUGIN_RESULT result = PLUGIN_CONTINUE;
  473. FORALL_PLUGINS
  474. {
  475. if ( ! m_Plugins[i]->IsDisabled() )
  476. {
  477. result = m_Plugins[i]->GetCallback()->NetworkIDValidated( pszUserName, pszNetworkID );
  478. if ( result == PLUGIN_STOP ) // stop executing right away
  479. {
  480. return;
  481. }
  482. }
  483. }
  484. }
  485. void CServerPlugin::OnQueryCvarValueFinished( QueryCvarCookie_t iCookie, edict_t *pPlayerEntity, EQueryCvarValueStatus eStatus, const char *pCvarName, const char *pCvarValue )
  486. {
  487. FORALL_PLUGINS
  488. {
  489. CPlugin *p = m_Plugins[i];
  490. if ( !p->IsDisabled() )
  491. {
  492. // OnQueryCvarValueFinished was added in version 2 of this interface.
  493. if ( p->GetPluginInterfaceVersion() >= 2 )
  494. {
  495. p->GetCallback()->OnQueryCvarValueFinished( iCookie, pPlayerEntity, eStatus, pCvarName, pCvarValue );
  496. }
  497. }
  498. }
  499. }
  500. void CServerPlugin::OnEdictAllocated( edict_t *edict )
  501. {
  502. FORALL_PLUGINS
  503. {
  504. CPlugin *p = m_Plugins[i];
  505. if ( !p->IsDisabled() )
  506. {
  507. // OnEdictAllocated was added in version 3 of this interface.
  508. if ( p->GetPluginInterfaceVersion() >= 3 )
  509. {
  510. p->GetCallback()->OnEdictAllocated( edict );
  511. }
  512. }
  513. }
  514. }
  515. void CServerPlugin::OnEdictFreed( const edict_t *edict )
  516. {
  517. FORALL_PLUGINS
  518. {
  519. CPlugin *p = m_Plugins[i];
  520. if ( !p->IsDisabled() )
  521. {
  522. // OnEdictFreed was added in version 3 of this interface.
  523. if ( p->GetPluginInterfaceVersion() >= 3 )
  524. {
  525. p->GetCallback()->OnEdictFreed( edict );
  526. }
  527. }
  528. }
  529. }
  530. //---------------------------------------------------------------------------------
  531. // Purpose: creates a VGUI menu on a clients screen
  532. //---------------------------------------------------------------------------------
  533. void CServerPlugin::CreateMessage( edict_t *pEntity, DIALOG_TYPE type, KeyValues *data, IServerPluginCallbacks *plugin )
  534. {
  535. if ( !pEntity )
  536. {
  537. ConMsg( "Invaid pEntity\n" );
  538. return;
  539. }
  540. if ( !data )
  541. {
  542. ConMsg( "No data keyvalues provided\n" );
  543. return;
  544. }
  545. if ( !plugin )
  546. {
  547. ConMsg( "No plugin provided\n" );
  548. return;
  549. }
  550. if ( m_PluginHelperCheck && !m_PluginHelperCheck->CreateMessage( plugin->GetPluginDescription(), pEntity, type, data ) )
  551. {
  552. ConMsg( "Disallowed by game dll\n" );
  553. return;
  554. }
  555. int clientnum = NUM_FOR_EDICT( pEntity );
  556. if (clientnum < 1 || clientnum > sv.GetClientCount() )
  557. {
  558. ConMsg( "Invalid entity\n" );
  559. return;
  560. }
  561. IClient *client = sv.Client(clientnum-1);
  562. SVC_Menu menu( type, data );
  563. client->SendNetMsg( menu );
  564. }
  565. void CServerPlugin::ClientCommand( edict_t *pEntity, const char *cmd )
  566. {
  567. int entnum = NUM_FOR_EDICT( pEntity );
  568. if ( ( entnum < 1 ) || ( entnum > sv.GetClientCount() ) )
  569. {
  570. Msg("\n!!!\nCServerPlugin::ClientCommand: Some entity tried to stuff '%s' to console buffer of entity %i when maxclients was set to %i, ignoring\n\n",
  571. cmd, entnum, sv.GetMaxClients() );
  572. return;
  573. }
  574. sv.GetClient(entnum-1)->ExecuteStringCommand( cmd );
  575. }
  576. //---------------------------------------------------------------------------------
  577. //
  578. //
  579. // Purpose: client commands for plugin functions
  580. //
  581. //
  582. //---------------------------------------------------------------------------------
  583. CON_COMMAND( plugin_print, "Prints details about loaded plugins" )
  584. {
  585. g_pServerPluginHandler->PrintDetails();
  586. }
  587. CON_COMMAND( plugin_pause, "plugin_pause <index> : pauses a loaded plugin" )
  588. {
  589. if ( args.ArgC() < 2 )
  590. {
  591. Warning( "Syntax: plugin_pause <index>\n" );
  592. }
  593. else
  594. {
  595. g_pServerPluginHandler->DisablePlugin( atoi(args[2]) );
  596. ConMsg( "Plugin disabled\n" );
  597. }
  598. }
  599. CON_COMMAND( plugin_unpause, "plugin_unpause <index> : unpauses a disabled plugin" )
  600. {
  601. if ( args.ArgC() < 2 )
  602. {
  603. Warning( "Syntax: plugin_unpause <index>\n" );
  604. }
  605. else
  606. {
  607. g_pServerPluginHandler->EnablePlugin( atoi(args[2]) );
  608. ConMsg( "Plugin enabled\n" );
  609. }
  610. }
  611. CON_COMMAND( plugin_pause_all, "pauses all loaded plugins" )
  612. {
  613. g_pServerPluginHandler->DisablePlugins();
  614. ConMsg( "Plugins disabled\n" );
  615. }
  616. CON_COMMAND( plugin_unpause_all, "unpauses all disabled plugins" )
  617. {
  618. g_pServerPluginHandler->EnablePlugins();
  619. ConMsg( "Plugins enabled\n" );
  620. }
  621. CON_COMMAND( plugin_load, "plugin_load <filename> : loads a plugin" )
  622. {
  623. if ( sv.IsDedicated() && sv.IsActive() )
  624. {
  625. ConMsg( "plugin_load : cannot load a plugin while running a map\n" );
  626. }
  627. else if ( !sv.IsDedicated() && cl.IsConnected() )
  628. {
  629. ConMsg( "plugin_load : cannot load a plugin while connected to a server\n" );
  630. }
  631. else
  632. {
  633. if ( args.ArgC() < 2 )
  634. {
  635. Warning( "plugin_load <filename>\n" );
  636. }
  637. else
  638. {
  639. if ( !g_pServerPluginHandler->LoadPlugin( args[1] ) )
  640. {
  641. Warning( "Unable to load plugin \"%s\"\n", args[1] );
  642. return;
  643. }
  644. ConMsg( "Loaded plugin \"%s\"\n", args[1] );
  645. }
  646. }
  647. }
  648. CON_COMMAND( plugin_unload, "plugin_unload <index> : unloads a plugin" )
  649. {
  650. if ( args.ArgC() < 2 )
  651. {
  652. Warning( "plugin_unload <index>\n" );
  653. }
  654. else
  655. {
  656. if ( !g_pServerPluginHandler->UnloadPlugin( atoi(args[1]) ) )
  657. {
  658. Warning( "Unable to unload plugin \"%s\", not found\n", args[1] );
  659. return;
  660. }
  661. ConMsg( "Unloaded plugin \"%s\"\n", args[1] );
  662. }
  663. }