/ghost/game_base.cpp

http://ghostcb.googlecode.com/ · C++ · 5237 lines · 3551 code · 1091 blank · 595 comment · 1303 complexity · cf655cf2ef0b8f397572278fd51a8654 MD5 · raw file

Large files are truncated click here to view the full 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. // Next line enables database-stats in UI (Stats, DotA/DB)
  15. //#define ENABLE_UI_DB_STATS
  16. #include "ghost.h"
  17. #include "util.h"
  18. #include "config.h"
  19. #include "language.h"
  20. #include "socket.h"
  21. #include "ghostdb.h"
  22. #include "bnet.h"
  23. #include "map.h"
  24. #include "packed.h"
  25. #include "savegame.h"
  26. #include "replay.h"
  27. #include "gameplayer.h"
  28. #include "gameprotocol.h"
  29. #include "game_base.h"
  30. #include "ui/forward.h"
  31. #include <cmath>
  32. #include <string.h>
  33. #include <time.h>
  34. #include "next_combination.h"
  35. //
  36. // CBaseGame
  37. //
  38. static uint32_t nextID = 0;
  39. CBaseGame :: CBaseGame( CGHost *nGHost, CMap *nMap, CSaveGame *nSaveGame, uint16_t nHostPort, unsigned char nGameState, string nGameName, string nOwnerName, string nCreatorName, string nCreatorServer )
  40. {
  41. m_GameID = nextID++;
  42. m_GHost = nGHost;
  43. m_Socket = new CTCPServer( );
  44. m_Protocol = new CGameProtocol( m_GHost );
  45. m_Map = new CMap( *nMap );
  46. m_SaveGame = nSaveGame;
  47. if( m_GHost->m_SaveReplays && !m_SaveGame )
  48. m_Replay = new CReplay( );
  49. else
  50. m_Replay = NULL;
  51. m_Exiting = false;
  52. m_Saving = false;
  53. m_HostPort = nHostPort;
  54. m_GameState = nGameState;
  55. m_VirtualHostPID = 255;
  56. m_FakePlayerPID = 255;
  57. // wait time of 1 minute = 0 empty actions required
  58. // wait time of 2 minutes = 1 empty action required
  59. // etc...
  60. if( m_GHost->m_ReconnectWaitTime == 0 )
  61. m_GProxyEmptyActions = 0;
  62. else
  63. {
  64. m_GProxyEmptyActions = m_GHost->m_ReconnectWaitTime - 1;
  65. // clamp to 9 empty actions (10 minutes)
  66. if( m_GProxyEmptyActions > 9 )
  67. m_GProxyEmptyActions = 9;
  68. }
  69. m_GameName = nGameName;
  70. m_LastGameName = nGameName;
  71. m_VirtualHostName = m_GHost->m_VirtualHostName;
  72. m_OwnerName = nOwnerName;
  73. m_CreatorName = nCreatorName;
  74. m_CreatorServer = nCreatorServer;
  75. m_HCLCommandString = m_Map->GetMapDefaultHCL( );
  76. m_RandomSeed = GetTicks( );
  77. m_HostCounter = m_GHost->m_HostCounter++;
  78. m_EntryKey = rand( );
  79. m_Latency = m_GHost->m_Latency;
  80. m_SyncLimit = m_GHost->m_SyncLimit;
  81. m_SyncCounter = 0;
  82. m_GameTicks = 0;
  83. m_CreationTime = GetTime( );
  84. m_LastPingTime = GetTime( );
  85. m_LastRefreshTime = GetTime( );
  86. m_LastDownloadTicks = GetTime( );
  87. m_DownloadCounter = 0;
  88. m_LastDownloadCounterResetTicks = GetTicks( );
  89. m_LastAnnounceTime = 0;
  90. m_AnnounceInterval = 0;
  91. m_LastAutoStartTime = GetTime( );
  92. m_AutoStartPlayers = 0;
  93. m_LastCountDownTicks = 0;
  94. m_CountDownCounter = 0;
  95. m_StartedLoadingTicks = 0;
  96. m_StartPlayers = 0;
  97. m_LastLagScreenResetTime = 0;
  98. m_LastActionSentTicks = 0;
  99. m_LastActionLateBy = 0;
  100. m_StartedLaggingTime = 0;
  101. m_LastLagScreenTime = 0;
  102. m_LastReservedSeen = GetTime( );
  103. m_StartedKickVoteTime = 0;
  104. m_GameOverTime = 0;
  105. m_LastPlayerLeaveTicks = 0;
  106. m_MinimumScore = 0.0;
  107. m_MaximumScore = 0.0;
  108. m_SlotInfoChanged = false;
  109. m_Locked = false;
  110. m_RefreshMessages = m_GHost->m_RefreshMessages;
  111. m_RefreshError = false;
  112. m_RefreshRehosted = false;
  113. m_MuteAll = false;
  114. m_MuteLobby = false;
  115. m_CountDownStarted = false;
  116. m_GameLoading = false;
  117. m_GameLoaded = false;
  118. m_LoadInGame = m_Map->GetMapLoadInGame( );
  119. m_Lagging = false;
  120. m_AutoSave = m_GHost->m_AutoSave;
  121. m_MatchMaking = false;
  122. m_LocalAdminMessages = m_GHost->m_LocalAdminMessages;
  123. m_UsingStart = false;
  124. m_LastUiTime = 0;
  125. m_LastUiSlotsTime = 0;
  126. if( m_SaveGame )
  127. {
  128. m_EnforceSlots = m_SaveGame->GetSlots( );
  129. m_Slots = m_SaveGame->GetSlots( );
  130. // the savegame slots contain player entries
  131. // we really just want the open/closed/computer entries
  132. // so open all the player slots
  133. for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); ++i )
  134. {
  135. if( (*i).GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && (*i).GetComputer( ) == 0 )
  136. {
  137. (*i).SetPID( 0 );
  138. (*i).SetDownloadStatus( 255 );
  139. (*i).SetSlotStatus( SLOTSTATUS_OPEN );
  140. }
  141. }
  142. }
  143. else
  144. m_Slots = m_Map->GetSlots( );
  145. if( !m_GHost->m_IPBlackListFile.empty( ) )
  146. {
  147. ifstream in;
  148. in.open( m_GHost->m_IPBlackListFile.c_str( ) );
  149. if( in.fail( ) )
  150. CONSOLE_Print( "[GAME: " + m_GameName + "] error loading IP blacklist file [" + m_GHost->m_IPBlackListFile + "]" );
  151. else
  152. {
  153. CONSOLE_Print( "[GAME: " + m_GameName + "] loading IP blacklist file [" + m_GHost->m_IPBlackListFile + "]" );
  154. string Line;
  155. while( !in.eof( ) )
  156. {
  157. getline( in, Line );
  158. // ignore blank lines and comments
  159. if( Line.empty( ) || Line[0] == '#' )
  160. continue;
  161. // remove newlines and partial newlines to help fix issues with Windows formatted files on Linux systems
  162. Line.erase( remove( Line.begin( ), Line.end( ), ' ' ), Line.end( ) );
  163. Line.erase( remove( Line.begin( ), Line.end( ), '\r' ), Line.end( ) );
  164. Line.erase( remove( Line.begin( ), Line.end( ), '\n' ), Line.end( ) );
  165. // ignore lines that don't look like IP addresses
  166. if( Line.find_first_not_of( "1234567890." ) != string :: npos )
  167. continue;
  168. m_IPBlackList.insert( Line );
  169. }
  170. in.close( );
  171. CONSOLE_Print( "[GAME: " + m_GameName + "] loaded " + UTIL_ToString( m_IPBlackList.size( ) ) + " lines from IP blacklist file" );
  172. }
  173. }
  174. // start listening for connections
  175. if( !m_GHost->m_BindAddress.empty( ) )
  176. CONSOLE_Print( "[GAME: " + m_GameName + "] attempting to bind to address [" + m_GHost->m_BindAddress + "]" );
  177. else
  178. CONSOLE_Print( "[GAME: " + m_GameName + "] attempting to bind to all available addresses" );
  179. if( m_Socket->Listen( m_GHost->m_BindAddress, m_HostPort ) )
  180. CONSOLE_Print( "[GAME: " + m_GameName + "] listening on port " + UTIL_ToString( m_HostPort ) );
  181. else
  182. {
  183. CONSOLE_Print( "[GAME: " + m_GameName + "] error listening on port " + UTIL_ToString( m_HostPort ) );
  184. m_Exiting = true;
  185. }
  186. forward( new CFwdData( FWD_GAME_ADD, m_GameName, m_GameID ) );
  187. vector<string> MapData;
  188. MapData.push_back( "Local Path" ); MapData.push_back( m_Map->GetMapLocalPath( ) );
  189. forward( new CFwdData( FWD_GAME_MAP_INFO_ADD, MapData, m_GameID ) );
  190. MapData.clear( );
  191. MapData.push_back( "Players" ); MapData.push_back( UTIL_ToString( GetNumHumanPlayers( ) ) + "/" + UTIL_ToString( m_GameLoading || m_GameLoaded ? m_StartPlayers : m_Slots.size( ) ) );
  192. forward( new CFwdData( FWD_GAME_MAP_INFO_ADD, MapData, m_GameID ) );
  193. MapData.clear( );
  194. MapData.push_back( "Teams" ); MapData.push_back( UTIL_ToString( m_Map->GetMapNumTeams( ) ) );
  195. forward( new CFwdData( FWD_GAME_MAP_INFO_ADD, MapData, m_GameID ) );
  196. MapData.clear( );
  197. MapData.push_back( "Visibility" ); MapData.push_back( m_Map->GetMapVisibility( ) == 4 ? "Default" :
  198. m_Map->GetMapVisibility( ) == 3 ? "Always visible" :
  199. m_Map->GetMapVisibility( ) == 2 ? "Explored" : "Hide Terrain" );
  200. forward( new CFwdData( FWD_GAME_MAP_INFO_ADD, MapData, m_GameID ) );
  201. MapData.clear( );
  202. MapData.push_back( "Speed" ); MapData.push_back( m_Map->GetMapSpeed( ) == 3 ? "Fast" :
  203. m_Map->GetMapSpeed( ) == 2 ? "Normal" : "Slow" );
  204. forward( new CFwdData( FWD_GAME_MAP_INFO_ADD, MapData, m_GameID ) );
  205. MapData.clear( );
  206. MapData.push_back( "HCL" ); MapData.push_back( m_HCLCommandString );
  207. forward( new CFwdData( FWD_GAME_MAP_INFO_ADD, MapData, m_GameID ) );
  208. MapData.clear( );
  209. MapData.push_back( "Owner" ); MapData.push_back( m_OwnerName );
  210. forward( new CFwdData( FWD_GAME_MAP_INFO_ADD, MapData, m_GameID ) );
  211. MapData.clear( );
  212. MapData.push_back( "Time" );
  213. if( m_GameLoading || m_GameLoaded )
  214. MapData.push_back( UTIL_ToString( ( m_GameTicks / 1000 ) / 60 ) + " minutes" );
  215. else
  216. MapData.push_back( UTIL_ToString( ( GetTime( ) - m_CreationTime ) / 60 ) + " minutes" );
  217. forward( new CFwdData( FWD_GAME_MAP_INFO_ADD, MapData, m_GameID ) );
  218. }
  219. CBaseGame :: ~CBaseGame( )
  220. {
  221. forward( new CFwdData( FWD_GAME_REMOVE, m_GameID ) );
  222. // save replay
  223. // todotodo: put this in a thread
  224. if( m_Replay && ( m_GameLoading || m_GameLoaded ) )
  225. {
  226. time_t Now = time( NULL );
  227. char Time[17];
  228. memset( Time, 0, sizeof( char ) * 17 );
  229. strftime( Time, sizeof( char ) * 17, "%Y-%m-%d %H-%M", localtime( &Now ) );
  230. string MinString = UTIL_ToString( ( m_GameTicks / 1000 ) / 60 );
  231. string SecString = UTIL_ToString( ( m_GameTicks / 1000 ) % 60 );
  232. if( MinString.size( ) == 1 )
  233. MinString.insert( 0, "0" );
  234. if( SecString.size( ) == 1 )
  235. SecString.insert( 0, "0" );
  236. m_Replay->BuildReplay( m_GameName, m_StatString, m_GHost->m_ReplayWar3Version, m_GHost->m_ReplayBuildNumber );
  237. m_Replay->Save( m_GHost->m_TFT, m_GHost->m_ReplayPath + UTIL_FileSafeName( "GHost++ " + string( Time ) + " " + m_GameName + " (" + MinString + "m" + SecString + "s).w3g" ) );
  238. }
  239. delete m_Socket;
  240. delete m_Protocol;
  241. delete m_Map;
  242. delete m_Replay;
  243. for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); ++i )
  244. delete *i;
  245. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  246. delete *i;
  247. for( vector<CCallableScoreCheck *> :: iterator i = m_ScoreChecks.begin( ); i != m_ScoreChecks.end( ); ++i )
  248. m_GHost->m_Callables.push_back( *i );
  249. while( !m_Actions.empty( ) )
  250. {
  251. delete m_Actions.front( );
  252. m_Actions.pop( );
  253. }
  254. }
  255. uint32_t CBaseGame :: GetNextTimedActionTicks( )
  256. {
  257. // return the number of ticks (ms) until the next "timed action", which for our purposes is the next game update
  258. // the main GHost++ loop will make sure the next loop update happens at or before this value
  259. // note: there's no reason this function couldn't take into account the game's other timers too but they're far less critical
  260. // warning: this function must take into account when actions are not being sent (e.g. during loading or lagging)
  261. if( !m_GameLoaded || m_Lagging )
  262. return 50;
  263. uint32_t TicksSinceLastUpdate = GetTicks( ) - m_LastActionSentTicks;
  264. if( TicksSinceLastUpdate > m_Latency - m_LastActionLateBy )
  265. return 0;
  266. else
  267. return m_Latency - m_LastActionLateBy - TicksSinceLastUpdate;
  268. }
  269. uint32_t CBaseGame :: GetSlotsOccupied( )
  270. {
  271. uint32_t NumSlotsOccupied = 0;
  272. for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); ++i )
  273. {
  274. if( (*i).GetSlotStatus( ) == SLOTSTATUS_OCCUPIED )
  275. ++NumSlotsOccupied;
  276. }
  277. return NumSlotsOccupied;
  278. }
  279. uint32_t CBaseGame :: GetSlotsOpen( )
  280. {
  281. uint32_t NumSlotsOpen = 0;
  282. for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); ++i )
  283. {
  284. if( (*i).GetSlotStatus( ) == SLOTSTATUS_OPEN )
  285. ++NumSlotsOpen;
  286. }
  287. return NumSlotsOpen;
  288. }
  289. uint32_t CBaseGame :: GetNumPlayers( )
  290. {
  291. uint32_t NumPlayers = GetNumHumanPlayers( );
  292. if( m_FakePlayerPID != 255 )
  293. ++NumPlayers;
  294. return NumPlayers;
  295. }
  296. uint32_t CBaseGame :: GetNumHumanPlayers( )
  297. {
  298. uint32_t NumHumanPlayers = 0;
  299. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  300. {
  301. if( !(*i)->GetLeftMessageSent( ) )
  302. ++NumHumanPlayers;
  303. }
  304. return NumHumanPlayers;
  305. }
  306. string CBaseGame :: GetDescription( )
  307. {
  308. string Description = m_GameName + " : " + m_OwnerName + " : " + UTIL_ToString( GetNumHumanPlayers( ) ) + "/" + UTIL_ToString( m_GameLoading || m_GameLoaded ? m_StartPlayers : m_Slots.size( ) );
  309. if( m_GameLoading || m_GameLoaded )
  310. Description += " : " + UTIL_ToString( ( m_GameTicks / 1000 ) / 60 ) + "m";
  311. else
  312. Description += " : " + UTIL_ToString( ( GetTime( ) - m_CreationTime ) / 60 ) + "m";
  313. return Description;
  314. }
  315. void CBaseGame :: SetAnnounce( uint32_t interval, string message )
  316. {
  317. m_AnnounceInterval = interval;
  318. m_AnnounceMessage = message;
  319. m_LastAnnounceTime = GetTime( );
  320. }
  321. unsigned int CBaseGame :: SetFD( void *fd, void *send_fd, int *nfds )
  322. {
  323. unsigned int NumFDs = 0;
  324. if( m_Socket )
  325. {
  326. m_Socket->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
  327. ++NumFDs;
  328. }
  329. for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); ++i )
  330. {
  331. if( (*i)->GetSocket( ) )
  332. {
  333. (*i)->GetSocket( )->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
  334. ++NumFDs;
  335. }
  336. }
  337. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  338. {
  339. if( (*i)->GetSocket( ) )
  340. {
  341. (*i)->GetSocket( )->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
  342. ++NumFDs;
  343. }
  344. }
  345. return NumFDs;
  346. }
  347. bool CBaseGame :: Update( void *fd, void *send_fd )
  348. {
  349. // update callables
  350. for( vector<CCallableScoreCheck *> :: iterator i = m_ScoreChecks.begin( ); i != m_ScoreChecks.end( ); )
  351. {
  352. if( (*i)->GetReady( ) )
  353. {
  354. double Score = (*i)->GetResult( );
  355. for( vector<CPotentialPlayer *> :: iterator j = m_Potentials.begin( ); j != m_Potentials.end( ); ++j )
  356. {
  357. if( (*j)->GetJoinPlayer( ) && (*j)->GetJoinPlayer( )->GetName( ) == (*i)->GetName( ) )
  358. EventPlayerJoinedWithScore( *j, (*j)->GetJoinPlayer( ), Score );
  359. }
  360. m_GHost->m_DB->RecoverCallable( *i );
  361. delete *i;
  362. i = m_ScoreChecks.erase( i );
  363. }
  364. else
  365. ++i;
  366. }
  367. // update players
  368. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); )
  369. {
  370. if( (*i)->Update( fd ) )
  371. {
  372. EventPlayerDeleted( *i );
  373. delete *i;
  374. i = m_Players.erase( i );
  375. }
  376. else
  377. ++i;
  378. }
  379. for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); )
  380. {
  381. if( (*i)->Update( fd ) )
  382. {
  383. // flush the socket (e.g. in case a rejection message is queued)
  384. if( (*i)->GetSocket( ) )
  385. (*i)->GetSocket( )->DoSend( (fd_set *)send_fd );
  386. delete *i;
  387. i = m_Potentials.erase( i );
  388. }
  389. else
  390. ++i;
  391. }
  392. // create the virtual host player
  393. if( !m_GameLoading && !m_GameLoaded && GetNumPlayers( ) < 12 )
  394. CreateVirtualHost( );
  395. // unlock the game
  396. if( m_Locked && !GetPlayerFromName( m_OwnerName, false ) )
  397. {
  398. SendAllChat( m_GHost->m_Language->GameUnlocked( ) );
  399. m_Locked = false;
  400. }
  401. // ping every 5 seconds
  402. // changed this to ping during game loading as well to hopefully fix some problems with people disconnecting during loading
  403. // changed this to ping during the game as well
  404. if( GetTime( ) - m_LastPingTime >= 5 )
  405. {
  406. // note: we must send pings to players who are downloading the map because Warcraft III disconnects from the lobby if it doesn't receive a ping every ~90 seconds
  407. // so if the player takes longer than 90 seconds to download the map they would be disconnected unless we keep sending pings
  408. // todotodo: ignore pings received from players who have recently finished downloading the map
  409. SendAll( m_Protocol->SEND_W3GS_PING_FROM_HOST( ) );
  410. // we also broadcast the game to the local network every 5 seconds so we hijack this timer for our nefarious purposes
  411. // however we only want to broadcast if the countdown hasn't started
  412. // see the !sendlan code later in this file for some more information about how this works
  413. // todotodo: should we send a game cancel message somewhere? we'll need to implement a host counter for it to work
  414. if( !m_CountDownStarted )
  415. {
  416. // construct a fixed host counter which will be used to identify players from this "realm" (i.e. LAN)
  417. // the fixed host counter's 4 most significant bits will contain a 4 bit ID (0-15)
  418. // the rest of the fixed host counter will contain the 28 least significant bits of the actual host counter
  419. // 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
  420. // when a player joins a game we can obtain the ID from the received host counter
  421. // note: LAN broadcasts use an ID of 0, battle.net refreshes use an ID of 1-10, the rest are unused
  422. uint32_t FixedHostCounter = m_HostCounter & 0x0FFFFFFF;
  423. // we send 12 for SlotsTotal because this determines how many PID's Warcraft 3 allocates
  424. // we need to make sure Warcraft 3 allocates at least SlotsTotal + 1 but at most 12 PID's
  425. // 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)
  426. // however, we can't send 13 for SlotsTotal because this causes Warcraft 3 to crash when sharing control of units
  427. // 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)
  428. // we also send 12 for SlotsOpen because Warcraft 3 assumes there's always at least one player in the game (the host)
  429. // 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
  430. // the easiest solution is to simply send 12 for both so the game will always show up as (1/12) players
  431. if( m_SaveGame )
  432. {
  433. // note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
  434. uint32_t MapGameType = MAPGAMETYPE_SAVEDGAME;
  435. BYTEARRAY MapWidth;
  436. BYTEARRAY MapHeight;
  437. if ( m_GHost->m_Reconnect )
  438. {
  439. MapWidth.push_back( 192 );
  440. MapWidth.push_back( 7 );
  441. MapHeight.push_back( 192 );
  442. MapHeight.push_back( 7 );
  443. }
  444. else
  445. {
  446. MapWidth.push_back( 0 );
  447. MapWidth.push_back( 0 );
  448. MapHeight.push_back( 0 );
  449. MapHeight.push_back( 0 );
  450. }
  451. BYTEARRAY data = 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 );
  452. m_GHost->m_UDPSocket->Broadcast( 6112, data );
  453. for(vector<CTCPSocket * >::iterator i = m_GHost->m_GameBroadcasters.begin( ); i!= m_GHost->m_GameBroadcasters.end( ); i++ )
  454. {
  455. (*i)->PutBytes( data );
  456. }
  457. }
  458. else
  459. {
  460. // note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
  461. // note: we do not use m_Map->GetMapGameType because none of the filters are set when broadcasting to LAN (also as you might expect)
  462. uint32_t MapGameType = MAPGAMETYPE_UNKNOWN0;
  463. BYTEARRAY MapWidth;
  464. BYTEARRAY MapHeight;
  465. if ( m_GHost->m_Reconnect )
  466. {
  467. MapWidth.push_back( 192 );
  468. MapWidth.push_back( 7 );
  469. MapHeight.push_back( 192 );
  470. MapHeight.push_back( 7 );
  471. }
  472. else
  473. {
  474. MapWidth = m_Map->GetMapWidth( );
  475. MapHeight = m_Map->GetMapHeight( );
  476. }
  477. BYTEARRAY data = 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, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), 12, 12, m_HostPort, FixedHostCounter, m_EntryKey );
  478. m_GHost->m_UDPSocket->Broadcast( 6112, data );
  479. for(vector<CTCPSocket * >::iterator i = m_GHost->m_GameBroadcasters.begin( ); i!= m_GHost->m_GameBroadcasters.end( ); i++ )
  480. {
  481. (*i)->PutBytes( data );
  482. }
  483. }
  484. }
  485. m_LastPingTime = GetTime( );
  486. }
  487. // auto rehost if there was a refresh error in autohosted games
  488. if( m_RefreshError && !m_CountDownStarted && m_GameState == GAME_PUBLIC && !m_GHost->m_AutoHostGameName.empty( ) && m_GHost->m_AutoHostMaximumGames != 0 && m_GHost->m_AutoHostAutoStartPlayers != 0 && m_AutoStartPlayers != 0 )
  489. {
  490. // there's a slim chance that this isn't actually an autohosted game since there is no explicit autohost flag
  491. // however, if autohosting is enabled and this game is public and this game is set to autostart, it's probably autohosted
  492. // so rehost it using the current autohost game name
  493. string GameName = m_GHost->m_AutoHostGameName + " #" + UTIL_ToString( m_GHost->m_HostCounter );
  494. CONSOLE_Print( "[GAME: " + m_GameName + "] automatically trying to rehost as public game [" + GameName + "] due to refresh failure" );
  495. m_LastGameName = m_GameName;
  496. m_GameName = GameName;
  497. m_HostCounter = m_GHost->m_HostCounter++;
  498. m_RefreshError = false;
  499. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  500. {
  501. (*i)->QueueGameUncreate( );
  502. (*i)->QueueEnterChat( );
  503. // the game creation message will be sent on the next refresh
  504. }
  505. m_CreationTime = GetTime( );
  506. m_LastRefreshTime = GetTime( );
  507. }
  508. // refresh every 3 seconds
  509. if( !m_RefreshError && !m_CountDownStarted && m_GameState == GAME_PUBLIC && GetSlotsOpen( ) > 0 && GetTime( ) - m_LastRefreshTime >= 3 )
  510. {
  511. // send a game refresh packet to each battle.net connection
  512. bool Refreshed = false;
  513. for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
  514. {
  515. // don't queue a game refresh message if the queue contains more than 1 packet because they're very low priority
  516. if( (*i)->GetOutPacketsQueued( ) <= 1 )
  517. {
  518. /*
  519. Varlock's original refresh
  520. (*i)->QueueGameRefresh( m_GameState, m_GameName, string( ), m_Map, m_SaveGame, GetTime( ) - m_CreationTime, m_HostCounter );
  521. */
  522. // Refresh code from Fire86 (http://codelain.com/forum/index.php?topic=11373.msg88767#msg88767)
  523. (*i)->QueueGameRefresh( m_GameState, m_GameName, string( ), m_Map, m_SaveGame, 0, m_HostCounter );
  524. Refreshed = true;
  525. }
  526. }
  527. // only print the "game refreshed" message if we actually refreshed on at least one battle.net server
  528. if( m_RefreshMessages && Refreshed )
  529. SendAllChat( m_GHost->m_Language->GameRefreshed( ) );
  530. m_LastRefreshTime = GetTime( );
  531. }
  532. // send more map data
  533. if( !m_GameLoading && !m_GameLoaded && GetTicks( ) - m_LastDownloadCounterResetTicks >= 1000 )
  534. {
  535. // hackhack: another timer hijack is in progress here
  536. // since the download counter is reset once per second it's a great place to update the slot info if necessary
  537. if( m_SlotInfoChanged )
  538. SendAllSlotInfo( );
  539. m_DownloadCounter = 0;
  540. m_LastDownloadCounterResetTicks = GetTicks( );
  541. }
  542. if( !m_GameLoading && !m_GameLoaded && GetTicks( ) - m_LastDownloadTicks >= 100 )
  543. {
  544. uint32_t Downloaders = 0;
  545. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  546. {
  547. if( (*i)->GetDownloadStarted( ) && !(*i)->GetDownloadFinished( ) )
  548. {
  549. ++Downloaders;
  550. if( m_GHost->m_MaxDownloaders > 0 && Downloaders > m_GHost->m_MaxDownloaders )
  551. break;
  552. // send up to 100 pieces of the map at once so that the download goes faster
  553. // if we wait for each MAPPART packet to be acknowledged by the client it'll take a long time to download
  554. // this is because we would have to wait the round trip time (the ping time) between sending every 1442 bytes of map data
  555. // doing it this way allows us to send at least 140 KB in each round trip interval which is much more reasonable
  556. // the theoretical throughput is [140 KB * 1000 / ping] in KB/sec so someone with 100 ping (round trip ping, not LC ping) could download at 1400 KB/sec
  557. // note: this creates a queue of map data which clogs up the connection when the client is on a slower connection (e.g. dialup)
  558. // in this case any changes to the lobby are delayed by the amount of time it takes to send the queued data (i.e. 140 KB, which could be 30 seconds or more)
  559. // for example, players joining and leaving, slot changes, chat messages would all appear to happen much later for the low bandwidth player
  560. // note: the throughput is also limited by the number of times this code is executed each second
  561. // e.g. if we send the maximum amount (140 KB) 10 times per second the theoretical throughput is 1400 KB/sec
  562. // therefore the maximum throughput is 1400 KB/sec regardless of ping and this value slowly diminishes as the player's ping increases
  563. // in addition to this, the throughput is limited by the configuration value bot_maxdownloadspeed
  564. // in summary: the actual throughput is MIN( 140 * 1000 / ping, 1400, bot_maxdownloadspeed ) in KB/sec assuming only one player is downloading the map
  565. uint32_t MapSize = UTIL_ByteArrayToUInt32( m_Map->GetMapSize( ), false );
  566. while( (*i)->GetLastMapPartSent( ) < (*i)->GetLastMapPartAcked( ) + 1442 * 100 && (*i)->GetLastMapPartSent( ) < MapSize )
  567. {
  568. if( (*i)->GetLastMapPartSent( ) == 0 )
  569. {
  570. // overwrite the "started download ticks" since this is the first time we've sent any map data to the player
  571. // prior to this we've only determined if the player needs to download the map but it's possible we could have delayed sending any data due to download limits
  572. (*i)->SetStartedDownloadingTicks( GetTicks( ) );
  573. }
  574. // limit the download speed if we're sending too much data
  575. // the download counter is the # of map bytes downloaded in the last second (it's reset once per second)
  576. if( m_GHost->m_MaxDownloadSpeed > 0 && m_DownloadCounter > m_GHost->m_MaxDownloadSpeed * 1024 )
  577. break;
  578. Send( *i, m_Protocol->SEND_W3GS_MAPPART( GetHostPID( ), (*i)->GetPID( ), (*i)->GetLastMapPartSent( ), m_Map->GetMapData( ) ) );
  579. (*i)->SetLastMapPartSent( (*i)->GetLastMapPartSent( ) + 1442 );
  580. m_DownloadCounter += 1442;
  581. }
  582. }
  583. }
  584. m_LastDownloadTicks = GetTicks( );
  585. }
  586. // announce every m_AnnounceInterval seconds
  587. if( !m_AnnounceMessage.empty( ) && !m_CountDownStarted && GetTime( ) - m_LastAnnounceTime >= m_AnnounceInterval )
  588. {
  589. SendAllChat( m_AnnounceMessage );
  590. m_LastAnnounceTime = GetTime( );
  591. }
  592. // kick players who don't spoof check within 20 seconds when spoof checks are required and the game is autohosted
  593. if( !m_CountDownStarted && m_GHost->m_RequireSpoofChecks && m_GameState == GAME_PUBLIC && !m_GHost->m_AutoHostGameName.empty( ) && m_GHost->m_AutoHostMaximumGames != 0 && m_GHost->m_AutoHostAutoStartPlayers != 0 && m_AutoStartPlayers != 0 )
  594. {
  595. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  596. {
  597. if( !(*i)->GetSpoofed( ) && GetTime( ) - (*i)->GetJoinTime( ) >= 20 )
  598. {
  599. (*i)->SetDeleteMe( true );
  600. (*i)->SetLeftReason( m_GHost->m_Language->WasKickedForNotSpoofChecking( ) );
  601. (*i)->SetLeftCode( PLAYERLEAVE_LOBBY );
  602. OpenSlot( GetSIDFromPID( (*i)->GetPID( ) ), false );
  603. }
  604. }
  605. }
  606. // try to auto start every 5 sec (!start) or 10 sec (!autostart)
  607. if( m_UsingStart == true )
  608. {
  609. if( !m_CountDownStarted && m_AutoStartPlayers != 0 && GetTime( ) - m_LastAutoStartTime >= 5 )
  610. {
  611. StartCountDownAuto( m_GHost->m_RequireSpoofChecks );
  612. m_LastAutoStartTime = GetTime( );
  613. }
  614. }
  615. else
  616. if( !m_CountDownStarted && m_AutoStartPlayers != 0 && GetTime( ) - m_LastAutoStartTime >= 10 )
  617. {
  618. StartCountDownAuto( m_GHost->m_RequireSpoofChecks );
  619. m_LastAutoStartTime = GetTime( );
  620. }
  621. // normal countdown if active every 1200 ms (because we need an extra sec for 0 )
  622. if ( m_GHost->m_UseNormalCountDown )
  623. {
  624. if( m_CountDownStarted && GetTicks( ) >= m_LastCountDownTicks + 1200 )
  625. {
  626. if( m_CountDownCounter > 0 )
  627. {
  628. --m_CountDownCounter;
  629. }
  630. else if( !m_GameLoading && !m_GameLoaded )
  631. {
  632. EventGameStarted( );
  633. }
  634. m_LastCountDownTicks = GetTicks( );
  635. }
  636. }
  637. else
  638. {
  639. // countdown (ghost style) every 500 ms
  640. if( m_CountDownStarted && GetTicks( ) >= m_LastCountDownTicks + 500 )
  641. {
  642. if( m_CountDownCounter > 0 )
  643. {
  644. // we use a countdown counter rather than a "finish countdown time" here because it might alternately round up or down the count
  645. // this sometimes resulted in a countdown of e.g. "6 5 3 2 1" during my testing which looks pretty dumb
  646. // doing it this way ensures it's always "5 4 3 2 1" but each interval might not be *exactly* the same length
  647. SendAllChat( UTIL_ToString( m_CountDownCounter ) + ". . ." );
  648. m_CountDownCounter--;
  649. }
  650. else if( !m_GameLoading && !m_GameLoaded )
  651. EventGameStarted( );
  652. m_LastCountDownTicks = GetTicks( );
  653. }
  654. }
  655. // check if the lobby is "abandoned" and needs to be closed since it will never start
  656. if( !m_GameLoading && !m_GameLoaded && m_AutoStartPlayers == 0 && m_GHost->m_LobbyTimeLimit > 0 )
  657. {
  658. // check if there's a player with reserved status in the game
  659. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  660. {
  661. if( (*i)->GetReserved( ) )
  662. m_LastReservedSeen = GetTime( );
  663. }
  664. // check if we've hit the time limit
  665. if( GetTime( ) - m_LastReservedSeen >= m_GHost->m_LobbyTimeLimit * 60 )
  666. {
  667. CONSOLE_Print( "[GAME: " + m_GameName + "] is over (lobby time limit hit)" );
  668. return true;
  669. }
  670. }
  671. // check if the game is loaded
  672. if( m_GameLoading )
  673. {
  674. bool FinishedLoading = true;
  675. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  676. {
  677. FinishedLoading = (*i)->GetFinishedLoading( );
  678. if( !FinishedLoading )
  679. break;
  680. }
  681. if( FinishedLoading )
  682. {
  683. m_LastActionSentTicks = GetTicks( );
  684. m_GameLoading = false;
  685. m_GameLoaded = true;
  686. EventGameLoaded( );
  687. }
  688. else
  689. {
  690. // reset the "lag" screen (the load-in-game screen) every 30 seconds
  691. if( m_LoadInGame && GetTime( ) - m_LastLagScreenResetTime >= 30 )
  692. {
  693. bool UsingGProxy = false;
  694. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  695. {
  696. if( (*i)->GetGProxy( ) )
  697. UsingGProxy = true;
  698. }
  699. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  700. {
  701. if( (*i)->GetFinishedLoading( ) )
  702. {
  703. // stop the lag screen
  704. for( vector<CGamePlayer *> :: iterator j = m_Players.begin( ); j != m_Players.end( ); ++j )
  705. {
  706. if( !(*j)->GetFinishedLoading( ) )
  707. Send( *i, m_Protocol->SEND_W3GS_STOP_LAG( *j, true ) );
  708. }
  709. // send an empty update
  710. // this resets the lag screen timer but creates a rather annoying problem
  711. // in order to prevent a desync we must make sure every player receives the exact same "desyncable game data" (updates and player leaves) in the exact same order
  712. // unfortunately we cannot send updates to players who are still loading the map, so we buffer the updates to those players (see the else clause a few lines down for the code)
  713. // in addition to this we must ensure any player leave messages are sent in the exact same position relative to these updates so those must be buffered too
  714. if( UsingGProxy && !(*i)->GetGProxy( ) )
  715. {
  716. // we must send empty actions to non-GProxy++ players
  717. // GProxy++ will insert these itself so we don't need to send them to GProxy++ players
  718. // empty actions are used to extend the time a player can use when reconnecting
  719. for( unsigned char j = 0; j < m_GProxyEmptyActions; ++j )
  720. Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
  721. }
  722. Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
  723. // start the lag screen
  724. Send( *i, m_Protocol->SEND_W3GS_START_LAG( m_Players, true ) );
  725. }
  726. else
  727. {
  728. // buffer the empty update since the player is still loading the map
  729. if( UsingGProxy && !(*i)->GetGProxy( ) )
  730. {
  731. // we must send empty actions to non-GProxy++ players
  732. // GProxy++ will insert these itself so we don't need to send them to GProxy++ players
  733. // empty actions are used to extend the time a player can use when reconnecting
  734. for( unsigned char j = 0; j < m_GProxyEmptyActions; ++j )
  735. (*i)->AddLoadInGameData( m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
  736. }
  737. (*i)->AddLoadInGameData( m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
  738. }
  739. }
  740. // add actions to replay
  741. if( m_Replay )
  742. {
  743. if( UsingGProxy )
  744. {
  745. for( unsigned char i = 0; i < m_GProxyEmptyActions; ++i )
  746. m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
  747. }
  748. m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
  749. }
  750. // Warcraft III doesn't seem to respond to empty actions
  751. /* if( UsingGProxy )
  752. m_SyncCounter += m_GProxyEmptyActions;
  753. m_SyncCounter++; */
  754. m_LastLagScreenResetTime = GetTime( );
  755. }
  756. }
  757. }
  758. // keep track of the largest sync counter (the number of keepalive packets received by each player)
  759. // if anyone falls behind by more than m_SyncLimit keepalives we start the lag screen
  760. if( m_GameLoaded )
  761. {
  762. // check if anyone has started lagging
  763. // we consider a player to have started lagging if they're more than m_SyncLimit keepalives behind
  764. if( !m_Lagging )
  765. {
  766. string LaggingString;
  767. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  768. {
  769. if( m_SyncCounter - (*i)->GetSyncCounter( ) > m_SyncLimit )
  770. {
  771. (*i)->SetLagging( true );
  772. (*i)->SetStartedLaggingTicks( GetTicks( ) );
  773. m_Lagging = true;
  774. m_StartedLaggingTime = GetTime( );
  775. if( LaggingString.empty( ) )
  776. LaggingString = (*i)->GetName( );
  777. else
  778. LaggingString += ", " + (*i)->GetName( );
  779. }
  780. }
  781. if( m_Lagging )
  782. {
  783. // start the lag screen
  784. CONSOLE_Print( "[GAME: " + m_GameName + "] started lagging on [" + LaggingString + "]" );
  785. SendAll( m_Protocol->SEND_W3GS_START_LAG( m_Players ) );
  786. // reset everyone's drop vote
  787. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  788. (*i)->SetDropVote( false );
  789. m_LastLagScreenResetTime = GetTime( );
  790. }
  791. }
  792. if( m_Lagging )
  793. {
  794. bool UsingGProxy = false;
  795. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  796. {
  797. if( (*i)->GetGProxy( ) )
  798. UsingGProxy = true;
  799. }
  800. uint32_t WaitTime = 60;
  801. if( UsingGProxy )
  802. WaitTime = ( m_GProxyEmptyActions + 1 ) * 60;
  803. if( GetTime( ) - m_StartedLaggingTime >= WaitTime )
  804. StopLaggers( m_GHost->m_Language->WasAutomaticallyDroppedAfterSeconds( UTIL_ToString( WaitTime ) ) );
  805. // we cannot allow the lag screen to stay up for more than ~65 seconds because Warcraft III disconnects if it doesn't receive an action packet at least this often
  806. // one (easy) solution is to simply drop all the laggers if they lag for more than 60 seconds
  807. // another solution is to reset the lag screen the same way we reset it when using load-in-game
  808. // this is required in order to give GProxy++ clients more time to reconnect
  809. if( GetTime( ) - m_LastLagScreenResetTime >= 60 )
  810. {
  811. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  812. {
  813. // stop the lag screen
  814. for( vector<CGamePlayer *> :: iterator j = m_Players.begin( ); j != m_Players.end( ); ++j )
  815. {
  816. if( (*j)->GetLagging( ) )
  817. Send( *i, m_Protocol->SEND_W3GS_STOP_LAG( *j ) );
  818. }
  819. // send an empty update
  820. // this resets the lag screen timer
  821. if( UsingGProxy && !(*i)->GetGProxy( ) )
  822. {
  823. // we must send additional empty actions to non-GProxy++ players
  824. // GProxy++ will insert these itself so we don't need to send them to GProxy++ players
  825. // empty actions are used to extend the time a player can use when reconnecting
  826. for( unsigned char j = 0; j < m_GProxyEmptyActions; ++j )
  827. Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
  828. }
  829. Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
  830. // start the lag screen
  831. Send( *i, m_Protocol->SEND_W3GS_START_LAG( m_Players ) );
  832. }
  833. // add actions to replay
  834. if( m_Replay )
  835. {
  836. if( UsingGProxy )
  837. {
  838. for( unsigned char i = 0; i < m_GProxyEmptyActions; ++i )
  839. m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
  840. }
  841. m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
  842. }
  843. // Warcraft III doesn't seem to respond to empty actions
  844. /* if( UsingGProxy )
  845. m_SyncCounter += m_GProxyEmptyActions;
  846. m_SyncCounter++; */
  847. m_LastLagScreenResetTime = GetTime( );
  848. }
  849. // check if anyone has stopped lagging normally
  850. // we consider a player to have stopped lagging if they're less than half m_SyncLimit keepalives behind
  851. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  852. {
  853. if( (*i)->GetLagging( ) && m_SyncCounter - (*i)->GetSyncCounter( ) < m_SyncLimit / 2 )
  854. {
  855. // stop the lag screen for this player
  856. CONSOLE_Print( "[GAME: " + m_GameName + "] stopped lagging on [" + (*i)->GetName( ) + "]" );
  857. SendAll( m_Protocol->SEND_W3GS_STOP_LAG( *i ) );
  858. (*i)->SetLagging( false );
  859. (*i)->SetStartedLaggingTicks( 0 );
  860. }
  861. }
  862. // check if everyone has stopped lagging
  863. bool Lagging = false;
  864. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  865. {
  866. if( (*i)->GetLagging( ) )
  867. Lagging = true;
  868. }
  869. m_Lagging = Lagging;
  870. // reset m_LastActionSentTicks because we want the game to stop running while the lag screen is up
  871. m_LastActionSentTicks = GetTicks( );
  872. // keep track of the last lag screen time so we can avoid timing out players
  873. m_LastLagScreenTime = GetTime( );
  874. }
  875. }
  876. // send actions every m_Latency milliseconds
  877. // actions are at the heart of every Warcraft 3 game but luckily we don't need to know their contents to relay them
  878. // we queue player actions in EventPlayerAction then just resend them in batches to all players here
  879. if( m_GameLoaded && !m_Lagging && GetTicks( ) - m_LastActionSentTicks >= m_Latency - m_LastActionLateBy )
  880. SendAllActions( );
  881. // expire the votekick
  882. if( !m_KickVotePlayer.empty( ) && GetTime( ) - m_StartedKickVoteTime >= 60 )
  883. {
  884. CONSOLE_Print( "[GAME: " + m_GameName + "] votekick against player [" + m_KickVotePlayer + "] expired" );
  885. SendAllChat( m_GHost->m_Language->VoteKickExpired( m_KickVotePlayer ) );
  886. m_KickVotePlayer.clear( );
  887. m_StartedKickVoteTime = 0;
  888. }
  889. // start the gameover timer if there's only one player left
  890. if( m_Players.size( ) == 1 && m_FakePlayerPID == 255 && m_GameOverTime == 0 && ( m_GameLoading || m_GameLoaded ) )
  891. {
  892. CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (one player left)" );
  893. m_GameOverTime = GetTime( );
  894. }
  895. // finish the gameover timer
  896. if( m_GameOverTime != 0 && GetTime( ) - m_GameOverTime >= 60 )
  897. {
  898. bool AlreadyStopped = true;
  899. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  900. {
  901. if( !(*i)->GetDeleteMe( ) )
  902. {
  903. AlreadyStopped = false;
  904. break;
  905. }
  906. }
  907. if( !AlreadyStopped )
  908. {
  909. CONSOLE_Print( "[GAME: " + m_GameName + "] is over (gameover timer finished)" );
  910. StopPlayers( "was disconnected (gameover timer finished)" );
  911. }
  912. }
  913. // end the game if there aren't any players left
  914. if( m_Players.empty( ) && ( m_GameLoading || m_GameLoaded ) )
  915. {
  916. if( !m_Saving )
  917. {
  918. CONSOLE_Print( "[GAME: " + m_GameName + "] is over (no players left)" );
  919. SaveGameData( );
  920. m_Saving = true;
  921. }
  922. else if( IsGameDataSaved( ) )
  923. return true;
  924. }
  925. // accept new connections
  926. if( m_Socket )
  927. {
  928. CTCPSocket *NewSocket = m_Socket->Accept( (fd_set *)fd );
  929. if( NewSocket )
  930. {
  931. // check the IP blacklist
  932. if( m_IPBlackList.find( NewSocket->GetIPString( ) ) == m_IPBlackList.end( ) )
  933. {
  934. if( m_GHost->m_TCPNoDelay )
  935. NewSocket->SetNoDelay( true );
  936. m_Potentials.push_back( new CPotentialPlayer( m_Protocol, this, NewSocket ) );
  937. }
  938. else
  939. {
  940. CONSOLE_Print( "[GAME: " + m_GameName + "] rejected connection from [" + NewSocket->GetIPString( ) + "] due to blacklist" );
  941. delete NewSocket;
  942. }
  943. }
  944. if( m_Socket->HasError( ) )
  945. return true;
  946. }
  947. // update time in ui
  948. if( GetTime( ) > m_LastUiTime )
  949. {
  950. vector<string> MapData;
  951. MapData.push_back( "Time" );
  952. if( m_GameLoading || m_GameLoaded )
  953. MapData.push_back( UTIL_ToString( ( m_GameTicks / 1000 ) / 60 ) + " minutes" );
  954. else
  955. MapData.push_back( UTIL_ToString( ( GetTime( ) - m_CreationTime ) / 60 ) + " minutes" );
  956. forward( new CFwdData( FWD_GAME_MAP_INFO_UPDATE, MapData, m_GameID ) );
  957. m_LastUiTime = GetTime( ) + 60;
  958. }
  959. // update players in ui
  960. if( GetTime( ) > m_LastUiSlotsTime )
  961. {
  962. vector<string> playerData;
  963. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  964. {
  965. playerData.clear( );
  966. playerData.push_back( (*i)->GetName( ) ); // name
  967. playerData.push_back( UTIL_ToString( GetSIDFromPID( (*i)->GetPID( ) ) + 1 ) ); // slot
  968. playerData.push_back( m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( (*i)->GetExternalIP( ), true ) ) ); // from
  969. playerData.push_back( UTIL_ToString( (*i)->GetPing(true) ) + " ms" ); // ping
  970. playerData.push_back( m_Slots[ GetSIDFromPID( (*i)->GetPID( ) ) ].GetRace( ) == 1 ? "HU" :
  971. m_Slots[ GetSIDFromPID( (*i)->GetPID( ) ) ].GetRace( ) == 2 ? "ORC" :
  972. m_Slots[ GetSIDFromPID( (*i)->GetPID( ) ) ].GetRace( ) == 4 ? "NE" :
  973. m_Slots[ GetSIDFromPID( (*i)->GetPID( ) ) ].GetRace( ) == 8 ? "UD" : "??" ); // race
  974. playerData.push_back( UTIL_ToString( m_Slots[GetSIDFromPID( (*i)->GetPID( ) )].GetTeam( ) + 1 ) ); // team
  975. playerData.push_back( ColourValueToString( m_Slots[GetSIDFromPID( (*i)->GetPID( ) )].GetColour( ) ) ); // color
  976. playerData.push_back( UTIL_ToString( m_Slots[GetSIDFromPID( (*i)->GetPID( ) )].GetHandicap( ) ) ); // handicap
  977. playerData.push_back( (*i)->GetGProxy( ) ? "on" : "off" ); // gproxy++
  978. forward( new CFwdData( FWD_GAME_SLOT_UPDATE, playerData, m_GameID ) );
  979. }
  980. m_LastUiSlotsTime = GetTime( ) + 10;
  981. }
  982. return m_Exiting;
  983. }
  984. void CBaseGame :: UpdatePost( void *send_fd )
  985. {
  986. // we need to manually call DoSend on each player now because CGamePlayer :: Update doesn't do it
  987. // this is in case player 2 generates a packet for player 1 during the update but it doesn't get sent because player 1 already finished updating
  988. // in reality since we're queueing actions it might not make a big difference but oh well
  989. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  990. {
  991. if( (*i)->GetSocket( ) )
  992. (*i)->GetSocket( )->DoSend( (fd_set *)send_fd );
  993. }
  994. for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); ++i )
  995. {
  996. if( (*i)->GetSocket( ) )
  997. (*i)->GetSocket( )->DoSend( (fd_set *)send_fd );
  998. }
  999. }
  1000. void CBaseGame :: Send( CGamePlayer *player, BYTEARRAY data )
  1001. {
  1002. if( player )
  1003. player->Send( data );
  1004. }
  1005. void CBaseGame :: Send( unsigned char PID, BYTEARRAY data )
  1006. {
  1007. Send( GetPlayerFromPID( PID ), data );
  1008. }
  1009. void CBaseGame :: Send( BYTEARRAY PIDs, BYTEARRAY data )
  1010. {
  1011. for( unsigned int i = 0; i < PIDs.size( ); ++i )
  1012. Send( PIDs[i], data );
  1013. }
  1014. void CBaseGame :: SendAll( BYTEARRAY data )
  1015. {
  1016. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  1017. (*i)->Send( data );
  1018. }
  1019. void CBaseGame :: SendChat( unsigned char fromPID, CGamePlayer *player, string message )
  1020. {
  1021. // send a private message to one player - it'll be marked [Private] in Warcraft 3
  1022. if( player )
  1023. {
  1024. if( !m_GameLoading && !m_GameLoaded )
  1025. {
  1026. if( message.size( ) > 254 )
  1027. message = message.substr( 0, 254 );
  1028. Send( player, m_Protocol->SEND_W3GS_CHAT_FROM_HOST( fromPID, UTIL_CreateByteArray( player->GetPID( ) ), 16, BYTEARRAY( ), message ) );
  1029. }
  1030. else
  1031. {
  1032. unsigned char ExtraFlags[] = { 3, 0, 0, 0 };
  1033. // based on my limited testing it seems that the extra flags' first byte contains 3 plus the recipient's colour to denote a private message
  1034. unsigned char SID = GetSIDFromPID( player->GetPID( ) );
  1035. if( SID < m_Slots.size( ) )
  1036. ExtraFlags[0] = 3 + m_Slots[SID].GetColour( );
  1037. if( message.size( ) > 127 )
  1038. message = message.substr( 0, 127 );
  1039. Send( player, m_Protocol->SEND_W3GS_CHAT_FROM_HOST( fromPID, UTIL_CreateByteArray( player->GetPID( ) ), 32, UTIL_CreateByteArray( ExtraFlags, 4 ), message ) );
  1040. }
  1041. }
  1042. }
  1043. void CBaseGame :: SendChat( unsigned char fromPID, unsigned char toPID, string message )
  1044. {
  1045. SendChat( fromPID, GetPlayerFromPID( toPID ), message );
  1046. }
  1047. void CBaseGame :: SendChat( CGamePlayer *player, string message )
  1048. {
  1049. SendChat( GetHostPID( ), player, message );
  1050. }
  1051. void CBaseGame :: SendChat( unsigned char toPID, string message )
  1052. {
  1053. SendChat( GetHostPID( ), toPID, message );
  1054. }
  1055. void CBaseGame :: SendAllChat( unsigned char fromPID, string message )
  1056. {
  1057. // bot command
  1058. if( !message.empty( ) && message[0] == m_GHost->m_CommandTrigger )
  1059. {
  1060. // extract the command trigger, the command, and the payload
  1061. // e.g. "!say hello world" -> command: "say", payload: "hello world"
  1062. string Command;
  1063. string Payload;
  1064. string :: size_type PayloadStart = message.find( " " );
  1065. if( PayloadStart != string :: npos )
  1066. {
  1067. Command = message.substr( 1, PayloadStart - 1 );
  1068. Payload = message.substr( PayloadStart + 1 );
  1069. }
  1070. else
  1071. Command = message.substr( 1 );
  1072. transform( Command.begin( ), Command.end( ), Command.begin( ), (int(*)(int))tolower );
  1073. BYTEARRAY temp;
  1074. CGamePlayer *Player = new CGamePlayer(0, 0, 0, fromPID, m_CreatorServer, m_CreatorName, temp, false);
  1075. Player->SetSpoofed(true);
  1076. Player->SetSpoofedRealm(m_CreatorServer);
  1077. EventPlayerBotCommand( Player, Command, Payload );
  1078. delete Player;
  1079. return; // Don't show the message.
  1080. }
  1081. forward( new CFwdData( FWD_GAME_CHAT, "ADMIN:\3", 1, m_GameID ) );
  1082. forward( new CFwdData( FWD_GAME_CHAT, message, 0, m_GameID ) );
  1083. // send a public message to all players - it'll be marked [All] in Warcraft 3
  1084. if( GetNumHumanPlayers( ) > 0 )
  1085. {
  1086. CONSOLE_Print( "[GAME: " + m_GameName + "] [Local]: " + message );
  1087. if( !m_GameLoading && !m_GameLoaded )
  1088. {
  1089. if( message.size( ) > 254 )
  1090. message = message.substr( 0, 254 );
  1091. SendAll( m_Protocol->SEND_W3GS_CHAT_FROM_HOST( fromPID, GetPIDs( ), 16, BYTEARRAY( ), message ) );
  1092. }
  1093. else
  1094. {
  1095. if( message.size( ) > 127 )
  1096. message = message.substr( 0, 127 );
  1097. SendAll( m_Protocol->SEND_W3GS_CHAT_FROM_HOST( fromPID, GetPIDs( ), 32, UTIL_CreateByteArray( (uint32_t)0, false ), message ) );
  1098. if( m_Replay )
  1099. m_Replay->AddChatMessage( fromPID, 32, 0, message );
  1100. }
  1101. }
  1102. }
  1103. void CBaseGame :: SendAllChat( string message )
  1104. {
  1105. SendAllChat( GetHostPID( ), message );
  1106. }
  1107. void CBaseGame :: SendLocalAdminChat( string message )
  1108. {
  1109. if( !m_LocalAdminMessages )
  1110. return;
  1111. // send a message to LAN/local players who are admins
  1112. // at the time of this writing it is only possible for the game owner to meet this criteria because being an admin requires spoof checking
  1113. // this is mainly used for relaying battle.net whispers, chat messages, and emotes to these players
  1114. for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
  1115. {
  1116. if( (*i)->GetSpoofed( ) && IsOwner( (*i)->GetName( ) ) && ( UTIL_IsLanIP( (*i)->GetExternalIP( ) ) || UTIL_IsLocalIP( (*i)->GetExternalIP( ), m_GHost->m_LocalAddresses ) ) )
  1117. {
  1118. if( m_VirtualHostPID != 255 )
  1119. SendChat( m_VirtualHostPID, *i, message );
  1120. else
  1121. {
  1122. // make the chat message originate from the recipient since it's not going to be logged to the replay
  1123. SendChat( (*i)->GetPID( ), *i, message );
  1124. }
  1125. }
  1126. }
  1127. }
  1128. void CBaseGame :: SendAllSlotInfo( )
  1129. {
  1130. if( !m_GameLoading && !m_GameLoaded )
  1131. {
  1132. SendAll( m_Protocol->SEND_W3GS_SLOTINFO( m_Slots, m_RandomSeed, m_Map->GetMapLayoutStyle( ), m_Map->GetMapNumPlayers( ) ) );
  1133. m_SlotInfoChanged = false;
  1134. }
  1135. }
  1136. void CBaseGame :: SendVirtualHostPlayerInfo( CGamePlayer *player )
  1137. {
  1138. if( m_Virt