PageRenderTime 150ms CodeModel.GetById 38ms app.highlight 92ms RepoModel.GetById 1ms app.codeStats 2ms

/ghost/game_base.cpp

http://ghostcb.googlecode.com/
C++ | 5237 lines | 3908 code | 874 blank | 455 comment | 1032 complexity | cf655cf2ef0b8f397572278fd51a8654 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

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

Large files files are truncated, but you can click here to view the full file