PageRenderTime 326ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/ghost/game.cpp

http://ghostcb.googlecode.com/
C++ | 2268 lines | 1737 code | 299 blank | 232 comment | 594 complexity | 9d4e701e4a6df370f0491d91f9837c3a MD5 | raw file
  1. /*
  2. Copyright [2008] [Trevor Hogan]
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/
  13. */
  14. #include "ghost.h"
  15. #include "util.h"
  16. #include "config.h"
  17. #include "language.h"
  18. #include "socket.h"
  19. #include "ghostdb.h"
  20. #include "bnet.h"
  21. #include "map.h"
  22. #include "packed.h"
  23. #include "savegame.h"
  24. #include "gameplayer.h"
  25. #include "gameprotocol.h"
  26. #include "game_base.h"
  27. #include "game.h"
  28. #include "stats.h"
  29. #include "statsdota.h"
  30. #include "statsw3mmd.h"
  31. #include "ui/forward.h"
  32. #include <cmath>
  33. #include <string.h>
  34. #include <time.h>
  35. //
  36. // sorting classes
  37. //
  38. class CGamePlayerSortAscByPing
  39. {
  40. public:
  41. bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
  42. {
  43. return Player1->GetPing( false ) < Player2->GetPing( false );
  44. }
  45. };
  46. class CGamePlayerSortDescByPing
  47. {
  48. public:
  49. bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
  50. {
  51. return Player1->GetPing( false ) > Player2->GetPing( false );
  52. }
  53. };
  54. //
  55. // CGame
  56. //
  57. CGame :: CGame( CGHost *nGHost, CMap *nMap, CSaveGame *nSaveGame, uint16_t nHostPort, unsigned char nGameState, string nGameName, string nOwnerName, string nCreatorName, string nCreatorServer ) : CBaseGame( nGHost, nMap, nSaveGame, nHostPort, nGameState, nGameName, nOwnerName, nCreatorName, nCreatorServer ), m_DBBanLast( NULL ), m_CallableGameAdd( NULL )
  58. {
  59. m_DBGame = new CDBGame( 0, string( ), m_Map->GetMapPath( ), string( ), string( ), string( ), 0 );
  60. if( m_Map->GetMapType( ) == "w3mmd" )
  61. m_Stats = new CStatsW3MMD( this, m_Map->GetMapStatsW3MMDCategory( ) );
  62. else if( m_Map->GetMapType( ) == "dota" )
  63. m_Stats = new CStatsDOTA( this );
  64. else
  65. m_Stats = NULL;
  66. }
  67. CGame :: ~CGame( )
  68. {
  69. if( m_CallableGameAdd && m_CallableGameAdd->GetReady( ) )
  70. {
  71. if( m_CallableGameAdd->GetResult( ) > 0 )
  72. {
  73. CONSOLE_Print( "[GAME: " + m_GameName + "] saving player/stats data to database" );
  74. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Saving player/stats data to database", 0, 0));
  75. // store the CDBGamePlayers in the database
  76. for( vector<CDBGamePlayer *> :: iterator i = m_DBGamePlayers.begin( ); i != m_DBGamePlayers.end( ); ++i )
  77. m_GHost->m_Callables.push_back( m_GHost->m_DB->ThreadedGamePlayerAdd( m_CallableGameAdd->GetResult( ), (*i)->GetName( ), (*i)->GetIP( ), (*i)->GetSpoofed( ), (*i)->GetSpoofedRealm( ), (*i)->GetReserved( ), (*i)->GetLoadingTime( ), (*i)->GetLeft( ), (*i)->GetLeftReason( ), (*i)->GetTeam( ), (*i)->GetColour( ) ) );
  78. // store the stats in the database
  79. if( m_Stats )
  80. m_Stats->Save( m_GHost, m_GHost->m_DB, m_CallableGameAdd->GetResult( ) );
  81. }
  82. else
  83. {
  84. CONSOLE_Print( "[GAME: " + m_GameName + "] unable to save player/stats data to database" );
  85. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Unable to save player/stats data to database", 0, 0));
  86. }
  87. m_GHost->m_DB->RecoverCallable( m_CallableGameAdd );
  88. delete m_CallableGameAdd;
  89. m_CallableGameAdd = NULL;
  90. }
  91. for( vector<PairedBanCheck> :: iterator i = m_PairedBanChecks.begin( ); i != m_PairedBanChecks.end( ); ++i )
  92. m_GHost->m_Callables.push_back( i->second );
  93. for( vector<PairedBanAdd> :: iterator i = m_PairedBanAdds.begin( ); i != m_PairedBanAdds.end( ); ++i )
  94. m_GHost->m_Callables.push_back( i->second );
  95. for( vector<PairedGPSCheck> :: iterator i = m_PairedGPSChecks.begin( ); i != m_PairedGPSChecks.end( ); ++i )
  96. m_GHost->m_Callables.push_back( i->second );
  97. for( vector<PairedDPSCheck> :: iterator i = m_PairedDPSChecks.begin( ); i != m_PairedDPSChecks.end( ); ++i )
  98. m_GHost->m_Callables.push_back( i->second );
  99. for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); ++i )
  100. delete *i;
  101. delete m_DBGame;
  102. for( vector<CDBGamePlayer *> :: iterator i = m_DBGamePlayers.begin( ); i != m_DBGamePlayers.end( ); ++i )
  103. delete *i;
  104. delete m_Stats;
  105. // it's a "bad thing" if m_CallableGameAdd is non NULL here
  106. // it means the game is being deleted after m_CallableGameAdd was created (the first step to saving the game data) but before the associated thread terminated
  107. // rather than failing horribly we choose to allow the thread to complete in the orphaned callables list but step 2 will never be completed
  108. // so this will create a game entry in the database without any gameplayers and/or DotA stats
  109. if( m_CallableGameAdd )
  110. {
  111. CONSOLE_Print( "[GAME: " + m_GameName + "] game is being deleted before all game data was saved, game data has been lost" );
  112. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Game is being deleted before all game data was saved, game data has been lost", 0, 0));
  113. m_GHost->m_Callables.push_back( m_CallableGameAdd );
  114. }
  115. }
  116. bool CGame :: Update( void *fd, void *send_fd )
  117. {
  118. // update callables
  119. for( vector<PairedBanCheck> :: iterator i = m_PairedBanChecks.begin( ); i != m_PairedBanChecks.end( ); )
  120. {
  121. if( i->second->GetReady( ) )
  122. {
  123. CDBBan *Ban = i->second->GetResult( );
  124. if( Ban )
  125. SendAllChat( m_GHost->m_Language->UserWasBannedOnByBecause( i->second->GetServer( ), i->second->GetUser( ), Ban->GetDate( ), Ban->GetAdmin( ), Ban->GetReason( ) ) );
  126. else
  127. SendAllChat( m_GHost->m_Language->UserIsNotBanned( i->second->GetServer( ), i->second->GetUser( ) ) );
  128. m_GHost->m_DB->RecoverCallable( i->second );
  129. delete i->second;
  130. i = m_PairedBanChecks.erase( i );
  131. }
  132. else
  133. ++i;
  134. }
  135. for( vector<PairedBanAdd> :: iterator i = m_PairedBanAdds.begin( ); i != m_PairedBanAdds.end( ); )
  136. {
  137. if( i->second->GetReady( ) )
  138. {
  139. if( i->second->GetResult( ) )
  140. {
  141. string alias = i->second->GetServer( );
  142. for( vector<CBNET *> :: iterator j = m_GHost->m_BNETs.begin( ); j != m_GHost->m_BNETs.end( ); ++j )
  143. {
  144. if( (*j)->GetServer( ) == i->second->GetServer( ) )
  145. {
  146. (*j)->AddBan( i->second->GetUser( ), i->second->GetIP( ), i->second->GetGameName( ), i->second->GetAdmin( ), i->second->GetReason( ) );
  147. alias = (*j)->GetServerAlias();
  148. }
  149. }
  150. SendAllChat( m_GHost->m_Language->PlayerWasBannedByPlayer( alias, i->second->GetUser( ), i->first, i->second->GetIP( ), i->second->GetGameName( ), i->second->GetAdmin( ) ) );
  151. SendAllChat( i->second->GetReason( ) );
  152. }
  153. m_GHost->m_DB->RecoverCallable( i->second );
  154. delete i->second;
  155. i = m_PairedBanAdds.erase( i );
  156. }
  157. else
  158. ++i;
  159. }
  160. for( vector<PairedGPSCheck> :: iterator i = m_PairedGPSChecks.begin( ); i != m_PairedGPSChecks.end( ); )
  161. {
  162. if( i->second->GetReady( ) )
  163. {
  164. CDBGamePlayerSummary *GamePlayerSummary = i->second->GetResult( );
  165. if( GamePlayerSummary )
  166. {
  167. if( i->first.empty( ) )
  168. SendAllChat( m_GHost->m_Language->HasPlayedGamesWithThisBot( i->second->GetName( ), GamePlayerSummary->GetFirstGameDateTime( ), GamePlayerSummary->GetLastGameDateTime( ), UTIL_ToString( GamePlayerSummary->GetTotalGames( ) ), UTIL_ToString( (float)GamePlayerSummary->GetAvgLoadingTime( ) / 1000, 2 ), UTIL_ToString( GamePlayerSummary->GetAvgLeftPercent( ) ) ) );
  169. else
  170. {
  171. CGamePlayer *Player = GetPlayerFromName( i->first, true );
  172. if( Player )
  173. SendChat( Player, m_GHost->m_Language->HasPlayedGamesWithThisBot( i->second->GetName( ), GamePlayerSummary->GetFirstGameDateTime( ), GamePlayerSummary->GetLastGameDateTime( ), UTIL_ToString( GamePlayerSummary->GetTotalGames( ) ), UTIL_ToString( (float)GamePlayerSummary->GetAvgLoadingTime( ) / 1000, 2 ), UTIL_ToString( GamePlayerSummary->GetAvgLeftPercent( ) ) ) );
  174. }
  175. }
  176. else
  177. {
  178. if( i->first.empty( ) )
  179. SendAllChat( m_GHost->m_Language->HasntPlayedGamesWithThisBot( i->second->GetName( ) ) );
  180. else
  181. {
  182. CGamePlayer *Player = GetPlayerFromName( i->first, true );
  183. if( Player )
  184. SendChat( Player, m_GHost->m_Language->HasntPlayedGamesWithThisBot( i->second->GetName( ) ) );
  185. }
  186. }
  187. m_GHost->m_DB->RecoverCallable( i->second );
  188. delete i->second;
  189. i = m_PairedGPSChecks.erase( i );
  190. }
  191. else
  192. ++i;
  193. }
  194. for( vector<PairedDPSCheck> :: iterator i = m_PairedDPSChecks.begin( ); i != m_PairedDPSChecks.end( ); )
  195. {
  196. if( i->second->GetReady( ) )
  197. {
  198. CDBDotAPlayerSummary *DotAPlayerSummary = i->second->GetResult( );
  199. if( DotAPlayerSummary )
  200. {
  201. string Summary = m_GHost->m_Language->HasPlayedDotAGamesWithThisBot( i->second->GetName( ),
  202. UTIL_ToString( DotAPlayerSummary->GetTotalGames( ) ),
  203. UTIL_ToString( DotAPlayerSummary->GetTotalWins( ) ),
  204. UTIL_ToString( DotAPlayerSummary->GetTotalLosses( ) ),
  205. UTIL_ToString( DotAPlayerSummary->GetTotalKills( ) ),
  206. UTIL_ToString( DotAPlayerSummary->GetTotalDeaths( ) ),
  207. UTIL_ToString( DotAPlayerSummary->GetTotalCreepKills( ) ),
  208. UTIL_ToString( DotAPlayerSummary->GetTotalCreepDenies( ) ),
  209. UTIL_ToString( DotAPlayerSummary->GetTotalAssists( ) ),
  210. UTIL_ToString( DotAPlayerSummary->GetTotalNeutralKills( ) ),
  211. UTIL_ToString( DotAPlayerSummary->GetTotalTowerKills( ) ),
  212. UTIL_ToString( DotAPlayerSummary->GetTotalRaxKills( ) ),
  213. UTIL_ToString( DotAPlayerSummary->GetTotalCourierKills( ) ),
  214. UTIL_ToString( DotAPlayerSummary->GetAvgKills( ), 2 ),
  215. UTIL_ToString( DotAPlayerSummary->GetAvgDeaths( ), 2 ),
  216. UTIL_ToString( DotAPlayerSummary->GetAvgCreepKills( ), 2 ),
  217. UTIL_ToString( DotAPlayerSummary->GetAvgCreepDenies( ), 2 ),
  218. UTIL_ToString( DotAPlayerSummary->GetAvgAssists( ), 2 ),
  219. UTIL_ToString( DotAPlayerSummary->GetAvgNeutralKills( ), 2 ),
  220. UTIL_ToString( DotAPlayerSummary->GetAvgTowerKills( ), 2 ),
  221. UTIL_ToString( DotAPlayerSummary->GetAvgRaxKills( ), 2 ),
  222. UTIL_ToString( DotAPlayerSummary->GetAvgCourierKills( ), 2 ) );
  223. if( i->first.empty( ) )
  224. SendAllChat( Summary );
  225. else
  226. {
  227. CGamePlayer *Player = GetPlayerFromName( i->first, true );
  228. if( Player )
  229. SendChat( Player, Summary );
  230. }
  231. }
  232. else
  233. {
  234. if( i->first.empty( ) )
  235. SendAllChat( m_GHost->m_Language->HasntPlayedDotAGamesWithThisBot( i->second->GetName( ) ) );
  236. else
  237. {
  238. CGamePlayer *Player = GetPlayerFromName( i->first, true );
  239. if( Player )
  240. SendChat( Player, m_GHost->m_Language->HasntPlayedDotAGamesWithThisBot( i->second->GetName( ) ) );
  241. }
  242. }
  243. m_GHost->m_DB->RecoverCallable( i->second );
  244. delete i->second;
  245. i = m_PairedDPSChecks.erase( i );
  246. }
  247. else
  248. ++i;
  249. }
  250. return CBaseGame :: Update( fd, send_fd );
  251. }
  252. // this function is only called when a player leave packet is received, not when there's a socket error, kick, etc...
  253. void CGame :: EventPlayerLeft( CGamePlayer *player, uint32_t reason )
  254. {
  255. // Check if leaver is admin/root admin with a loop then set the bool accordingly.
  256. bool isAdmin = false;
  257. for( vector<CBNET *> :: iterator j = m_GHost->m_BNETs.begin( ); j != m_GHost->m_BNETs.end( ); ++j )
  258. {
  259. if( (*j)->IsAdmin(player->GetName( ) ) || (*j)->IsRootAdmin( player->GetName( )) )
  260. {
  261. isAdmin = true;
  262. break;
  263. }
  264. }
  265. CBaseGame :: EventPlayerLeft( player, reason );
  266. }
  267. void CGame :: EventPlayerDeleted( CGamePlayer *player )
  268. {
  269. CBaseGame :: EventPlayerDeleted( player );
  270. // record everything we need to know about the player for storing in the database later
  271. // since we haven't stored the game yet (it's not over yet!) we can't link the gameplayer to the game
  272. // see the destructor for where these CDBGamePlayers are stored in the database
  273. // we could have inserted an incomplete record on creation and updated it later but this makes for a cleaner interface
  274. if( m_GameLoading || m_GameLoaded )
  275. {
  276. // todotodo: since we store players that crash during loading it's possible that the stats classes could have no information on them
  277. // that could result in a DBGamePlayer without a corresponding DBDotAPlayer - just be aware of the possibility
  278. unsigned char SID = GetSIDFromPID( player->GetPID( ) );
  279. unsigned char Team = 255;
  280. unsigned char Colour = 255;
  281. if( SID < m_Slots.size( ) )
  282. {
  283. Team = m_Slots[SID].GetTeam( );
  284. Colour = m_Slots[SID].GetColour( );
  285. }
  286. m_DBGamePlayers.push_back( new CDBGamePlayer( 0, 0, player->GetName( ), player->GetExternalIPString( ), player->GetSpoofed( ) ? 1 : 0, player->GetSpoofedRealm( ), player->GetReserved( ) ? 1 : 0, player->GetFinishedLoading( ) ? player->GetFinishedLoadingTicks( ) - m_StartedLoadingTicks : 0, m_GameTicks / 1000, player->GetLeftReason( ), Team, Colour ) );
  287. // also keep track of the last player to leave for the !banlast command
  288. for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); ++i )
  289. {
  290. if( (*i)->GetName( ) == player->GetName( ) )
  291. m_DBBanLast = *i;
  292. }
  293. }
  294. }
  295. void CGame :: EventPlayerAction( CGamePlayer *player, CIncomingAction *action )
  296. {
  297. CBaseGame :: EventPlayerAction( player, action );
  298. // give the stats class a chance to process the action
  299. if( m_Stats && m_Stats->ProcessAction( action ) && m_GameOverTime == 0 )
  300. {
  301. CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (stats class reported game over)" );
  302. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Gameover timer started (stats class reported game over)", 0, 0));
  303. SendEndMessage( );
  304. m_GameOverTime = GetTime( );
  305. }
  306. }
  307. bool CGame :: EventPlayerBotCommand( CGamePlayer *player, string command, string payload )
  308. {
  309. bool HideCommand = CBaseGame :: EventPlayerBotCommand( player, command, payload );
  310. // todotodo: don't be lazy
  311. string User = player->GetName( );
  312. string Command = command;
  313. string Payload = payload;
  314. bool AdminCheck = false;
  315. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  316. {
  317. if( (*i)->GetServer( ) == player->GetSpoofedRealm( ) && (*i)->IsAdmin( User ) )
  318. {
  319. AdminCheck = true;
  320. break;
  321. }
  322. //GHost++ Custom Build Addition start
  323. else if( ( UTIL_IsLanIP( player->GetExternalIP( ) ) || UTIL_IsLocalIP( player->GetExternalIP( ), m_GHost->m_LocalAddresses ) ) && m_GHost->m_LANAdmins != 0 )
  324. {
  325. if ( m_GHost->m_LANAdmins == 1 || m_GHost->m_LANAdmins == 3 )
  326. if ( ( m_GHost->m_GetLANRootAdmins == 1 && (*i)->IsLANRootAdmin( User ) ) || m_GHost->m_GetLANRootAdmins != 1 )
  327. AdminCheck = true;
  328. break;
  329. }
  330. //GHost++ Custom Build Addition end
  331. }
  332. bool RootAdminCheck = false;
  333. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  334. {
  335. if( (*i)->GetServer( ) == player->GetSpoofedRealm( ) && (*i)->IsRootAdmin( User ) )
  336. {
  337. RootAdminCheck = true;
  338. break;
  339. }
  340. //GHost++ Custom Build Addition start
  341. else if( ( UTIL_IsLanIP( player->GetExternalIP( ) ) || UTIL_IsLocalIP( player->GetExternalIP( ), m_GHost->m_LocalAddresses ) ) && m_GHost->m_LANAdmins != 0 )
  342. {
  343. if ( m_GHost->m_LANAdmins == 2 )
  344. if ( ( m_GHost->m_GetLANRootAdmins == 1 && (*i)->IsLANRootAdmin( User ) ) || m_GHost->m_GetLANRootAdmins != 1 )
  345. RootAdminCheck = true;
  346. if ( m_GHost->m_LANAdmins == 3 )
  347. if ( ( m_GHost->m_GetLANRootAdmins == 1 && (*i)->IsLANRootAdmin( User ) ) )
  348. RootAdminCheck = true;
  349. break;
  350. }
  351. //GHost++ Custom Build Addition end
  352. }
  353. if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
  354. {
  355. CONSOLE_Print( "[GAME: " + m_GameName + "] admin [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]" );
  356. //forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Admin [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]", 0, 0));
  357. if( !m_Locked || RootAdminCheck || IsOwner( User ) )
  358. {
  359. /*****************
  360. * ADMIN COMMANDS *
  361. ******************/
  362. //
  363. // !ABORT (abort countdown)
  364. // !A
  365. //
  366. // we use "!a" as an alias for abort because you don't have much time to abort the countdown so it's useful for the abort command to be easy to type
  367. if( ( Command == "abort" || Command == "a" ) && m_CountDownStarted && !m_GameLoading && !m_GameLoaded )
  368. {
  369. if ( m_GHost->m_HideCommands )
  370. HideCommand = true;
  371. SendAllChat( m_GHost->m_Language->CountDownAborted( ) );
  372. m_CountDownStarted = false;
  373. m_AutoStartPlayers = 0;
  374. m_UsingStart = false;
  375. }
  376. //
  377. // !ADDBAN
  378. // !BAN
  379. //
  380. else if( ( Command == "addban" || Command == "ban" ) && !Payload.empty( ) && !m_GHost->m_BNETs.empty( ) )
  381. {
  382. if ( m_GHost->m_HideCommands )
  383. HideCommand = true;
  384. // extract the victim and the reason
  385. // e.g. "Varlock leaver after dying" -> victim: "Varlock", reason: "leaver after dying"
  386. string Victim;
  387. string Reason;
  388. stringstream SS;
  389. SS << Payload;
  390. SS >> Victim;
  391. if( !SS.eof( ) )
  392. {
  393. getline( SS, Reason );
  394. string :: size_type Start = Reason.find_first_not_of( " " );
  395. if( Start != string :: npos )
  396. Reason = Reason.substr( Start );
  397. }
  398. if( m_GameLoaded )
  399. {
  400. string VictimLower = Victim;
  401. transform( VictimLower.begin( ), VictimLower.end( ), VictimLower.begin( ), (int(*)(int))tolower );
  402. uint32_t Matches = 0;
  403. CDBBan *LastMatch = NULL;
  404. // try to match each player with the passed string (e.g. "Varlock" would be matched with "lock")
  405. // we use the m_DBBans vector for this in case the player already left and thus isn't in the m_Players vector anymore
  406. for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); ++i )
  407. {
  408. string TestName = (*i)->GetName( );
  409. transform( TestName.begin( ), TestName.end( ), TestName.begin( ), (int(*)(int))tolower );
  410. if( TestName.find( VictimLower ) != string :: npos )
  411. {
  412. Matches++;
  413. LastMatch = *i;
  414. // if the name matches exactly stop any further matching
  415. if( TestName == VictimLower )
  416. {
  417. Matches = 1;
  418. break;
  419. }
  420. }
  421. }
  422. if( Matches == 0 )
  423. SendAllChat( m_GHost->m_Language->UnableToBanNoMatchesFound( Victim ) );
  424. else if( Matches == 1 )
  425. {
  426. // calculate timestamp
  427. uint32_t timestamp = m_GameTicks - m_GameStartTime;
  428. string MinString = UTIL_ToString( ( timestamp / 1000 ) / 60 );
  429. string SecString = UTIL_ToString( ( timestamp / 1000 ) % 60 );
  430. if( MinString.size( ) == 1 )
  431. MinString.insert( 0, "0" );
  432. if( SecString.size( ) == 1 )
  433. SecString.insert( 0, "0" );
  434. Reason = "[Reason: " + MinString + ":" + SecString + "] " + Reason;
  435. m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( LastMatch->GetServer( ), LastMatch->GetName( ), LastMatch->GetIP( ), m_GameName, User, Reason ) ) );
  436. }
  437. else
  438. SendAllChat( m_GHost->m_Language->UnableToBanFoundMoreThanOneMatch( Victim ) );
  439. }
  440. else
  441. {
  442. CGamePlayer *LastMatch = NULL;
  443. uint32_t Matches = GetPlayerFromNamePartial( Victim, &LastMatch );
  444. if( Matches == 0 )
  445. SendAllChat( m_GHost->m_Language->UnableToBanNoMatchesFound( Victim ) );
  446. else if( Matches == 1 )
  447. m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( LastMatch->GetJoinedRealm( ), LastMatch->GetName( ), LastMatch->GetExternalIPString( ), m_GameName, User, Reason ) ) );
  448. else
  449. SendAllChat( m_GHost->m_Language->UnableToBanFoundMoreThanOneMatch( Victim ) );
  450. }
  451. }
  452. //
  453. // !ANNOUNCE
  454. //
  455. else if( Command == "announce" && !m_CountDownStarted )
  456. {
  457. if ( m_GHost->m_HideCommands )
  458. HideCommand = true;
  459. if( Payload.empty( ) || Payload == "off" )
  460. {
  461. SendAllChat( m_GHost->m_Language->AnnounceMessageDisabled( ) );
  462. SetAnnounce( 0, string( ) );
  463. }
  464. else
  465. {
  466. // extract the interval and the message
  467. // e.g. "30 hello everyone" -> interval: "30", message: "hello everyone"
  468. uint32_t Interval;
  469. string Message;
  470. stringstream SS;
  471. SS << Payload;
  472. SS >> Interval;
  473. if( SS.fail( ) || Interval == 0 )
  474. {
  475. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to announce command" );
  476. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to announce command", 0, 0));
  477. }
  478. else
  479. {
  480. if( SS.eof( ) )
  481. {
  482. CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to announce command" );
  483. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to announce command", 0, 0));
  484. }
  485. else
  486. {
  487. getline( SS, Message );
  488. string :: size_type Start = Message.find_first_not_of( " " );
  489. if( Start != string :: npos )
  490. Message = Message.substr( Start );
  491. SendAllChat( m_GHost->m_Language->AnnounceMessageEnabled( ) );
  492. SetAnnounce( Interval, Message );
  493. }
  494. }
  495. }
  496. }
  497. //
  498. // !AUTOSAVE
  499. //
  500. else if( Command == "autosave" )
  501. {
  502. if ( m_GHost->m_HideCommands )
  503. HideCommand = true;
  504. if( Payload == "on" )
  505. {
  506. SendAllChat( m_GHost->m_Language->AutoSaveEnabled( ) );
  507. m_AutoSave = true;
  508. }
  509. else if( Payload == "off" )
  510. {
  511. SendAllChat( m_GHost->m_Language->AutoSaveDisabled( ) );
  512. m_AutoSave = false;
  513. }
  514. }
  515. //
  516. // !AUTOSTART
  517. //
  518. else if( Command == "autostart" && !m_CountDownStarted )
  519. {
  520. if ( m_GHost->m_HideCommands )
  521. HideCommand = true;
  522. if( Payload.empty( ) || Payload == "off" )
  523. {
  524. SendAllChat( m_GHost->m_Language->AutoStartDisabled( ) );
  525. m_AutoStartPlayers = 0;
  526. if ( m_UsingStart == true )
  527. m_UsingStart = false;
  528. }
  529. else
  530. {
  531. uint32_t AutoStartPlayers = UTIL_ToUInt32( Payload );
  532. if( AutoStartPlayers != 0 )
  533. {
  534. SendAllChat( m_GHost->m_Language->AutoStartEnabled( UTIL_ToString( AutoStartPlayers ) ) );
  535. m_AutoStartPlayers = AutoStartPlayers;
  536. }
  537. }
  538. }
  539. //
  540. // !BANLAST
  541. //
  542. else if( Command == "banlast" && m_GameLoaded && !m_GHost->m_BNETs.empty( ) && m_DBBanLast )
  543. {
  544. if ( m_GHost->m_HideCommands )
  545. HideCommand = true;
  546. // calculate timestamp
  547. uint32_t timestamp = m_GameTicks - m_GameStartTime;
  548. string MinString = UTIL_ToString( ( timestamp / 1000 ) / 60 );
  549. string SecString = UTIL_ToString( ( timestamp / 1000 ) % 60 );
  550. if( MinString.size( ) == 1 )
  551. MinString.insert( 0, "0" );
  552. if( SecString.size( ) == 1 )
  553. SecString.insert( 0, "0" );
  554. string Reason = "[Reason: " + MinString + ":" + SecString + "] " + Payload;
  555. m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( m_DBBanLast->GetServer( ), m_DBBanLast->GetName( ), m_DBBanLast->GetIP( ), m_GameName, User, Reason ) ) );
  556. }
  557. //
  558. // !CHECK
  559. //
  560. else if( Command == "check" )
  561. {
  562. if ( m_GHost->m_HideCommands )
  563. HideCommand = true;
  564. if( !Payload.empty( ) )
  565. {
  566. CGamePlayer *LastMatch = NULL;
  567. uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
  568. if( Matches == 0 )
  569. SendAllChat( m_GHost->m_Language->UnableToCheckPlayerNoMatchesFound( Payload ) );
  570. else if( Matches == 1 )
  571. {
  572. bool LastMatchAdminCheck = false;
  573. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  574. {
  575. if( (*i)->GetServer( ) == LastMatch->GetSpoofedRealm( ) && (*i)->IsAdmin( LastMatch->GetName( ) ) )
  576. {
  577. LastMatchAdminCheck = true;
  578. break;
  579. }
  580. }
  581. bool LastMatchRootAdminCheck = false;
  582. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  583. {
  584. if( (*i)->GetServer( ) == LastMatch->GetSpoofedRealm( ) && (*i)->IsRootAdmin( LastMatch->GetName( ) ) )
  585. {
  586. LastMatchRootAdminCheck = true;
  587. break;
  588. }
  589. }
  590. SendAllChat( m_GHost->m_Language->CheckedPlayer( LastMatch->GetName( ), LastMatch->GetNumPings( ) > 0 ? UTIL_ToString( LastMatch->GetPing( m_GHost->m_LCPings ) ) + "ms" : "N/A", m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( LastMatch->GetExternalIP( ), true ) ), LastMatchAdminCheck || LastMatchRootAdminCheck ? "Yes" : "No", IsOwner( LastMatch->GetName( ) ) ? "Yes" : "No", LastMatch->GetSpoofed( ) ? "Yes" : "No", LastMatch->GetSpoofedRealm( ).empty( ) ? "N/A" : LastMatch->GetSpoofedRealm( ), LastMatch->GetReserved( ) ? "Yes" : "No" ) );
  591. }
  592. else
  593. SendAllChat( m_GHost->m_Language->UnableToCheckPlayerFoundMoreThanOneMatch( Payload ) );
  594. }
  595. else
  596. SendAllChat( m_GHost->m_Language->CheckedPlayer( User, player->GetNumPings( ) > 0 ? UTIL_ToString( player->GetPing( m_GHost->m_LCPings ) ) + "ms" : "N/A", m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( player->GetExternalIP( ), true ) ), AdminCheck || RootAdminCheck ? "Yes" : "No", IsOwner( User ) ? "Yes" : "No", player->GetSpoofed( ) ? "Yes" : "No", player->GetSpoofedRealm( ).empty( ) ? "N/A" : player->GetSpoofedRealm( ), player->GetReserved( ) ? "Yes" : "No" ) );
  597. }
  598. //
  599. // !CHECKBAN
  600. //
  601. else if( Command == "checkban" && !Payload.empty( ) && !m_GHost->m_BNETs.empty( ) )
  602. {
  603. if ( m_GHost->m_HideCommands )
  604. HideCommand = true;
  605. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  606. m_PairedBanChecks.push_back( PairedBanCheck( User, m_GHost->m_DB->ThreadedBanCheck( (*i)->GetServer( ), Payload, string( ) ) ) );
  607. }
  608. //
  609. // !CLEARHCL
  610. //
  611. else if( Command == "clearhcl" && !m_CountDownStarted )
  612. {
  613. if ( m_GHost->m_HideCommands )
  614. HideCommand = true;
  615. m_HCLCommandString.clear( );
  616. SendAllChat( m_GHost->m_Language->ClearingHCL( ) );
  617. }
  618. //
  619. // !CLOSE (close slot)
  620. //
  621. else if( Command == "close" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
  622. {
  623. if ( m_GHost->m_HideCommands )
  624. HideCommand = true;
  625. // close as many slots as specified, e.g. "5 10" closes slots 5 and 10
  626. stringstream SS;
  627. SS << Payload;
  628. while( !SS.eof( ) )
  629. {
  630. uint32_t SID;
  631. SS >> SID;
  632. if( SS.fail( ) )
  633. {
  634. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to close command" );
  635. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input to close command", 0, 0));
  636. break;
  637. }
  638. else
  639. CloseSlot( (unsigned char)( SID - 1 ), true );
  640. }
  641. }
  642. //
  643. // !CLOSEALL
  644. //
  645. else if( Command == "closeall" && !m_GameLoading && !m_GameLoaded )
  646. {
  647. if ( m_GHost->m_HideCommands )
  648. HideCommand = true;
  649. CloseAllSlots( );
  650. }
  651. //
  652. // !COMP (computer slot)
  653. //
  654. else if( Command == "comp" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
  655. {
  656. if ( m_GHost->m_HideCommands )
  657. HideCommand = true;
  658. // extract the slot and the skill
  659. // e.g. "1 2" -> slot: "1", skill: "2"
  660. uint32_t Slot;
  661. uint32_t Skill = 1;
  662. stringstream SS;
  663. SS << Payload;
  664. SS >> Slot;
  665. if( SS.fail( ) )
  666. {
  667. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comp command" );
  668. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to comp command", 0, 0));
  669. }
  670. else
  671. {
  672. if( !SS.eof( ) )
  673. SS >> Skill;
  674. if( SS.fail( ) )
  675. {
  676. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to comp command" );
  677. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #2 to comp command", 0, 0));
  678. }
  679. else
  680. ComputerSlot( (unsigned char)( Slot - 1 ), (unsigned char)Skill, true );
  681. }
  682. }
  683. //
  684. // !COMPCOLOUR (computer colour change)
  685. //
  686. else if( Command == "compcolour" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
  687. {
  688. if ( m_GHost->m_HideCommands )
  689. HideCommand = true;
  690. // extract the slot and the colour
  691. // e.g. "1 2" -> slot: "1", colour: "2"
  692. uint32_t Slot;
  693. uint32_t Colour;
  694. stringstream SS;
  695. SS << Payload;
  696. SS >> Slot;
  697. if( SS.fail( ) )
  698. {
  699. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to compcolour command" );
  700. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to compcolour command", 0, 0));
  701. }
  702. else
  703. {
  704. if( SS.eof( ) )
  705. {
  706. CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to compcolour command" );
  707. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to compcolour command", 0, 0));
  708. }
  709. else
  710. {
  711. SS >> Colour;
  712. if( SS.fail( ) )
  713. {
  714. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to compcolour command" );
  715. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #2 to compcolour command", 0, 0));
  716. }
  717. else
  718. {
  719. unsigned char SID = (unsigned char)( Slot - 1 );
  720. if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && Colour < 12 && SID < m_Slots.size( ) )
  721. {
  722. if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
  723. ColourSlot( SID, Colour );
  724. }
  725. }
  726. }
  727. }
  728. }
  729. //
  730. // !COMPHANDICAP (computer handicap change)
  731. //
  732. else if( Command == "comphandicap" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
  733. {
  734. if ( m_GHost->m_HideCommands )
  735. HideCommand = true;
  736. // extract the slot and the handicap
  737. // e.g. "1 50" -> slot: "1", handicap: "50"
  738. uint32_t Slot;
  739. uint32_t Handicap;
  740. stringstream SS;
  741. SS << Payload;
  742. SS >> Slot;
  743. if( SS.fail( ) )
  744. {
  745. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comphandicap command" );
  746. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to comphandicap command", 0, 0));
  747. }
  748. else
  749. {
  750. if( SS.eof( ) )
  751. {
  752. CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to comphandicap command" );
  753. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to comphandicap command", 0, 0));
  754. }
  755. else
  756. {
  757. SS >> Handicap;
  758. if( SS.fail( ) )
  759. {
  760. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to comphandicap command" );
  761. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #2 to comphandicap command", 0, 0));
  762. }
  763. else
  764. {
  765. unsigned char SID = (unsigned char)( Slot - 1 );
  766. if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && ( Handicap == 50 || Handicap == 60 || Handicap == 70 || Handicap == 80 || Handicap == 90 || Handicap == 100 ) && SID < m_Slots.size( ) )
  767. {
  768. if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
  769. {
  770. m_Slots[SID].SetHandicap( (unsigned char)Handicap );
  771. SendAllSlotInfo( );
  772. }
  773. }
  774. }
  775. }
  776. }
  777. }
  778. //
  779. // !COMPRACE (computer race change)
  780. //
  781. else if( Command == "comprace" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
  782. {
  783. if ( m_GHost->m_HideCommands )
  784. HideCommand = true;
  785. // extract the slot and the race
  786. // e.g. "1 human" -> slot: "1", race: "human"
  787. uint32_t Slot;
  788. string Race;
  789. stringstream SS;
  790. SS << Payload;
  791. SS >> Slot;
  792. if( SS.fail( ) )
  793. {
  794. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comprace command" );
  795. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to comprace command", 0, 0));
  796. }
  797. else
  798. {
  799. if( SS.eof( ) )
  800. {
  801. CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to comprace command" );
  802. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to comprace command", 0, 0));
  803. }
  804. else
  805. {
  806. getline( SS, Race );
  807. string :: size_type Start = Race.find_first_not_of( " " );
  808. if( Start != string :: npos )
  809. Race = Race.substr( Start );
  810. transform( Race.begin( ), Race.end( ), Race.begin( ), (int(*)(int))tolower );
  811. unsigned char SID = (unsigned char)( Slot - 1 );
  812. if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && !( m_Map->GetMapFlags( ) & MAPFLAG_RANDOMRACES ) && SID < m_Slots.size( ) )
  813. {
  814. if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
  815. {
  816. if( Race == "human" )
  817. {
  818. m_Slots[SID].SetRace( SLOTRACE_HUMAN | SLOTRACE_SELECTABLE );
  819. SendAllSlotInfo( );
  820. }
  821. else if( Race == "orc" )
  822. {
  823. m_Slots[SID].SetRace( SLOTRACE_ORC | SLOTRACE_SELECTABLE );
  824. SendAllSlotInfo( );
  825. }
  826. else if( Race == "night elf" )
  827. {
  828. m_Slots[SID].SetRace( SLOTRACE_NIGHTELF | SLOTRACE_SELECTABLE );
  829. SendAllSlotInfo( );
  830. }
  831. else if( Race == "undead" )
  832. {
  833. m_Slots[SID].SetRace( SLOTRACE_UNDEAD | SLOTRACE_SELECTABLE );
  834. SendAllSlotInfo( );
  835. }
  836. else if( Race == "random" )
  837. {
  838. m_Slots[SID].SetRace( SLOTRACE_RANDOM | SLOTRACE_SELECTABLE );
  839. SendAllSlotInfo( );
  840. }
  841. else
  842. {
  843. CONSOLE_Print( "[GAME: " + m_GameName + "] unknown race [" + Race + "] sent to comprace command" );
  844. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Unknown race [" + Race + "] sent to comprace command", 0, 0));
  845. }
  846. }
  847. }
  848. }
  849. }
  850. }
  851. //
  852. // !COMPTEAM (computer team change)
  853. //
  854. else if( Command == "compteam" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
  855. {
  856. if ( m_GHost->m_HideCommands )
  857. HideCommand = true;
  858. // extract the slot and the team
  859. // e.g. "1 2" -> slot: "1", team: "2"
  860. uint32_t Slot;
  861. uint32_t Team;
  862. stringstream SS;
  863. SS << Payload;
  864. SS >> Slot;
  865. if( SS.fail( ) )
  866. {
  867. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to compteam command" );
  868. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to compteam command", 0, 0));
  869. }
  870. else
  871. {
  872. if( SS.eof( ) )
  873. {
  874. CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to compteam command" );
  875. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to compteam command", 0, 0));
  876. }
  877. else
  878. {
  879. SS >> Team;
  880. if( SS.fail( ) )
  881. {
  882. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to compteam command" );
  883. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #2 to compteam command", 0, 0));
  884. }
  885. else
  886. {
  887. unsigned char SID = (unsigned char)( Slot - 1 );
  888. if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && Team < 12 && SID < m_Slots.size( ) )
  889. {
  890. if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
  891. {
  892. m_Slots[SID].SetTeam( (unsigned char)( Team - 1 ) );
  893. SendAllSlotInfo( );
  894. }
  895. }
  896. }
  897. }
  898. }
  899. }
  900. //
  901. // !DBSTATUS
  902. //
  903. else if( Command == "dbstatus" )
  904. {
  905. if ( m_GHost->m_HideCommands )
  906. HideCommand = true;
  907. SendAllChat( m_GHost->m_DB->GetStatus( ) );
  908. }
  909. //
  910. // !DOWNLOAD
  911. // !DL
  912. //
  913. else if( ( Command == "download" || Command == "dl" ) && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
  914. {
  915. if ( m_GHost->m_HideCommands )
  916. HideCommand = true;
  917. CGamePlayer *LastMatch = NULL;
  918. uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
  919. if( Matches == 0 )
  920. SendAllChat( m_GHost->m_Language->UnableToStartDownloadNoMatchesFound( Payload ) );
  921. else if( Matches == 1 )
  922. {
  923. if( !LastMatch->GetDownloadStarted( ) && !LastMatch->GetDownloadFinished( ) )
  924. {
  925. unsigned char SID = GetSIDFromPID( LastMatch->GetPID( ) );
  926. if( SID < m_Slots.size( ) && m_Slots[SID].GetDownloadStatus( ) != 100 )
  927. {
  928. // inform the client that we are willing to send the map
  929. CONSOLE_Print( "[GAME: " + m_GameName + "] map download started for player [" + LastMatch->GetName( ) + "]" );
  930. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Map download started for player [" + LastMatch->GetName( ) + "]", 0, 0));
  931. Send( LastMatch, m_Protocol->SEND_W3GS_STARTDOWNLOAD( GetHostPID( ) ) );
  932. LastMatch->SetDownloadAllowed( true );
  933. LastMatch->SetDownloadStarted( true );
  934. LastMatch->SetStartedDownloadingTicks( GetTicks( ) );
  935. }
  936. }
  937. }
  938. else
  939. SendAllChat( m_GHost->m_Language->UnableToStartDownloadFoundMoreThanOneMatch( Payload ) );
  940. }
  941. //
  942. // !DROP
  943. //
  944. else if( Command == "drop" && m_GameLoaded )
  945. {
  946. if ( m_GHost->m_HideCommands )
  947. HideCommand = true;
  948. StopLaggers( "lagged out (dropped by admin)" );
  949. }
  950. //
  951. // !END
  952. //
  953. else if( Command == "end" && m_GameLoaded )
  954. {
  955. if ( m_GHost->m_HideCommands )
  956. HideCommand = true;
  957. CONSOLE_Print( "[GAME: " + m_GameName + "] is over (admin ended game)" );
  958. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Game is over (admin ended game)", 0, 0));
  959. StopPlayers( "was disconnected (admin ended game)" );
  960. }
  961. //
  962. // !FAKEPLAYER
  963. //
  964. else if( Command == "fakeplayer" && !m_CountDownStarted )
  965. {
  966. if ( m_GHost->m_HideCommands )
  967. HideCommand = true;
  968. if( m_FakePlayerPID == 255 )
  969. CreateFakePlayer( );
  970. else
  971. DeleteFakePlayer( );
  972. }
  973. //
  974. // !FPPAUSE
  975. //
  976. else if( Command == "fppause" && m_FakePlayerPID != 255 && m_GameLoaded )
  977. {
  978. if ( m_GHost->m_HideCommands )
  979. HideCommand = true;
  980. BYTEARRAY CRC;
  981. BYTEARRAY Action;
  982. Action.push_back( 1 );
  983. m_Actions.push( new CIncomingAction( m_FakePlayerPID, CRC, Action ) );
  984. }
  985. //
  986. // !FPRESUME
  987. //
  988. else if( Command == "fpresume" && m_FakePlayerPID != 255 && m_GameLoaded )
  989. {
  990. if ( m_GHost->m_HideCommands )
  991. HideCommand = true;
  992. BYTEARRAY CRC;
  993. BYTEARRAY Action;
  994. Action.push_back( 2 );
  995. m_Actions.push( new CIncomingAction( m_FakePlayerPID, CRC, Action ) );
  996. }
  997. //
  998. // !FROM
  999. //
  1000. else if( Command == "from" )
  1001. {
  1002. if ( m_GHost->m_HideCommands )
  1003. HideCommand = true;
  1004. string Froms;
  1005. if (!Payload.empty())
  1006. {
  1007. CGamePlayer *LastMatch = NULL;
  1008. uint32_t Matches = GetPlayerFromNamePartial( Payload , &LastMatch );
  1009. if( Matches == 0 )
  1010. CONSOLE_Print("No matches");
  1011. else if( Matches == 1 )
  1012. {
  1013. Froms = LastMatch->GetName( );
  1014. Froms += ": (";
  1015. Froms += m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( (LastMatch)->GetExternalIP( ), true ) );
  1016. Froms += ")";
  1017. SendAllChat(Froms);
  1018. }
  1019. else
  1020. CONSOLE_Print("Found more than one match");
  1021. }
  1022. if (Payload.empty())
  1023. {
  1024. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  1025. {
  1026. // we reverse the byte order on the IP because it's stored in network byte order
  1027. Froms += (*i)->GetNameTerminated( );
  1028. Froms += ": (";
  1029. Froms += m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( (*i)->GetExternalIP( ), true ) );
  1030. Froms += ")";
  1031. if( i != m_Players.end( ) - 1 )
  1032. Froms += ", ";
  1033. else
  1034. SendAllChat( Froms );
  1035. }
  1036. }
  1037. }
  1038. //
  1039. // !HCL
  1040. //
  1041. else if( Command == "hcl" && !m_CountDownStarted )
  1042. {
  1043. if ( m_GHost->m_HideCommands )
  1044. HideCommand = true;
  1045. if( !Payload.empty( ) )
  1046. {
  1047. if( Payload.size( ) <= m_Slots.size( ) )
  1048. {
  1049. string HCLChars = "abcdefghijklmnopqrstuvwxyz0123456789 -=,.";
  1050. if( Payload.find_first_not_of( HCLChars ) == string :: npos )
  1051. {
  1052. m_HCLCommandString = Payload;
  1053. SendAllChat( m_GHost->m_Language->SettingHCL( m_HCLCommandString ) );
  1054. vector<string> MapData;
  1055. MapData.push_back( "HCL" ); MapData.push_back( m_HCLCommandString );
  1056. forward( new CFwdData( FWD_GAME_MAP_INFO_UPDATE, MapData, m_GameID ) );
  1057. }
  1058. else
  1059. SendAllChat( m_GHost->m_Language->UnableToSetHCLInvalid( ) );
  1060. }
  1061. else
  1062. SendAllChat( m_GHost->m_Language->UnableToSetHCLTooLong( ) );
  1063. }
  1064. else
  1065. SendAllChat( m_GHost->m_Language->TheHCLIs( m_HCLCommandString ) );
  1066. }
  1067. //
  1068. // !HOLD (hold a slot for someone)
  1069. //
  1070. else if( Command == "hold" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
  1071. {
  1072. if ( m_GHost->m_HideCommands )
  1073. HideCommand = true;
  1074. // hold as many players as specified, e.g. "Varlock Kilranin" holds players "Varlock" and "Kilranin"
  1075. stringstream SS;
  1076. SS << Payload;
  1077. while( !SS.eof( ) )
  1078. {
  1079. string HoldName;
  1080. SS >> HoldName;
  1081. if( SS.fail( ) )
  1082. {
  1083. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to hold command" );
  1084. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input to hold command", 0, 0));
  1085. break;
  1086. }
  1087. else
  1088. {
  1089. SendAllChat( m_GHost->m_Language->AddedPlayerToTheHoldList( HoldName ) );
  1090. AddToReserved( HoldName );
  1091. }
  1092. }
  1093. }
  1094. //
  1095. // !KICK (kick a player)
  1096. //
  1097. else if( Command == "kick" && !Payload.empty( ) )
  1098. {
  1099. if ( m_GHost->m_HideCommands )
  1100. HideCommand = true;
  1101. CGamePlayer *LastMatch = NULL;
  1102. uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
  1103. if( Matches == 0 )
  1104. SendAllChat( m_GHost->m_Language->UnableToKickNoMatchesFound( Payload ) );
  1105. else if( Matches == 1 )
  1106. {
  1107. LastMatch->SetDeleteMe( true );
  1108. LastMatch->SetLeftReason( m_GHost->m_Language->WasKickedByPlayer( User ) );
  1109. if( !m_GameLoading && !m_GameLoaded )
  1110. LastMatch->SetLeftCode( PLAYERLEAVE_LOBBY );
  1111. else
  1112. LastMatch->SetLeftCode( PLAYERLEAVE_LOST );
  1113. if( !m_GameLoading && !m_GameLoaded )
  1114. OpenSlot( GetSIDFromPID( LastMatch->GetPID( ) ), false );
  1115. }
  1116. else
  1117. SendAllChat( m_GHost->m_Language->UnableToKickFoundMoreThanOneMatch( Payload ) );
  1118. }
  1119. //
  1120. // !LATENCY (set game latency)
  1121. //
  1122. else if( Command == "latency" )
  1123. {
  1124. if ( m_GHost->m_HideCommands )
  1125. HideCommand = true;
  1126. if( Payload.empty( ) )
  1127. SendAllChat( m_GHost->m_Language->LatencyIs( UTIL_ToString( m_Latency ) ) );
  1128. else
  1129. {
  1130. m_Latency = UTIL_ToUInt32( Payload );
  1131. if( m_Latency <= 20 )
  1132. {
  1133. m_Latency = 20;
  1134. SendAllChat( m_GHost->m_Language->SettingLatencyToMinimum( "20" ) );
  1135. }
  1136. else if( m_Latency >= 500 )
  1137. {
  1138. m_Latency = 500;
  1139. SendAllChat( m_GHost->m_Language->SettingLatencyToMaximum( "500" ) );
  1140. }
  1141. else
  1142. SendAllChat( m_GHost->m_Language->SettingLatencyTo( UTIL_ToString( m_Latency ) ) );
  1143. }
  1144. }
  1145. //
  1146. // !LOCK
  1147. //
  1148. else if( Command == "lock" && ( RootAdminCheck || IsOwner( User ) ) )
  1149. {
  1150. if ( m_GHost->m_HideCommands )
  1151. HideCommand = true;
  1152. SendAllChat( m_GHost->m_Language->GameLocked( ) );
  1153. m_Locked = true;
  1154. }
  1155. //
  1156. // !MESSAGES
  1157. //
  1158. else if( Command == "messages" )
  1159. {
  1160. if ( m_GHost->m_HideCommands )
  1161. HideCommand = true;
  1162. if( Payload == "on" )
  1163. {
  1164. SendAllChat( m_GHost->m_Language->LocalAdminMessagesEnabled( ) );
  1165. m_LocalAdminMessages = true;
  1166. }
  1167. else if( Payload == "off" )
  1168. {
  1169. SendAllChat( m_GHost->m_Language->LocalAdminMessagesDisabled( ) );
  1170. m_LocalAdminMessages = false;
  1171. }
  1172. }
  1173. //
  1174. // !MUTE
  1175. //
  1176. else if( Command == "mute" )
  1177. {
  1178. if ( m_GHost->m_HideCommands )
  1179. HideCommand = true;
  1180. CGamePlayer *LastMatch = NULL;
  1181. uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
  1182. if( Matches == 0 )
  1183. SendAllChat( m_GHost->m_Language->UnableToMuteNoMatchesFound( Payload ) );
  1184. else if( Matches == 1 )
  1185. {
  1186. SendAllChat( m_GHost->m_Language->MutedPlayer( LastMatch->GetName( ), User ) );
  1187. LastMatch->SetMuted( true );
  1188. }
  1189. else
  1190. SendAllChat( m_GHost->m_Language->UnableToMuteFoundMoreThanOneMatch( Payload ) );
  1191. }
  1192. //
  1193. // !MUTEALL
  1194. //
  1195. else if( Command == "muteall" && m_GameLoaded )
  1196. {
  1197. if ( m_GHost->m_HideCommands )
  1198. HideCommand = true;
  1199. SendAllChat( m_GHost->m_Language->GlobalChatMuted( ) );
  1200. m_MuteAll = true;
  1201. }
  1202. //
  1203. // !OPEN (open slot)
  1204. //
  1205. else if( Command == "open" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
  1206. {
  1207. if ( m_GHost->m_HideCommands )
  1208. HideCommand = true;
  1209. // open as many slots as specified, e.g. "5 10" opens slots 5 and 10
  1210. stringstream SS;
  1211. SS << Payload;
  1212. while( !SS.eof( ) )
  1213. {
  1214. uint32_t SID;
  1215. SS >> SID;
  1216. if( SS.fail( ) )
  1217. {
  1218. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to open command" );
  1219. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input to open command", 0, 0));
  1220. break;
  1221. }
  1222. else
  1223. OpenSlot( (unsigned char)( SID - 1 ), true );
  1224. }
  1225. }
  1226. //
  1227. // !OPENALL
  1228. //
  1229. else if( Command == "openall" && !m_GameLoading && !m_GameLoaded )
  1230. {
  1231. if ( m_GHost->m_HideCommands )
  1232. HideCommand = true;
  1233. OpenAllSlots( );
  1234. }
  1235. //
  1236. // !OWNER (set game owner)
  1237. //
  1238. else if( Command == "owner" )
  1239. {
  1240. if( RootAdminCheck || IsOwner( User ) || !GetPlayerFromName( m_OwnerName, false ) )
  1241. {
  1242. if ( m_GHost->m_HideCommands )
  1243. HideCommand = true;
  1244. if( !Payload.empty( ) )
  1245. {
  1246. SendAllChat( m_GHost->m_Language->SettingGameOwnerTo( Payload ) );
  1247. m_OwnerName = Payload;
  1248. }
  1249. else
  1250. {
  1251. SendAllChat( m_GHost->m_Language->SettingGameOwnerTo( User ) );
  1252. m_OwnerName = User;
  1253. }
  1254. }
  1255. else
  1256. SendAllChat( m_GHost->m_Language->UnableToSetGameOwner( m_OwnerName ) );
  1257. }
  1258. //
  1259. // !PING
  1260. //
  1261. else if( Command == "ping" )
  1262. {
  1263. if ( m_GHost->m_HideCommands )
  1264. HideCommand = true;
  1265. // kick players with ping higher than payload if payload isn't empty
  1266. // we only do this if the game hasn't started since we don't want to kick players from a game in progress
  1267. uint32_t Kicked = 0;
  1268. uint32_t KickPing = 0;
  1269. string Pings;
  1270. uint32_t Matches = 0;
  1271. if (!Payload.empty())
  1272. {
  1273. CGamePlayer *LastMatch = NULL;
  1274. Matches = GetPlayerFromNamePartial( Payload , &LastMatch );
  1275. if( Matches == 0 )
  1276. CONSOLE_Print("No matches");
  1277. else if( Matches == 1 )
  1278. {
  1279. Pings = LastMatch->GetName( );
  1280. Pings +=": ";
  1281. if( LastMatch->GetNumPings( ) > 0 )
  1282. {
  1283. Pings += UTIL_ToString( LastMatch->GetPing( m_GHost->m_LCPings ) );
  1284. Pings +=" ms";
  1285. } else
  1286. Pings += "N/A";
  1287. SendAllChat(Pings);
  1288. }
  1289. else
  1290. CONSOLE_Print("Found more than one match");
  1291. }
  1292. if( !m_GameLoading && !m_GameLoaded && !Payload.empty( ) )
  1293. KickPing = UTIL_ToUInt32( Payload );
  1294. if (Payload.empty() || Matches == 0 )
  1295. {
  1296. // copy the m_Players vector so we can sort by descending ping so it's easier to find players with high pings
  1297. vector<CGamePlayer *> SortedPlayers = m_Players;
  1298. sort( SortedPlayers.begin( ), SortedPlayers.end( ), CGamePlayerSortDescByPing( ) );
  1299. for( vector<CGamePlayer *> :: iterator i = SortedPlayers.begin( ); i != SortedPlayers.end( ); ++i )
  1300. {
  1301. Pings += (*i)->GetNameTerminated( );
  1302. Pings += ": ";
  1303. if( (*i)->GetNumPings( ) > 0 )
  1304. {
  1305. Pings += UTIL_ToString( (*i)->GetPing( m_GHost->m_LCPings ) );
  1306. if( !m_GameLoading && !m_GameLoaded && !(*i)->GetReserved( ) && KickPing > 0 && (*i)->GetPing( m_GHost->m_LCPings ) > KickPing )
  1307. {
  1308. (*i)->SetDeleteMe( true );
  1309. (*i)->SetLeftReason( "was kicked for excessive ping " + UTIL_ToString( (*i)->GetPing( m_GHost->m_LCPings ) ) + " > " + UTIL_ToString( KickPing ) );
  1310. (*i)->SetLeftCode( PLAYERLEAVE_LOBBY );
  1311. OpenSlot( GetSIDFromPID( (*i)->GetPID( ) ), false );
  1312. Kicked++;
  1313. }
  1314. Pings += "ms";
  1315. }
  1316. else
  1317. Pings += "N/A";
  1318. if( i != SortedPlayers.end( ) - 1 )
  1319. Pings += ", ";
  1320. }
  1321. SendAllChat( Pings );
  1322. if( Kicked > 0 )
  1323. SendAllChat( m_GHost->m_Language->KickingPlayersWithPingsGreaterThan( UTIL_ToString( Kicked ), UTIL_ToString( KickPing ) ) );
  1324. }
  1325. }
  1326. //
  1327. // !PRIV (rehost as private game)
  1328. //
  1329. else if( Command == "priv" && !Payload.empty( ) && !m_CountDownStarted && !m_SaveGame )
  1330. {
  1331. if ( m_GHost->m_HideCommands )
  1332. HideCommand = true;
  1333. if( Payload.length() < 31 )
  1334. {
  1335. CONSOLE_Print( "[GAME: " + m_GameName + "] trying to rehost as private game [" + Payload + "]" );
  1336. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Trying to rehost as private game [" + Payload + "]", 0, 0));
  1337. SendAllChat( m_GHost->m_Language->TryingToRehostAsPrivateGame( Payload ) );
  1338. m_GameState = GAME_PRIVATE;
  1339. m_LastGameName = m_GameName;
  1340. m_GameName = Payload;
  1341. m_HostCounter = m_GHost->m_HostCounter++;
  1342. m_RefreshError = false;
  1343. m_RefreshRehosted = true;
  1344. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  1345. {
  1346. // unqueue any existing game refreshes because we're going to assume the next successful game refresh indicates that the rehost worked
  1347. // this ignores the fact that it's possible a game refresh was just sent and no response has been received yet
  1348. // we assume this won't happen very often since the only downside is a potential false positive
  1349. (*i)->UnqueueGameRefreshes( );
  1350. (*i)->QueueGameUncreate( );
  1351. (*i)->QueueEnterChat( );
  1352. // we need to send the game creation message now because private games are not refreshed
  1353. (*i)->QueueGameCreate( m_GameState, m_GameName, string( ), m_Map, NULL, m_HostCounter );
  1354. if( (*i)->GetPasswordHashType( ) != "pvpgn" )
  1355. (*i)->QueueEnterChat( );
  1356. }
  1357. m_CreationTime = GetTime( );
  1358. m_LastRefreshTime = GetTime( );
  1359. }
  1360. else
  1361. SendAllChat( m_GHost->m_Language->UnableToCreateGameNameTooLong( Payload ) );
  1362. // auto set HCL if map_defaulthcl is not empty
  1363. AutoSetHCL();
  1364. }
  1365. //
  1366. // !PUB (rehost as public game)
  1367. //
  1368. else if( Command == "pub" && !Payload.empty( ) && !m_CountDownStarted && !m_SaveGame )
  1369. {
  1370. if ( m_GHost->m_HideCommands )
  1371. HideCommand = true;
  1372. if( Payload.length() < 31 )
  1373. {
  1374. CONSOLE_Print( "[GAME: " + m_GameName + "] trying to rehost as public game [" + Payload + "]" );
  1375. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Trying to rehost as public game [" + Payload + "]", 0, 0));
  1376. SendAllChat( m_GHost->m_Language->TryingToRehostAsPublicGame( Payload ) );
  1377. m_GameState = GAME_PUBLIC;
  1378. m_LastGameName = m_GameName;
  1379. m_GameName = Payload;
  1380. m_HostCounter = m_GHost->m_HostCounter++;
  1381. m_RefreshError = false;
  1382. m_RefreshRehosted = true;
  1383. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  1384. {
  1385. // unqueue any existing game refreshes because we're going to assume the next successful game refresh indicates that the rehost worked
  1386. // this ignores the fact that it's possible a game refresh was just sent and no response has been received yet
  1387. // we assume this won't happen very often since the only downside is a potential false positive
  1388. (*i)->UnqueueGameRefreshes( );
  1389. (*i)->QueueGameUncreate( );
  1390. (*i)->QueueEnterChat( );
  1391. // the game creation message will be sent on the next refresh
  1392. }
  1393. m_CreationTime = GetTime( );
  1394. m_LastRefreshTime = GetTime( );
  1395. }
  1396. else
  1397. SendAllChat( m_GHost->m_Language->UnableToCreateGameNameTooLong( Payload ) );
  1398. // auto set HCL if map_defaulthcl is not empty
  1399. AutoSetHCL();
  1400. }
  1401. //
  1402. // !REFRESH (turn on or off refresh messages)
  1403. //
  1404. else if( Command == "refresh" && !m_CountDownStarted )
  1405. {
  1406. if ( m_GHost->m_HideCommands )
  1407. HideCommand = true;
  1408. if( Payload == "on" )
  1409. {
  1410. SendAllChat( m_GHost->m_Language->RefreshMessagesEnabled( ) );
  1411. m_RefreshMessages = true;
  1412. }
  1413. else if( Payload == "off" )
  1414. {
  1415. SendAllChat( m_GHost->m_Language->RefreshMessagesDisabled( ) );
  1416. m_RefreshMessages = false;
  1417. }
  1418. }
  1419. //
  1420. // !SAY
  1421. //
  1422. else if( Command == "say" && !Payload.empty( ) )
  1423. {
  1424. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  1425. (*i)->QueueChatCommand( Payload );
  1426. HideCommand = true;
  1427. }
  1428. //
  1429. // !SENDLAN
  1430. //
  1431. else if( Command == "sendlan" && !Payload.empty( ) && !m_CountDownStarted )
  1432. {
  1433. if ( m_GHost->m_HideCommands )
  1434. HideCommand = true;
  1435. // extract the ip and the port
  1436. // e.g. "1.2.3.4 6112" -> ip: "1.2.3.4", port: "6112"
  1437. string IP;
  1438. uint32_t Port = 6112;
  1439. stringstream SS;
  1440. SS << Payload;
  1441. SS >> IP;
  1442. if( !SS.eof( ) )
  1443. SS >> Port;
  1444. if( SS.fail( ) )
  1445. {
  1446. CONSOLE_Print( "[GAME: " + m_GameName + "] bad inputs to sendlan command" );
  1447. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad inputs to sendlan command", 0, 0));
  1448. }
  1449. else
  1450. {
  1451. // construct a fixed host counter which will be used to identify players from this "realm" (i.e. LAN)
  1452. // the fixed host counter's 4 most significant bits will contain a 4 bit ID (0-15)
  1453. // the rest of the fixed host counter will contain the 28 least significant bits of the actual host counter
  1454. // since we're destroying 4 bits of information here the actual host counter should not be greater than 2^28 which is a reasonable assumption
  1455. // when a player joins a game we can obtain the ID from the received host counter
  1456. // note: LAN broadcasts use an ID of 0, battle.net refreshes use an ID of 1-10, the rest are unused
  1457. uint32_t FixedHostCounter = m_HostCounter & 0x0FFFFFFF;
  1458. // we send 12 for SlotsTotal because this determines how many PID's Warcraft 3 allocates
  1459. // we need to make sure Warcraft 3 allocates at least SlotsTotal + 1 but at most 12 PID's
  1460. // this is because we need an extra PID for the virtual host player (but we always delete the virtual host player when the 12th person joins)
  1461. // however, we can't send 13 for SlotsTotal because this causes Warcraft 3 to crash when sharing control of units
  1462. // nor can we send SlotsTotal because then Warcraft 3 crashes when playing maps with less than 12 PID's (because of the virtual host player taking an extra PID)
  1463. // we also send 12 for SlotsOpen because Warcraft 3 assumes there's always at least one player in the game (the host)
  1464. // so if we try to send accurate numbers it'll always be off by one and results in Warcraft 3 assuming the game is full when it still needs one more player
  1465. // the easiest solution is to simply send 12 for both so the game will always show up as (1/12) players
  1466. if( m_SaveGame )
  1467. {
  1468. // note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
  1469. uint32_t MapGameType = MAPGAMETYPE_SAVEDGAME;
  1470. BYTEARRAY MapWidth;
  1471. MapWidth.push_back( 0 );
  1472. MapWidth.push_back( 0 );
  1473. BYTEARRAY MapHeight;
  1474. MapHeight.push_back( 0 );
  1475. MapHeight.push_back( 0 );
  1476. m_GHost->m_UDPSocket->SendTo( IP, Port, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), 12, 12, m_HostPort, FixedHostCounter, m_EntryKey ) );
  1477. }
  1478. else
  1479. {
  1480. // note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
  1481. // note: we do not use m_Map->GetMapGameType because none of the filters are set when broadcasting to LAN (also as you might expect)
  1482. uint32_t MapGameType = MAPGAMETYPE_UNKNOWN0;
  1483. m_GHost->m_UDPSocket->SendTo( IP, Port, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), 12, 12, m_HostPort, FixedHostCounter, m_EntryKey ) );
  1484. }
  1485. }
  1486. }
  1487. //
  1488. // !SP
  1489. //
  1490. else if( Command == "sp" && !m_CountDownStarted )
  1491. {
  1492. if ( m_GHost->m_HideCommands )
  1493. HideCommand = true;
  1494. SendAllChat( m_GHost->m_Language->ShufflingPlayers( ) );
  1495. ShuffleSlots( );
  1496. }
  1497. //
  1498. // !START
  1499. //
  1500. else if( Command == "start" && !m_CountDownStarted )
  1501. {
  1502. if ( m_GHost->m_HideCommands )
  1503. HideCommand = true;
  1504. // if the player sent "!start force" skip the checks and start the countdown
  1505. // otherwise check if a player left recently then start the autostart
  1506. // *(GCBC)*
  1507. if( Payload.empty( ) )
  1508. {
  1509. if( GetTicks( ) - m_LastPlayerLeaveTicks >= 2000 )
  1510. {
  1511. uint32_t AutoStartPlayers = GetNumHumanPlayers( );
  1512. if( AutoStartPlayers != 0 )
  1513. {
  1514. SendAllChat( m_GHost->m_Language->AutoStartEnabled( UTIL_ToString( AutoStartPlayers ) ) );
  1515. m_AutoStartPlayers = AutoStartPlayers;
  1516. m_UsingStart = true;
  1517. }
  1518. }
  1519. else
  1520. SendAllChat( m_GHost->m_Language->CountDownAbortedSomeoneLeftRecently( ) );
  1521. }
  1522. if( Payload == "force" )
  1523. StartCountDown( true );
  1524. // *(GCBC)*
  1525. if( Payload == "now" )
  1526. {
  1527. // skip checks and start the game right now
  1528. m_CountDownStarted = true;
  1529. m_CountDownCounter = 0;
  1530. }
  1531. }
  1532. // *(GCBC)*
  1533. // !STARTN
  1534. //
  1535. if( Command == "startn" && !m_CountDownStarted )
  1536. {
  1537. if ( m_GHost->m_HideCommands )
  1538. HideCommand = true;
  1539. // skip checks and start the game right now
  1540. m_CountDownStarted = true;
  1541. m_CountDownCounter = 0;
  1542. }
  1543. //
  1544. // !SWAP (swap slots)
  1545. //
  1546. else if( Command == "swap" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
  1547. {
  1548. if ( m_GHost->m_HideCommands )
  1549. HideCommand = true;
  1550. uint32_t SID1;
  1551. uint32_t SID2;
  1552. stringstream SS;
  1553. SS << Payload;
  1554. SS >> SID1;
  1555. if( SS.fail( ) )
  1556. {
  1557. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to swap command" );
  1558. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to swap command", 0, 0));
  1559. }
  1560. else
  1561. {
  1562. if( SS.eof( ) )
  1563. {
  1564. CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to swap command" );
  1565. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to swap command", 0, 0));
  1566. }
  1567. else
  1568. {
  1569. SS >> SID2;
  1570. if( SS.fail( ) )
  1571. CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to swap command" );
  1572. else
  1573. SwapSlots( (unsigned char)( SID1 - 1 ), (unsigned char)( SID2 - 1 ) );
  1574. }
  1575. }
  1576. }
  1577. //
  1578. // !SYNCLIMIT
  1579. //
  1580. else if( Command == "synclimit" )
  1581. {
  1582. if ( m_GHost->m_HideCommands )
  1583. HideCommand = true;
  1584. if( Payload.empty( ) )
  1585. SendAllChat( m_GHost->m_Language->SyncLimitIs( UTIL_ToString( m_SyncLimit ) ) );
  1586. else
  1587. {
  1588. m_SyncLimit = UTIL_ToUInt32( Payload );
  1589. if( m_SyncLimit <= 10 )
  1590. {
  1591. m_SyncLimit = 10;
  1592. SendAllChat( m_GHost->m_Language->SettingSyncLimitToMinimum( "10" ) );
  1593. }
  1594. else if( m_SyncLimit >= 10000 )
  1595. {
  1596. m_SyncLimit = 10000;
  1597. SendAllChat( m_GHost->m_Language->SettingSyncLimitToMaximum( "10000" ) );
  1598. }
  1599. else
  1600. SendAllChat( m_GHost->m_Language->SettingSyncLimitTo( UTIL_ToString( m_SyncLimit ) ) );
  1601. }
  1602. }
  1603. //
  1604. // !UNHOST
  1605. //
  1606. else if( Command == "unhost" && !m_CountDownStarted )
  1607. {
  1608. if ( m_GHost->m_HideCommands )
  1609. HideCommand = true;
  1610. m_Exiting = true;
  1611. }
  1612. //
  1613. // !UNLOCK
  1614. //
  1615. else if( Command == "unlock" && ( RootAdminCheck || IsOwner( User ) ) )
  1616. {
  1617. if ( m_GHost->m_HideCommands )
  1618. HideCommand = true;
  1619. SendAllChat( m_GHost->m_Language->GameUnlocked( ) );
  1620. m_Locked = false;
  1621. }
  1622. //
  1623. // !UNMUTE
  1624. //
  1625. else if( Command == "unmute" )
  1626. {
  1627. if ( m_GHost->m_HideCommands )
  1628. HideCommand = true;
  1629. CGamePlayer *LastMatch = NULL;
  1630. uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
  1631. if( Matches == 0 )
  1632. SendAllChat( m_GHost->m_Language->UnableToMuteNoMatchesFound( Payload ) );
  1633. else if( Matches == 1 )
  1634. {
  1635. SendAllChat( m_GHost->m_Language->UnmutedPlayer( LastMatch->GetName( ), User ) );
  1636. LastMatch->SetMuted( false );
  1637. }
  1638. else
  1639. SendAllChat( m_GHost->m_Language->UnableToMuteFoundMoreThanOneMatch( Payload ) );
  1640. }
  1641. //
  1642. // !UNMUTEALL
  1643. //
  1644. else if( Command == "unmuteall" && m_GameLoaded )
  1645. {
  1646. if ( m_GHost->m_HideCommands )
  1647. HideCommand = true;
  1648. SendAllChat( m_GHost->m_Language->GlobalChatUnmuted( ) );
  1649. m_MuteAll = false;
  1650. }
  1651. //
  1652. // !VIRTUALHOST
  1653. //
  1654. else if( Command == "virtualhost" && !Payload.empty( ) && Payload.size( ) <= 15 && !m_CountDownStarted )
  1655. {
  1656. if ( m_GHost->m_HideCommands )
  1657. HideCommand = true;
  1658. DeleteVirtualHost( );
  1659. m_VirtualHostName = Payload;
  1660. }
  1661. //
  1662. // !VOTECANCEL
  1663. //
  1664. else if( Command == "votecancel" && !m_KickVotePlayer.empty( ) )
  1665. {
  1666. if ( m_GHost->m_HideCommands )
  1667. HideCommand = true;
  1668. SendAllChat( m_GHost->m_Language->VoteKickCancelled( m_KickVotePlayer ) );
  1669. m_KickVotePlayer.clear( );
  1670. m_StartedKickVoteTime = 0;
  1671. }
  1672. //
  1673. // !W
  1674. //
  1675. else if( Command == "w" && !Payload.empty( ) )
  1676. {
  1677. // extract the name and the message
  1678. // e.g. "Varlock hello there!" -> name: "Varlock", message: "hello there!"
  1679. string Name;
  1680. string Message;
  1681. string :: size_type MessageStart = Payload.find( " " );
  1682. if( MessageStart != string :: npos )
  1683. {
  1684. Name = Payload.substr( 0, MessageStart );
  1685. Message = Payload.substr( MessageStart + 1 );
  1686. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  1687. (*i)->QueueChatCommand( Message, Name, true, false );
  1688. }
  1689. HideCommand = true;
  1690. }
  1691. //
  1692. //
  1693. // !normalcountdown
  1694. //
  1695. else if ( Command == "normalcountdown" && !Payload.empty( ) )
  1696. {
  1697. if ( m_GHost->m_HideCommands )
  1698. HideCommand = true;
  1699. if ( Payload == "on" )
  1700. {
  1701. m_GHost->m_UseNormalCountDown = true;
  1702. SendAllChat("Normal WC3 countdown enabled");
  1703. }
  1704. else if ( Payload == "off" )
  1705. {
  1706. m_GHost->m_UseNormalCountDown = false;
  1707. SendAllChat("Normal WC3 countdown disabled");
  1708. }
  1709. }
  1710. }
  1711. else
  1712. {
  1713. CONSOLE_Print( "[GAME: " + m_GameName + "] admin command ignored, the game is locked" );
  1714. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Admin command ignored, the game is locked", 0, 0));
  1715. SendChat( player, m_GHost->m_Language->TheGameIsLocked( ) );
  1716. }
  1717. }
  1718. else
  1719. {
  1720. if( !player->GetSpoofed( ) )
  1721. {
  1722. CONSOLE_Print( "[GAME: " + m_GameName + "] non-spoofchecked user [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]" );
  1723. //forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Non-spoofchecked user [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]", 0, 0));
  1724. }
  1725. else
  1726. {
  1727. CONSOLE_Print( "[GAME: " + m_GameName + "] non-admin [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]" );
  1728. //forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Non-admin [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]", 0, 0));
  1729. }
  1730. }
  1731. /*********************
  1732. * NON ADMIN COMMANDS *
  1733. *********************/
  1734. //
  1735. // !CHECKME
  1736. //
  1737. if( Command == "checkme" )
  1738. SendChat( player, m_GHost->m_Language->CheckedPlayer( User, player->GetNumPings( ) > 0 ? UTIL_ToString( player->GetPing( m_GHost->m_LCPings ) ) + "ms" : "N/A", m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( player->GetExternalIP( ), true ) ), AdminCheck || RootAdminCheck ? "Yes" : "No", IsOwner( User ) ? "Yes" : "No", player->GetSpoofed( ) ? "Yes" : "No", player->GetSpoofedRealm( ).empty( ) ? "N/A" : player->GetSpoofedRealm( ), player->GetReserved( ) ? "Yes" : "No" ) );
  1739. //
  1740. // !STATS
  1741. // !S
  1742. //
  1743. else if( ( Command == "stats" || Command == "s" ) && GetTime( ) - player->GetStatsSentTime( ) >= 5 )
  1744. {
  1745. string StatsUser = User;
  1746. if( !Payload.empty( ) )
  1747. StatsUser = Payload;
  1748. if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
  1749. m_PairedGPSChecks.push_back( PairedGPSCheck( string( ), m_GHost->m_DB->ThreadedGamePlayerSummaryCheck( StatsUser ) ) );
  1750. else
  1751. m_PairedGPSChecks.push_back( PairedGPSCheck( User, m_GHost->m_DB->ThreadedGamePlayerSummaryCheck( StatsUser ) ) );
  1752. player->SetStatsSentTime( GetTime( ) );
  1753. }
  1754. //
  1755. // !STATSDOTA
  1756. // !SD
  1757. // !DOTASTATS
  1758. // !DS
  1759. //
  1760. else if( ( Command == "statsdota" || Command == "sd" || Command == "ds" || Command == "dotastats" ) && GetTime( ) - player->GetStatsDotASentTime( ) >= 5 )
  1761. {
  1762. string StatsUser = User;
  1763. if( !Payload.empty( ) )
  1764. StatsUser = Payload;
  1765. if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
  1766. m_PairedDPSChecks.push_back( PairedDPSCheck( string( ), m_GHost->m_DB->ThreadedDotAPlayerSummaryCheck( StatsUser ) ) );
  1767. else
  1768. m_PairedDPSChecks.push_back( PairedDPSCheck( User, m_GHost->m_DB->ThreadedDotAPlayerSummaryCheck( StatsUser ) ) );
  1769. player->SetStatsDotASentTime( GetTime( ) );
  1770. }
  1771. //
  1772. // !VERSION
  1773. //
  1774. else if( Command == "version" )
  1775. {
  1776. if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
  1777. SendChat( player, m_GHost->m_Language->VersionAdmin( m_GHost->m_Version ) );
  1778. else
  1779. SendChat( player, m_GHost->m_Language->VersionNotAdmin( m_GHost->m_Version ) );
  1780. }
  1781. //
  1782. // !VOTEKICK
  1783. //
  1784. else if( Command == "votekick" && m_GHost->m_VoteKickAllowed && !Payload.empty( ) )
  1785. {
  1786. if( !m_KickVotePlayer.empty( ) )
  1787. SendChat( player, m_GHost->m_Language->UnableToVoteKickAlreadyInProgress( ) );
  1788. else if( m_Players.size( ) == 2 )
  1789. SendChat( player, m_GHost->m_Language->UnableToVoteKickNotEnoughPlayers( ) );
  1790. else
  1791. {
  1792. CGamePlayer *LastMatch = NULL;
  1793. uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
  1794. if( Matches == 0 )
  1795. SendChat( player, m_GHost->m_Language->UnableToVoteKickNoMatchesFound( Payload ) );
  1796. else if( Matches == 1 )
  1797. {
  1798. if( LastMatch->GetReserved( ) )
  1799. SendChat( player, m_GHost->m_Language->UnableToVoteKickPlayerIsReserved( LastMatch->GetName( ) ) );
  1800. else
  1801. {
  1802. m_KickVotePlayer = LastMatch->GetName( );
  1803. m_StartedKickVoteTime = GetTime( );
  1804. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  1805. (*i)->SetKickVote( false );
  1806. player->SetKickVote( true );
  1807. CONSOLE_Print( "[GAME: " + m_GameName + "] votekick against player [" + m_KickVotePlayer + "] started by player [" + User + "]" );
  1808. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Votekick against player [" + m_KickVotePlayer + "] started by player [" + User + "]", 0, 0));
  1809. SendAllChat( m_GHost->m_Language->StartedVoteKick( LastMatch->GetName( ), User, UTIL_ToString( (uint32_t)ceil( ( GetNumHumanPlayers( ) - 1 ) * (float)m_GHost->m_VoteKickPercentage / 100 ) - 1 ) ) );
  1810. SendAllChat( m_GHost->m_Language->TypeYesToVote( string( 1, m_GHost->m_CommandTrigger ) ) );
  1811. }
  1812. }
  1813. else
  1814. SendChat( player, m_GHost->m_Language->UnableToVoteKickFoundMoreThanOneMatch( Payload ) );
  1815. }
  1816. }
  1817. //
  1818. // !YES
  1819. //
  1820. else if( Command == "yes" && !m_KickVotePlayer.empty( ) && player->GetName( ) != m_KickVotePlayer && !player->GetKickVote( ) )
  1821. {
  1822. player->SetKickVote( true );
  1823. uint32_t VotesNeeded = (uint32_t)ceil( ( GetNumHumanPlayers( ) - 1 ) * (float)m_GHost->m_VoteKickPercentage / 100 );
  1824. uint32_t Votes = 0;
  1825. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  1826. {
  1827. if( (*i)->GetKickVote( ) )
  1828. ++Votes;
  1829. }
  1830. if( Votes >= VotesNeeded )
  1831. {
  1832. CGamePlayer *Victim = GetPlayerFromName( m_KickVotePlayer, true );
  1833. if( Victim )
  1834. {
  1835. Victim->SetDeleteMe( true );
  1836. Victim->SetLeftReason( m_GHost->m_Language->WasKickedByVote( ) );
  1837. if( !m_GameLoading && !m_GameLoaded )
  1838. Victim->SetLeftCode( PLAYERLEAVE_LOBBY );
  1839. else
  1840. Victim->SetLeftCode( PLAYERLEAVE_LOST );
  1841. if( !m_GameLoading && !m_GameLoaded )
  1842. OpenSlot( GetSIDFromPID( Victim->GetPID( ) ), false );
  1843. CONSOLE_Print( "[GAME: " + m_GameName + "] votekick against player [" + m_KickVotePlayer + "] passed with " + UTIL_ToString( Votes ) + "/" + UTIL_ToString( GetNumHumanPlayers( ) ) + " votes" );
  1844. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Votekick against player [" + m_KickVotePlayer + "] passed with " + UTIL_ToString( Votes ) + "/" + UTIL_ToString( GetNumHumanPlayers( ) ) + " votes", 0, 0));
  1845. SendAllChat( m_GHost->m_Language->VoteKickPassed( m_KickVotePlayer ) );
  1846. }
  1847. else
  1848. SendAllChat( m_GHost->m_Language->ErrorVoteKickingPlayer( m_KickVotePlayer ) );
  1849. m_KickVotePlayer.clear( );
  1850. m_StartedKickVoteTime = 0;
  1851. }
  1852. else
  1853. SendAllChat( m_GHost->m_Language->VoteKickAcceptedNeedMoreVotes( m_KickVotePlayer, User, UTIL_ToString( VotesNeeded - Votes ) ) );
  1854. }
  1855. return HideCommand;
  1856. }
  1857. void CGame :: EventGameStarted( )
  1858. {
  1859. CBaseGame :: EventGameStarted( );
  1860. // record everything we need to ban each player in case we decide to do so later
  1861. // this is because when a player leaves the game an admin might want to ban that player
  1862. // but since the player has already left the game we don't have access to their information anymore
  1863. // so we create a "potential ban" for each player and only store it in the database if requested to by an admin
  1864. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  1865. m_DBBans.push_back( new CDBBan( (*i)->GetJoinedRealm( ), (*i)->GetName( ), (*i)->GetExternalIPString( ), string( ), string( ), string( ), string( ) ) );
  1866. }
  1867. bool CGame :: IsGameDataSaved( )
  1868. {
  1869. return m_CallableGameAdd && m_CallableGameAdd->GetReady( );
  1870. }
  1871. void CGame :: SaveGameData( )
  1872. {
  1873. CONSOLE_Print( "[GAME: " + m_GameName + "] saving game data to database" );
  1874. forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Saving game data to database", 0, 0));
  1875. m_CallableGameAdd = m_GHost->m_DB->ThreadedGameAdd( m_GHost->m_BNETs.size( ) == 1 ? m_GHost->m_BNETs[0]->GetServer( ) : string( ), m_DBGame->GetMap( ), m_GameName, m_OwnerName, m_GameTicks / 1000, m_GameState, m_CreatorName, m_CreatorServer );
  1876. }