PageRenderTime 132ms CodeModel.GetById 19ms app.highlight 101ms RepoModel.GetById 1ms app.codeStats 0ms

/ghost/game.cpp

http://ghostcb.googlecode.com/
C++ | 2268 lines | 1737 code | 299 blank | 232 comment | 594 complexity | 9d4e701e4a6df370f0491d91f9837c3a MD5 | raw file

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#include "ghost.h"
  22#include "util.h"
  23#include "config.h"
  24#include "language.h"
  25#include "socket.h"
  26#include "ghostdb.h"
  27#include "bnet.h"
  28#include "map.h"
  29#include "packed.h"
  30#include "savegame.h"
  31#include "gameplayer.h"
  32#include "gameprotocol.h"
  33#include "game_base.h"
  34#include "game.h"
  35#include "stats.h"
  36#include "statsdota.h"
  37#include "statsw3mmd.h"
  38#include "ui/forward.h"
  39
  40#include <cmath>
  41#include <string.h>
  42#include <time.h>
  43
  44//
  45// sorting classes
  46//
  47
  48class CGamePlayerSortAscByPing
  49{
  50public:
  51	bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
  52	{
  53		return Player1->GetPing( false ) < Player2->GetPing( false );
  54	}
  55};
  56
  57class CGamePlayerSortDescByPing
  58{
  59public:
  60	bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
  61	{
  62		return Player1->GetPing( false ) > Player2->GetPing( false );
  63	}
  64};
  65
  66//
  67// CGame
  68//
  69
  70CGame :: CGame( CGHost *nGHost, CMap *nMap, CSaveGame *nSaveGame, uint16_t nHostPort, unsigned char nGameState, string nGameName, string nOwnerName, string nCreatorName, string nCreatorServer ) : CBaseGame( nGHost, nMap, nSaveGame, nHostPort, nGameState, nGameName, nOwnerName, nCreatorName, nCreatorServer ), m_DBBanLast( NULL ), m_CallableGameAdd( NULL )
  71{
  72    m_DBGame = new CDBGame( 0, string( ), m_Map->GetMapPath( ), string( ), string( ), string( ), 0 );
  73
  74	if( m_Map->GetMapType( ) == "w3mmd" )
  75		m_Stats = new CStatsW3MMD( this, m_Map->GetMapStatsW3MMDCategory( ) );
  76	else if( m_Map->GetMapType( ) == "dota" )
  77		m_Stats = new CStatsDOTA( this );
  78	else
  79		m_Stats = NULL;
  80}
  81
  82CGame :: ~CGame( )
  83{
  84	if( m_CallableGameAdd && m_CallableGameAdd->GetReady( ) )
  85	{
  86		if( m_CallableGameAdd->GetResult( ) > 0 )
  87		{
  88			CONSOLE_Print( "[GAME: " + m_GameName + "] saving player/stats data to database" );
  89			forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Saving player/stats data to database", 0, 0));
  90
  91			// store the CDBGamePlayers in the database
  92
  93			for( vector<CDBGamePlayer *> :: iterator i = m_DBGamePlayers.begin( ); i != m_DBGamePlayers.end( ); ++i )
  94				m_GHost->m_Callables.push_back( m_GHost->m_DB->ThreadedGamePlayerAdd( m_CallableGameAdd->GetResult( ), (*i)->GetName( ), (*i)->GetIP( ), (*i)->GetSpoofed( ), (*i)->GetSpoofedRealm( ), (*i)->GetReserved( ), (*i)->GetLoadingTime( ), (*i)->GetLeft( ), (*i)->GetLeftReason( ), (*i)->GetTeam( ), (*i)->GetColour( ) ) );
  95
  96			// store the stats in the database
  97
  98			if( m_Stats )
  99				m_Stats->Save( m_GHost, m_GHost->m_DB, m_CallableGameAdd->GetResult( ) );
 100		}
 101		else
 102		{
 103			CONSOLE_Print( "[GAME: " + m_GameName + "] unable to save player/stats data to database" );
 104			forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Unable to save player/stats data to database", 0, 0));
 105		}
 106
 107		m_GHost->m_DB->RecoverCallable( m_CallableGameAdd );
 108		delete m_CallableGameAdd;
 109		m_CallableGameAdd = NULL;
 110	}
 111
 112        for( vector<PairedBanCheck> :: iterator i = m_PairedBanChecks.begin( ); i != m_PairedBanChecks.end( ); ++i )
 113		m_GHost->m_Callables.push_back( i->second );
 114
 115        for( vector<PairedBanAdd> :: iterator i = m_PairedBanAdds.begin( ); i != m_PairedBanAdds.end( ); ++i )
 116		m_GHost->m_Callables.push_back( i->second );
 117
 118        for( vector<PairedGPSCheck> :: iterator i = m_PairedGPSChecks.begin( ); i != m_PairedGPSChecks.end( ); ++i )
 119		m_GHost->m_Callables.push_back( i->second );
 120
 121        for( vector<PairedDPSCheck> :: iterator i = m_PairedDPSChecks.begin( ); i != m_PairedDPSChecks.end( ); ++i )
 122		m_GHost->m_Callables.push_back( i->second );
 123
 124        for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); ++i )
 125		delete *i;
 126
 127	delete m_DBGame;
 128
 129        for( vector<CDBGamePlayer *> :: iterator i = m_DBGamePlayers.begin( ); i != m_DBGamePlayers.end( ); ++i )
 130		delete *i;
 131
 132	delete m_Stats;
 133
 134	// it's a "bad thing" if m_CallableGameAdd is non NULL here
 135	// it means the game is being deleted after m_CallableGameAdd was created (the first step to saving the game data) but before the associated thread terminated
 136	// rather than failing horribly we choose to allow the thread to complete in the orphaned callables list but step 2 will never be completed
 137	// so this will create a game entry in the database without any gameplayers and/or DotA stats
 138
 139	if( m_CallableGameAdd )
 140	{
 141		CONSOLE_Print( "[GAME: " + m_GameName + "] game is being deleted before all game data was saved, game data has been lost" );
 142		forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Game is being deleted before all game data was saved, game data has been lost", 0, 0));
 143		m_GHost->m_Callables.push_back( m_CallableGameAdd );
 144	}
 145}
 146
 147bool CGame :: Update( void *fd, void *send_fd )
 148{
 149	// update callables
 150
 151	for( vector<PairedBanCheck> :: iterator i = m_PairedBanChecks.begin( ); i != m_PairedBanChecks.end( ); )
 152	{
 153		if( i->second->GetReady( ) )
 154		{
 155			CDBBan *Ban = i->second->GetResult( );
 156
 157			if( Ban )
 158				SendAllChat( m_GHost->m_Language->UserWasBannedOnByBecause( i->second->GetServer( ), i->second->GetUser( ), Ban->GetDate( ), Ban->GetAdmin( ), Ban->GetReason( ) ) );
 159			else
 160				SendAllChat( m_GHost->m_Language->UserIsNotBanned( i->second->GetServer( ), i->second->GetUser( ) ) );
 161
 162			m_GHost->m_DB->RecoverCallable( i->second );
 163			delete i->second;
 164			i = m_PairedBanChecks.erase( i );
 165		}
 166		else
 167                        ++i;
 168	}
 169
 170	for( vector<PairedBanAdd> :: iterator i = m_PairedBanAdds.begin( ); i != m_PairedBanAdds.end( ); )
 171	{
 172		if( i->second->GetReady( ) )
 173		{
 174			if( i->second->GetResult( ) )
 175			{
 176				string alias = i->second->GetServer( );
 177				for( vector<CBNET *> :: iterator j = m_GHost->m_BNETs.begin( ); j != m_GHost->m_BNETs.end( ); ++j )
 178				{
 179					if( (*j)->GetServer( ) == i->second->GetServer( ) ) 
 180					{
 181						(*j)->AddBan( i->second->GetUser( ), i->second->GetIP( ), i->second->GetGameName( ), i->second->GetAdmin( ), i->second->GetReason( ) );
 182						alias = (*j)->GetServerAlias();
 183					}
 184				}
 185
 186				SendAllChat( m_GHost->m_Language->PlayerWasBannedByPlayer( alias, i->second->GetUser( ), i->first, i->second->GetIP( ), i->second->GetGameName( ), i->second->GetAdmin( ) ) );
 187			   SendAllChat( i->second->GetReason( ) );
 188			}
 189
 190			m_GHost->m_DB->RecoverCallable( i->second );
 191			delete i->second;
 192			i = m_PairedBanAdds.erase( i );
 193		}
 194		else
 195                        ++i;
 196	}
 197
 198	for( vector<PairedGPSCheck> :: iterator i = m_PairedGPSChecks.begin( ); i != m_PairedGPSChecks.end( ); )
 199	{
 200		if( i->second->GetReady( ) )
 201		{
 202			CDBGamePlayerSummary *GamePlayerSummary = i->second->GetResult( );
 203
 204			if( GamePlayerSummary )
 205			{
 206				if( i->first.empty( ) )
 207					SendAllChat( m_GHost->m_Language->HasPlayedGamesWithThisBot( i->second->GetName( ), GamePlayerSummary->GetFirstGameDateTime( ), GamePlayerSummary->GetLastGameDateTime( ), UTIL_ToString( GamePlayerSummary->GetTotalGames( ) ), UTIL_ToString( (float)GamePlayerSummary->GetAvgLoadingTime( ) / 1000, 2 ), UTIL_ToString( GamePlayerSummary->GetAvgLeftPercent( ) ) ) );
 208				else
 209				{
 210					CGamePlayer *Player = GetPlayerFromName( i->first, true );
 211
 212					if( Player )
 213						SendChat( Player, m_GHost->m_Language->HasPlayedGamesWithThisBot( i->second->GetName( ), GamePlayerSummary->GetFirstGameDateTime( ), GamePlayerSummary->GetLastGameDateTime( ), UTIL_ToString( GamePlayerSummary->GetTotalGames( ) ), UTIL_ToString( (float)GamePlayerSummary->GetAvgLoadingTime( ) / 1000, 2 ), UTIL_ToString( GamePlayerSummary->GetAvgLeftPercent( ) ) ) );
 214				}
 215			}
 216			else
 217			{
 218				if( i->first.empty( ) )
 219					SendAllChat( m_GHost->m_Language->HasntPlayedGamesWithThisBot( i->second->GetName( ) ) );
 220				else
 221				{
 222					CGamePlayer *Player = GetPlayerFromName( i->first, true );
 223
 224					if( Player )
 225						SendChat( Player, m_GHost->m_Language->HasntPlayedGamesWithThisBot( i->second->GetName( ) ) );
 226				}
 227			}
 228
 229			m_GHost->m_DB->RecoverCallable( i->second );
 230			delete i->second;
 231			i = m_PairedGPSChecks.erase( i );
 232		}
 233		else
 234                        ++i;
 235	}
 236
 237	for( vector<PairedDPSCheck> :: iterator i = m_PairedDPSChecks.begin( ); i != m_PairedDPSChecks.end( ); )
 238	{
 239		if( i->second->GetReady( ) )
 240		{
 241			CDBDotAPlayerSummary *DotAPlayerSummary = i->second->GetResult( );
 242
 243			if( DotAPlayerSummary )
 244			{
 245				string Summary = m_GHost->m_Language->HasPlayedDotAGamesWithThisBot(	i->second->GetName( ),
 246																						UTIL_ToString( DotAPlayerSummary->GetTotalGames( ) ),
 247																						UTIL_ToString( DotAPlayerSummary->GetTotalWins( ) ),
 248																						UTIL_ToString( DotAPlayerSummary->GetTotalLosses( ) ),
 249																						UTIL_ToString( DotAPlayerSummary->GetTotalKills( ) ),
 250																						UTIL_ToString( DotAPlayerSummary->GetTotalDeaths( ) ),
 251																						UTIL_ToString( DotAPlayerSummary->GetTotalCreepKills( ) ),
 252																						UTIL_ToString( DotAPlayerSummary->GetTotalCreepDenies( ) ),
 253																						UTIL_ToString( DotAPlayerSummary->GetTotalAssists( ) ),
 254																						UTIL_ToString( DotAPlayerSummary->GetTotalNeutralKills( ) ),
 255																						UTIL_ToString( DotAPlayerSummary->GetTotalTowerKills( ) ),
 256																						UTIL_ToString( DotAPlayerSummary->GetTotalRaxKills( ) ),
 257																						UTIL_ToString( DotAPlayerSummary->GetTotalCourierKills( ) ),
 258																						UTIL_ToString( DotAPlayerSummary->GetAvgKills( ), 2 ),
 259																						UTIL_ToString( DotAPlayerSummary->GetAvgDeaths( ), 2 ),
 260																						UTIL_ToString( DotAPlayerSummary->GetAvgCreepKills( ), 2 ),
 261																						UTIL_ToString( DotAPlayerSummary->GetAvgCreepDenies( ), 2 ),
 262																						UTIL_ToString( DotAPlayerSummary->GetAvgAssists( ), 2 ),
 263																						UTIL_ToString( DotAPlayerSummary->GetAvgNeutralKills( ), 2 ),
 264																						UTIL_ToString( DotAPlayerSummary->GetAvgTowerKills( ), 2 ),
 265																						UTIL_ToString( DotAPlayerSummary->GetAvgRaxKills( ), 2 ),
 266																						UTIL_ToString( DotAPlayerSummary->GetAvgCourierKills( ), 2 ) );
 267
 268				if( i->first.empty( ) )
 269					SendAllChat( Summary );
 270				else
 271				{
 272					CGamePlayer *Player = GetPlayerFromName( i->first, true );
 273
 274					if( Player )
 275						SendChat( Player, Summary );
 276				}
 277			}
 278			else
 279			{
 280				if( i->first.empty( ) )
 281					SendAllChat( m_GHost->m_Language->HasntPlayedDotAGamesWithThisBot( i->second->GetName( ) ) );
 282				else
 283				{
 284					CGamePlayer *Player = GetPlayerFromName( i->first, true );
 285
 286					if( Player )
 287						SendChat( Player, m_GHost->m_Language->HasntPlayedDotAGamesWithThisBot( i->second->GetName( ) ) );
 288				}
 289			}
 290
 291			m_GHost->m_DB->RecoverCallable( i->second );
 292			delete i->second;
 293			i = m_PairedDPSChecks.erase( i );
 294		}
 295		else
 296                        ++i;
 297	}
 298
 299	return CBaseGame :: Update( fd, send_fd );
 300}
 301
 302// this function is only called when a player leave packet is received, not when there's a socket error, kick, etc...
 303void CGame :: EventPlayerLeft( CGamePlayer *player, uint32_t reason )
 304{
 305	// Check if leaver is admin/root admin with a loop then set the bool accordingly.
 306	bool isAdmin = false;
 307	for( vector<CBNET *> :: iterator j = m_GHost->m_BNETs.begin( ); j != m_GHost->m_BNETs.end( ); ++j )
 308	{
 309		if( (*j)->IsAdmin(player->GetName( ) ) || (*j)->IsRootAdmin( player->GetName( )) ) 
 310		{
 311			isAdmin = true;
 312			break;
 313		}
 314	}
 315
 316	CBaseGame :: EventPlayerLeft( player, reason );
 317}
 318
 319void CGame :: EventPlayerDeleted( CGamePlayer *player )
 320{
 321	CBaseGame :: EventPlayerDeleted( player );
 322
 323	// record everything we need to know about the player for storing in the database later
 324	// since we haven't stored the game yet (it's not over yet!) we can't link the gameplayer to the game
 325	// see the destructor for where these CDBGamePlayers are stored in the database
 326	// we could have inserted an incomplete record on creation and updated it later but this makes for a cleaner interface
 327
 328	if( m_GameLoading || m_GameLoaded )
 329	{
 330		// todotodo: since we store players that crash during loading it's possible that the stats classes could have no information on them
 331		// that could result in a DBGamePlayer without a corresponding DBDotAPlayer - just be aware of the possibility
 332
 333		unsigned char SID = GetSIDFromPID( player->GetPID( ) );
 334		unsigned char Team = 255;
 335		unsigned char Colour = 255;
 336
 337		if( SID < m_Slots.size( ) )
 338		{
 339			Team = m_Slots[SID].GetTeam( );
 340			Colour = m_Slots[SID].GetColour( );
 341		}
 342
 343		m_DBGamePlayers.push_back( new CDBGamePlayer( 0, 0, player->GetName( ), player->GetExternalIPString( ), player->GetSpoofed( ) ? 1 : 0, player->GetSpoofedRealm( ), player->GetReserved( ) ? 1 : 0, player->GetFinishedLoading( ) ? player->GetFinishedLoadingTicks( ) - m_StartedLoadingTicks : 0, m_GameTicks / 1000, player->GetLeftReason( ), Team, Colour ) );
 344
 345		// also keep track of the last player to leave for the !banlast command
 346
 347                for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); ++i )
 348		{
 349			if( (*i)->GetName( ) == player->GetName( ) )
 350				m_DBBanLast = *i;
 351		}
 352	}
 353}
 354
 355void CGame :: EventPlayerAction( CGamePlayer *player, CIncomingAction *action )
 356{
 357	CBaseGame :: EventPlayerAction( player, action );
 358
 359	// give the stats class a chance to process the action
 360
 361	if( m_Stats && m_Stats->ProcessAction( action ) && m_GameOverTime == 0 )
 362	{
 363		CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (stats class reported game over)" );
 364		forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Gameover timer started (stats class reported game over)", 0, 0));
 365		SendEndMessage( );
 366		m_GameOverTime = GetTime( );
 367	}
 368}
 369
 370bool CGame :: EventPlayerBotCommand( CGamePlayer *player, string command, string payload )
 371{
 372	bool HideCommand = CBaseGame :: EventPlayerBotCommand( player, command, payload );
 373
 374	// todotodo: don't be lazy
 375
 376	string User = player->GetName( );
 377	string Command = command;
 378	string Payload = payload;
 379
 380	bool AdminCheck = false;
 381
 382        for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
 383	{
 384		if( (*i)->GetServer( ) == player->GetSpoofedRealm( ) && (*i)->IsAdmin( User ) )
 385		{
 386			AdminCheck = true;
 387			break;
 388		}
 389		//GHost++ Custom Build Addition start
 390		else if( ( UTIL_IsLanIP( player->GetExternalIP( ) ) || UTIL_IsLocalIP( player->GetExternalIP( ), m_GHost->m_LocalAddresses ) ) && m_GHost->m_LANAdmins != 0 )
 391		{
 392			if ( m_GHost->m_LANAdmins == 1 || m_GHost->m_LANAdmins == 3 )
 393				if ( ( m_GHost->m_GetLANRootAdmins == 1 && (*i)->IsLANRootAdmin( User ) ) || m_GHost->m_GetLANRootAdmins != 1 )
 394					AdminCheck = true;
 395			break;
 396		}
 397		//GHost++ Custom Build Addition end
 398	}
 399
 400	bool RootAdminCheck = false;
 401
 402        for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
 403	{
 404		if( (*i)->GetServer( ) == player->GetSpoofedRealm( ) && (*i)->IsRootAdmin( User ) )
 405		{
 406			RootAdminCheck = true;
 407			break;
 408		}
 409		//GHost++ Custom Build Addition start
 410		else if( ( UTIL_IsLanIP( player->GetExternalIP( ) ) || UTIL_IsLocalIP( player->GetExternalIP( ), m_GHost->m_LocalAddresses ) ) && m_GHost->m_LANAdmins != 0 )
 411		{
 412			if ( m_GHost->m_LANAdmins == 2 )
 413				if ( ( m_GHost->m_GetLANRootAdmins == 1 && (*i)->IsLANRootAdmin( User ) ) || m_GHost->m_GetLANRootAdmins != 1 )
 414					RootAdminCheck = true;
 415			if ( m_GHost->m_LANAdmins == 3 )
 416				if ( ( m_GHost->m_GetLANRootAdmins == 1 && (*i)->IsLANRootAdmin( User ) ) )
 417					RootAdminCheck = true;
 418			break;
 419		}
 420		//GHost++ Custom Build Addition end
 421	}
 422
 423	if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
 424	{
 425		CONSOLE_Print( "[GAME: " + m_GameName + "] admin [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]" );
 426		//forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Admin [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]", 0, 0));
 427
 428		if( !m_Locked || RootAdminCheck || IsOwner( User ) )
 429		{
 430			/*****************
 431			* ADMIN COMMANDS *
 432			******************/
 433
 434			//
 435			// !ABORT (abort countdown)
 436			// !A
 437			//
 438
 439			// we use "!a" as an alias for abort because you don't have much time to abort the countdown so it's useful for the abort command to be easy to type
 440
 441			if( ( Command == "abort" || Command == "a" ) && m_CountDownStarted && !m_GameLoading && !m_GameLoaded )
 442			{
 443				if ( m_GHost->m_HideCommands )
 444					HideCommand = true;
 445
 446				SendAllChat( m_GHost->m_Language->CountDownAborted( ) );
 447				m_CountDownStarted = false;
 448				m_AutoStartPlayers = 0;
 449				m_UsingStart = false;
 450			}
 451
 452			//
 453			// !ADDBAN
 454			// !BAN
 455			//
 456
 457			else if( ( Command == "addban" || Command == "ban" ) && !Payload.empty( ) && !m_GHost->m_BNETs.empty( ) )
 458			{
 459				if ( m_GHost->m_HideCommands )
 460					HideCommand = true;
 461
 462				// extract the victim and the reason
 463				// e.g. "Varlock leaver after dying" -> victim: "Varlock", reason: "leaver after dying"
 464
 465				string Victim;
 466				string Reason;
 467				stringstream SS;
 468				SS << Payload;
 469				SS >> Victim;
 470
 471				if( !SS.eof( ) )
 472				{
 473					getline( SS, Reason );
 474					string :: size_type Start = Reason.find_first_not_of( " " );
 475
 476					if( Start != string :: npos )
 477						Reason = Reason.substr( Start );
 478				}
 479
 480				if( m_GameLoaded )
 481				{
 482					string VictimLower = Victim;
 483					transform( VictimLower.begin( ), VictimLower.end( ), VictimLower.begin( ), (int(*)(int))tolower );
 484					uint32_t Matches = 0;
 485					CDBBan *LastMatch = NULL;
 486
 487					// try to match each player with the passed string (e.g. "Varlock" would be matched with "lock")
 488					// we use the m_DBBans vector for this in case the player already left and thus isn't in the m_Players vector anymore
 489
 490                                        for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); ++i )
 491					{
 492						string TestName = (*i)->GetName( );
 493						transform( TestName.begin( ), TestName.end( ), TestName.begin( ), (int(*)(int))tolower );
 494
 495						if( TestName.find( VictimLower ) != string :: npos )
 496						{
 497							Matches++;
 498							LastMatch = *i;
 499
 500							// if the name matches exactly stop any further matching
 501
 502							if( TestName == VictimLower )
 503							{
 504								Matches = 1;
 505								break;
 506							}
 507						}
 508				}
 509
 510					if( Matches == 0 )
 511						SendAllChat( m_GHost->m_Language->UnableToBanNoMatchesFound( Victim ) );
 512					else if( Matches == 1 ) 
 513					{
 514						// calculate timestamp
 515						uint32_t timestamp = m_GameTicks - m_GameStartTime; 
 516				
 517						string MinString = UTIL_ToString( ( timestamp / 1000 ) / 60 );
 518						string SecString = UTIL_ToString( ( timestamp / 1000 ) % 60 );
 519				
 520						if( MinString.size( ) == 1 )
 521							MinString.insert( 0, "0" );
 522				
 523						if( SecString.size( ) == 1 )
 524							SecString.insert( 0, "0" );
 525
 526						Reason = "[Reason: " + MinString + ":" + SecString + "] " + Reason; 					
 527				
 528						m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( LastMatch->GetServer( ), LastMatch->GetName( ), LastMatch->GetIP( ), m_GameName, User, Reason ) ) );
 529					}
 530					else
 531						SendAllChat( m_GHost->m_Language->UnableToBanFoundMoreThanOneMatch( Victim ) );
 532				}
 533				else
 534				{
 535					CGamePlayer *LastMatch = NULL;
 536					uint32_t Matches = GetPlayerFromNamePartial( Victim, &LastMatch );
 537
 538					if( Matches == 0 )
 539						SendAllChat( m_GHost->m_Language->UnableToBanNoMatchesFound( Victim ) );
 540					else if( Matches == 1 )
 541						m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( LastMatch->GetJoinedRealm( ), LastMatch->GetName( ), LastMatch->GetExternalIPString( ), m_GameName, User, Reason ) ) );
 542					else
 543						SendAllChat( m_GHost->m_Language->UnableToBanFoundMoreThanOneMatch( Victim ) );
 544				}
 545			}
 546
 547			//
 548			// !ANNOUNCE
 549			//
 550
 551			else if( Command == "announce" && !m_CountDownStarted )
 552			{
 553				if ( m_GHost->m_HideCommands )
 554					HideCommand = true;
 555
 556				if( Payload.empty( ) || Payload == "off" )
 557				{
 558					SendAllChat( m_GHost->m_Language->AnnounceMessageDisabled( ) );
 559					SetAnnounce( 0, string( ) );
 560				}
 561				else
 562				{
 563					// extract the interval and the message
 564					// e.g. "30 hello everyone" -> interval: "30", message: "hello everyone"
 565
 566					uint32_t Interval;
 567					string Message;
 568					stringstream SS;
 569					SS << Payload;
 570					SS >> Interval;
 571
 572					if( SS.fail( ) || Interval == 0 )
 573					{
 574						CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to announce command" );
 575						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to announce command", 0, 0));
 576					}
 577					else
 578					{
 579						if( SS.eof( ) )
 580						{
 581							CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to announce command" );
 582							forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to announce command", 0, 0));
 583						}
 584						else
 585						{
 586							getline( SS, Message );
 587							string :: size_type Start = Message.find_first_not_of( " " );
 588
 589							if( Start != string :: npos )
 590								Message = Message.substr( Start );
 591
 592							SendAllChat( m_GHost->m_Language->AnnounceMessageEnabled( ) );
 593							SetAnnounce( Interval, Message );
 594						}
 595					}
 596				}
 597			}
 598
 599			//
 600			// !AUTOSAVE
 601			//
 602
 603                        else if( Command == "autosave" )
 604			{
 605				if ( m_GHost->m_HideCommands )
 606					HideCommand = true;
 607
 608				if( Payload == "on" )
 609				{
 610					SendAllChat( m_GHost->m_Language->AutoSaveEnabled( ) );
 611					m_AutoSave = true;
 612				}
 613				else if( Payload == "off" )
 614				{
 615					SendAllChat( m_GHost->m_Language->AutoSaveDisabled( ) );
 616					m_AutoSave = false;
 617				}
 618			}
 619
 620			//
 621			// !AUTOSTART
 622			//
 623
 624                        else if( Command == "autostart" && !m_CountDownStarted )
 625			{
 626				if ( m_GHost->m_HideCommands )
 627					HideCommand = true;
 628
 629				if( Payload.empty( ) || Payload == "off" )
 630				{
 631					SendAllChat( m_GHost->m_Language->AutoStartDisabled( ) );
 632					m_AutoStartPlayers = 0;
 633					if ( m_UsingStart == true )
 634						m_UsingStart = false;
 635				}
 636				else
 637				{
 638					uint32_t AutoStartPlayers = UTIL_ToUInt32( Payload );
 639
 640					if( AutoStartPlayers != 0 )
 641					{
 642						SendAllChat( m_GHost->m_Language->AutoStartEnabled( UTIL_ToString( AutoStartPlayers ) ) );
 643						m_AutoStartPlayers = AutoStartPlayers;
 644					}
 645				}
 646			}
 647
 648			//
 649			// !BANLAST
 650			//
 651
 652			else if( Command == "banlast" && m_GameLoaded && !m_GHost->m_BNETs.empty( ) && m_DBBanLast )
 653			{
 654				if ( m_GHost->m_HideCommands )
 655					HideCommand = true;
 656
 657				// calculate timestamp
 658				uint32_t timestamp = m_GameTicks - m_GameStartTime; 
 659				
 660				string MinString = UTIL_ToString( ( timestamp / 1000 ) / 60 );
 661				string SecString = UTIL_ToString( ( timestamp / 1000 ) % 60 );
 662				
 663				if( MinString.size( ) == 1 )
 664					MinString.insert( 0, "0" );
 665				
 666				if( SecString.size( ) == 1 )
 667					SecString.insert( 0, "0" );
 668
 669				string Reason = "[Reason: " + MinString + ":" + SecString + "] " + Payload;			
 670				
 671				m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( m_DBBanLast->GetServer( ), m_DBBanLast->GetName( ), m_DBBanLast->GetIP( ), m_GameName, User, Reason ) ) );
 672			}
 673
 674			//
 675			// !CHECK
 676			//
 677
 678			else if( Command == "check" )
 679			{
 680				if ( m_GHost->m_HideCommands )
 681					HideCommand = true;
 682
 683				if( !Payload.empty( ) )
 684				{
 685					CGamePlayer *LastMatch = NULL;
 686					uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
 687
 688					if( Matches == 0 )
 689						SendAllChat( m_GHost->m_Language->UnableToCheckPlayerNoMatchesFound( Payload ) );
 690					else if( Matches == 1 )
 691					{
 692						bool LastMatchAdminCheck = false;
 693
 694                                                for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
 695						{
 696							if( (*i)->GetServer( ) == LastMatch->GetSpoofedRealm( ) && (*i)->IsAdmin( LastMatch->GetName( ) ) )
 697							{
 698								LastMatchAdminCheck = true;
 699								break;
 700							}
 701						}
 702
 703						bool LastMatchRootAdminCheck = false;
 704
 705                                                for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
 706						{
 707							if( (*i)->GetServer( ) == LastMatch->GetSpoofedRealm( ) && (*i)->IsRootAdmin( LastMatch->GetName( ) ) )
 708							{
 709								LastMatchRootAdminCheck = true;
 710								break;
 711							}
 712						}
 713
 714						SendAllChat( m_GHost->m_Language->CheckedPlayer( LastMatch->GetName( ), LastMatch->GetNumPings( ) > 0 ? UTIL_ToString( LastMatch->GetPing( m_GHost->m_LCPings ) ) + "ms" : "N/A", m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( LastMatch->GetExternalIP( ), true ) ), LastMatchAdminCheck || LastMatchRootAdminCheck ? "Yes" : "No", IsOwner( LastMatch->GetName( ) ) ? "Yes" : "No", LastMatch->GetSpoofed( ) ? "Yes" : "No", LastMatch->GetSpoofedRealm( ).empty( ) ? "N/A" : LastMatch->GetSpoofedRealm( ), LastMatch->GetReserved( ) ? "Yes" : "No" ) );
 715					}
 716					else
 717						SendAllChat( m_GHost->m_Language->UnableToCheckPlayerFoundMoreThanOneMatch( Payload ) );
 718				}
 719				else
 720					SendAllChat( m_GHost->m_Language->CheckedPlayer( User, player->GetNumPings( ) > 0 ? UTIL_ToString( player->GetPing( m_GHost->m_LCPings ) ) + "ms" : "N/A", m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( player->GetExternalIP( ), true ) ), AdminCheck || RootAdminCheck ? "Yes" : "No", IsOwner( User ) ? "Yes" : "No", player->GetSpoofed( ) ? "Yes" : "No", player->GetSpoofedRealm( ).empty( ) ? "N/A" : player->GetSpoofedRealm( ), player->GetReserved( ) ? "Yes" : "No" ) );
 721			}
 722
 723			//
 724			// !CHECKBAN
 725			//
 726
 727			else if( Command == "checkban" && !Payload.empty( ) && !m_GHost->m_BNETs.empty( ) )
 728			{
 729				if ( m_GHost->m_HideCommands )
 730					HideCommand = true;
 731
 732				for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); ++i )
 733					m_PairedBanChecks.push_back( PairedBanCheck( User, m_GHost->m_DB->ThreadedBanCheck( (*i)->GetServer( ), Payload, string( ) ) ) );
 734			}
 735
 736			//
 737			// !CLEARHCL
 738			//
 739
 740			else if( Command == "clearhcl" && !m_CountDownStarted )
 741			{
 742				if ( m_GHost->m_HideCommands )
 743					HideCommand = true;
 744
 745				m_HCLCommandString.clear( );
 746				SendAllChat( m_GHost->m_Language->ClearingHCL( ) );
 747			}
 748
 749			//
 750			// !CLOSE (close slot)
 751			//
 752
 753			else if( Command == "close" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
 754			{
 755				if ( m_GHost->m_HideCommands )
 756					HideCommand = true;
 757
 758				// close as many slots as specified, e.g. "5 10" closes slots 5 and 10
 759
 760				stringstream SS;
 761				SS << Payload;
 762
 763				while( !SS.eof( ) )
 764				{
 765					uint32_t SID;
 766					SS >> SID;
 767
 768					if( SS.fail( ) )
 769					{
 770						CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to close command" );
 771						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input to close command", 0, 0));
 772						break;
 773					}
 774					else
 775						CloseSlot( (unsigned char)( SID - 1 ), true );
 776				}
 777			}
 778
 779			//
 780			// !CLOSEALL
 781			//
 782
 783			else if( Command == "closeall" && !m_GameLoading && !m_GameLoaded )
 784			{
 785				if ( m_GHost->m_HideCommands )
 786					HideCommand = true;
 787
 788				CloseAllSlots( );
 789			}
 790
 791			//
 792			// !COMP (computer slot)
 793			//
 794
 795			else if( Command == "comp" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
 796			{
 797				if ( m_GHost->m_HideCommands )
 798					HideCommand = true;
 799
 800				// extract the slot and the skill
 801				// e.g. "1 2" -> slot: "1", skill: "2"
 802
 803				uint32_t Slot;
 804				uint32_t Skill = 1;
 805				stringstream SS;
 806				SS << Payload;
 807				SS >> Slot;
 808
 809				if( SS.fail( ) )
 810				{
 811					CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comp command" );
 812					forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to comp command", 0, 0));
 813				}
 814				else
 815				{
 816					if( !SS.eof( ) )
 817						SS >> Skill;
 818
 819					if( SS.fail( ) )
 820					{
 821						CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to comp command" );
 822						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #2 to comp command", 0, 0));
 823					}
 824					else
 825						ComputerSlot( (unsigned char)( Slot - 1 ), (unsigned char)Skill, true );
 826				}
 827			}
 828
 829			//
 830			// !COMPCOLOUR (computer colour change)
 831			//
 832
 833			else if( Command == "compcolour" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
 834			{
 835				if ( m_GHost->m_HideCommands )
 836					HideCommand = true;
 837
 838				// extract the slot and the colour
 839				// e.g. "1 2" -> slot: "1", colour: "2"
 840
 841				uint32_t Slot;
 842				uint32_t Colour;
 843				stringstream SS;
 844				SS << Payload;
 845				SS >> Slot;
 846
 847				if( SS.fail( ) )
 848				{
 849					CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to compcolour command" );
 850					forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to compcolour command", 0, 0));
 851				}
 852				else
 853				{
 854					if( SS.eof( ) )
 855					{
 856						CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to compcolour command" );
 857						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to compcolour command", 0, 0));
 858					}
 859					else
 860					{
 861						SS >> Colour;
 862
 863						if( SS.fail( ) )
 864						{
 865							CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to compcolour command" );
 866							forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #2 to compcolour command", 0, 0));
 867						}
 868						else
 869						{
 870							unsigned char SID = (unsigned char)( Slot - 1 );
 871
 872							if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && Colour < 12 && SID < m_Slots.size( ) )
 873							{
 874								if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
 875									ColourSlot( SID, Colour );
 876							}
 877						}
 878					}
 879				}
 880			}
 881
 882			//
 883			// !COMPHANDICAP (computer handicap change)
 884			//
 885
 886			else if( Command == "comphandicap" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
 887			{
 888				if ( m_GHost->m_HideCommands )
 889					HideCommand = true;
 890
 891				// extract the slot and the handicap
 892				// e.g. "1 50" -> slot: "1", handicap: "50"
 893
 894				uint32_t Slot;
 895				uint32_t Handicap;
 896				stringstream SS;
 897				SS << Payload;
 898				SS >> Slot;
 899
 900				if( SS.fail( ) )
 901				{
 902					CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comphandicap command" );
 903					forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to comphandicap command", 0, 0));
 904				}
 905				else
 906				{
 907					if( SS.eof( ) )
 908					{
 909						CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to comphandicap command" );
 910						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to comphandicap command", 0, 0));
 911					}
 912					else
 913					{
 914						SS >> Handicap;
 915
 916						if( SS.fail( ) )
 917						{
 918							CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to comphandicap command" );
 919							forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #2 to comphandicap command", 0, 0));
 920						}
 921						else
 922						{
 923							unsigned char SID = (unsigned char)( Slot - 1 );
 924
 925							if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && ( Handicap == 50 || Handicap == 60 || Handicap == 70 || Handicap == 80 || Handicap == 90 || Handicap == 100 ) && SID < m_Slots.size( ) )
 926							{
 927								if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
 928								{
 929									m_Slots[SID].SetHandicap( (unsigned char)Handicap );
 930									SendAllSlotInfo( );
 931								}
 932							}
 933						}
 934					}
 935				}
 936			}
 937
 938			//
 939			// !COMPRACE (computer race change)
 940			//
 941
 942			else if( Command == "comprace" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
 943			{
 944				if ( m_GHost->m_HideCommands )
 945					HideCommand = true;
 946
 947				// extract the slot and the race
 948				// e.g. "1 human" -> slot: "1", race: "human"
 949
 950				uint32_t Slot;
 951				string Race;
 952				stringstream SS;
 953				SS << Payload;
 954				SS >> Slot;
 955
 956				if( SS.fail( ) )
 957				{
 958					CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comprace command" );
 959					forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to comprace command", 0, 0));
 960				}
 961				else
 962				{
 963					if( SS.eof( ) )
 964					{
 965						CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to comprace command" );
 966						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to comprace command", 0, 0));
 967					}
 968					else
 969					{
 970						getline( SS, Race );
 971						string :: size_type Start = Race.find_first_not_of( " " );
 972
 973						if( Start != string :: npos )
 974							Race = Race.substr( Start );
 975
 976						transform( Race.begin( ), Race.end( ), Race.begin( ), (int(*)(int))tolower );
 977						unsigned char SID = (unsigned char)( Slot - 1 );
 978
 979						if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && !( m_Map->GetMapFlags( ) & MAPFLAG_RANDOMRACES ) && SID < m_Slots.size( ) )
 980						{
 981							if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
 982							{
 983								if( Race == "human" )
 984								{
 985									m_Slots[SID].SetRace( SLOTRACE_HUMAN | SLOTRACE_SELECTABLE );
 986									SendAllSlotInfo( );
 987								}
 988								else if( Race == "orc" )
 989								{
 990									m_Slots[SID].SetRace( SLOTRACE_ORC | SLOTRACE_SELECTABLE );
 991									SendAllSlotInfo( );
 992								}
 993								else if( Race == "night elf" )
 994								{
 995									m_Slots[SID].SetRace( SLOTRACE_NIGHTELF | SLOTRACE_SELECTABLE );
 996									SendAllSlotInfo( );
 997								}
 998								else if( Race == "undead" )
 999								{
1000									m_Slots[SID].SetRace( SLOTRACE_UNDEAD | SLOTRACE_SELECTABLE );
1001									SendAllSlotInfo( );
1002								}
1003								else if( Race == "random" )
1004								{
1005									m_Slots[SID].SetRace( SLOTRACE_RANDOM | SLOTRACE_SELECTABLE );
1006									SendAllSlotInfo( );
1007								}
1008								else
1009								{
1010									CONSOLE_Print( "[GAME: " + m_GameName + "] unknown race [" + Race + "] sent to comprace command" );
1011									forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Unknown race [" + Race + "] sent to comprace command", 0, 0));
1012								}
1013							}
1014						}
1015					}
1016				}
1017			}
1018
1019			//
1020			// !COMPTEAM (computer team change)
1021			//
1022
1023			else if( Command == "compteam" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
1024			{
1025				if ( m_GHost->m_HideCommands )
1026					HideCommand = true;
1027
1028				// extract the slot and the team
1029				// e.g. "1 2" -> slot: "1", team: "2"
1030
1031				uint32_t Slot;
1032				uint32_t Team;
1033				stringstream SS;
1034				SS << Payload;
1035				SS >> Slot;
1036
1037				if( SS.fail( ) )
1038				{
1039					CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to compteam command" );
1040					forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #1 to compteam command", 0, 0));
1041				}
1042				else
1043				{
1044					if( SS.eof( ) )
1045					{
1046						CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to compteam command" );
1047						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Missing input #2 to compteam command", 0, 0));
1048					}
1049					else
1050					{
1051						SS >> Team;
1052
1053						if( SS.fail( ) )
1054						{
1055							CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to compteam command" );
1056							forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input #2 to compteam command", 0, 0));
1057						}
1058						else
1059						{
1060							unsigned char SID = (unsigned char)( Slot - 1 );
1061
1062							if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && Team < 12 && SID < m_Slots.size( ) )
1063							{
1064								if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
1065								{
1066									m_Slots[SID].SetTeam( (unsigned char)( Team - 1 ) );
1067									SendAllSlotInfo( );
1068								}
1069							}
1070						}
1071					}
1072				}
1073			}
1074
1075			//
1076			// !DBSTATUS
1077			//
1078
1079			else if( Command == "dbstatus" )
1080			{
1081				if ( m_GHost->m_HideCommands )
1082					HideCommand = true;
1083
1084				SendAllChat( m_GHost->m_DB->GetStatus( ) );
1085			}
1086
1087			//
1088			// !DOWNLOAD
1089			// !DL
1090			//
1091
1092			else if( ( Command == "download" || Command == "dl" ) && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
1093			{
1094				if ( m_GHost->m_HideCommands )
1095					HideCommand = true;
1096
1097				CGamePlayer *LastMatch = NULL;
1098				uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
1099
1100				if( Matches == 0 )
1101					SendAllChat( m_GHost->m_Language->UnableToStartDownloadNoMatchesFound( Payload ) );
1102				else if( Matches == 1 )
1103				{
1104					if( !LastMatch->GetDownloadStarted( ) && !LastMatch->GetDownloadFinished( ) )
1105					{
1106						unsigned char SID = GetSIDFromPID( LastMatch->GetPID( ) );
1107
1108						if( SID < m_Slots.size( ) && m_Slots[SID].GetDownloadStatus( ) != 100 )
1109						{
1110							// inform the client that we are willing to send the map
1111
1112							CONSOLE_Print( "[GAME: " + m_GameName + "] map download started for player [" + LastMatch->GetName( ) + "]" );
1113							forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Map download started for player [" + LastMatch->GetName( ) + "]", 0, 0));
1114							Send( LastMatch, m_Protocol->SEND_W3GS_STARTDOWNLOAD( GetHostPID( ) ) );
1115							LastMatch->SetDownloadAllowed( true );
1116							LastMatch->SetDownloadStarted( true );
1117							LastMatch->SetStartedDownloadingTicks( GetTicks( ) );
1118						}
1119					}
1120				}
1121				else
1122					SendAllChat( m_GHost->m_Language->UnableToStartDownloadFoundMoreThanOneMatch( Payload ) );
1123			}
1124
1125			//
1126			// !DROP
1127			//
1128
1129			else if( Command == "drop" && m_GameLoaded )
1130			{
1131				if ( m_GHost->m_HideCommands )
1132					HideCommand = true;
1133
1134				StopLaggers( "lagged out (dropped by admin)" );
1135			}
1136
1137			//
1138			// !END
1139			//
1140
1141			else if( Command == "end" && m_GameLoaded )
1142			{
1143				if ( m_GHost->m_HideCommands )
1144					HideCommand = true;
1145
1146				CONSOLE_Print( "[GAME: " + m_GameName + "] is over (admin ended game)" );
1147				forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Game is over (admin ended game)", 0, 0));
1148				StopPlayers( "was disconnected (admin ended game)" );
1149			}
1150
1151			//
1152			// !FAKEPLAYER
1153			//
1154
1155			else if( Command == "fakeplayer" && !m_CountDownStarted )
1156			{
1157				if ( m_GHost->m_HideCommands )
1158					HideCommand = true;
1159
1160				if( m_FakePlayerPID == 255 )
1161					CreateFakePlayer( );
1162				else
1163					DeleteFakePlayer( );
1164			}
1165
1166			//
1167			// !FPPAUSE
1168			//
1169
1170			else if( Command == "fppause" && m_FakePlayerPID != 255 && m_GameLoaded )
1171			{
1172				if ( m_GHost->m_HideCommands )
1173					HideCommand = true;
1174
1175				BYTEARRAY CRC;
1176				BYTEARRAY Action;
1177				Action.push_back( 1 );
1178				m_Actions.push( new CIncomingAction( m_FakePlayerPID, CRC, Action ) );
1179			}
1180
1181			//
1182			// !FPRESUME
1183			//
1184
1185			else if( Command == "fpresume" && m_FakePlayerPID != 255 && m_GameLoaded )
1186			{
1187				if ( m_GHost->m_HideCommands )
1188					HideCommand = true;
1189
1190				BYTEARRAY CRC;
1191				BYTEARRAY Action;
1192				Action.push_back( 2 );
1193				m_Actions.push( new CIncomingAction( m_FakePlayerPID, CRC, Action ) );
1194			}
1195
1196			//
1197			// !FROM
1198			//
1199
1200			else if( Command == "from" )
1201			{
1202				if ( m_GHost->m_HideCommands )
1203					HideCommand = true;
1204
1205				string Froms;
1206
1207				if (!Payload.empty())
1208				{
1209					CGamePlayer *LastMatch = NULL;
1210					uint32_t Matches = GetPlayerFromNamePartial( Payload , &LastMatch );
1211
1212					if( Matches == 0 )
1213						CONSOLE_Print("No matches");
1214
1215					else if( Matches == 1 )
1216					{
1217						Froms = LastMatch->GetName( );
1218						Froms += ": (";
1219						Froms += m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( (LastMatch)->GetExternalIP( ), true ) );
1220						Froms += ")";
1221						SendAllChat(Froms);
1222					}
1223					else
1224						CONSOLE_Print("Found more than one match");
1225				}
1226
1227				if (Payload.empty())
1228				{
1229				for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); ++i )
1230				{
1231					// we reverse the byte order on the IP because it's stored in network byte order
1232
1233					Froms += (*i)->GetNameTerminated( );
1234					Froms += ": (";
1235					Froms += m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( (*i)->GetExternalIP( ), true ) );
1236					Froms += ")";
1237
1238					if( i != m_Players.end( ) - 1 )
1239						Froms += ", ";
1240
1241					else 
1242						SendAllChat( Froms );
1243				}
1244				}
1245			}
1246
1247			//
1248			// !HCL
1249			//
1250
1251			else if( Command == "hcl" && !m_CountDownStarted )
1252			{
1253				if ( m_GHost->m_HideCommands )
1254					HideCommand = true;
1255
1256				if( !Payload.empty( ) )
1257				{
1258					if( Payload.size( ) <= m_Slots.size( ) )
1259					{
1260						string HCLChars = "abcdefghijklmnopqrstuvwxyz0123456789 -=,.";
1261
1262						if( Payload.find_first_not_of( HCLChars ) == string :: npos )
1263						{
1264							m_HCLCommandString = Payload;
1265							SendAllChat( m_GHost->m_Language->SettingHCL( m_HCLCommandString ) );
1266
1267							vector<string> MapData;
1268							MapData.push_back( "HCL" ); MapData.push_back( m_HCLCommandString );
1269							forward( new CFwdData( FWD_GAME_MAP_INFO_UPDATE, MapData, m_GameID ) );
1270						}
1271						else
1272							SendAllChat( m_GHost->m_Language->UnableToSetHCLInvalid( ) );
1273					}
1274					else
1275						SendAllChat( m_GHost->m_Language->UnableToSetHCLTooLong( ) );
1276				}
1277				else
1278					SendAllChat( m_GHost->m_Language->TheHCLIs( m_HCLCommandString ) );
1279			}
1280
1281			//
1282			// !HOLD (hold a slot for someone)
1283			//
1284
1285			else if( Command == "hold" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
1286			{
1287				if ( m_GHost->m_HideCommands )
1288					HideCommand = true;
1289
1290				// hold as many players as specified, e.g. "Varlock Kilranin" holds players "Varlock" and "Kilranin"
1291
1292				stringstream SS;
1293				SS << Payload;
1294
1295				while( !SS.eof( ) )
1296				{
1297					string HoldName;
1298					SS >> HoldName;
1299
1300					if( SS.fail( ) )
1301					{
1302						CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to hold command" );
1303						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input to hold command", 0, 0));
1304						break;
1305					}
1306					else
1307					{
1308						SendAllChat( m_GHost->m_Language->AddedPlayerToTheHoldList( HoldName ) );
1309						AddToReserved( HoldName );
1310					}
1311				}
1312			}
1313
1314			//
1315			// !KICK (kick a player)
1316			//
1317
1318			else if( Command == "kick" && !Payload.empty( ) )
1319			{
1320				if ( m_GHost->m_HideCommands )
1321					HideCommand = true;
1322
1323				CGamePlayer *LastMatch = NULL;
1324				uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
1325
1326				if( Matches == 0 )
1327					SendAllChat( m_GHost->m_Language->UnableToKickNoMatchesFound( Payload ) );
1328				else if( Matches == 1 )
1329				{
1330					LastMatch->SetDeleteMe( true );
1331					LastMatch->SetLeftReason( m_GHost->m_Language->WasKickedByPlayer( User ) );
1332
1333					if( !m_GameLoading && !m_GameLoaded )
1334						LastMatch->SetLeftCode( PLAYERLEAVE_LOBBY );
1335					else
1336						LastMatch->SetLeftCode( PLAYERLEAVE_LOST );
1337
1338					if( !m_GameLoading && !m_GameLoaded )
1339						OpenSlot( GetSIDFromPID( LastMatch->GetPID( ) ), false );
1340				}
1341				else
1342					SendAllChat( m_GHost->m_Language->UnableToKickFoundMoreThanOneMatch( Payload ) );
1343			}
1344
1345			//
1346			// !LATENCY (set game latency)
1347			//
1348
1349			else if( Command == "latency" )
1350			{
1351				if ( m_GHost->m_HideCommands )
1352					HideCommand = true;
1353
1354				if( Payload.empty( ) )
1355					SendAllChat( m_GHost->m_Language->LatencyIs( UTIL_ToString( m_Latency ) ) );
1356				else
1357				{
1358					m_Latency = UTIL_ToUInt32( Payload );
1359
1360					if( m_Latency <= 20 )
1361					{
1362						m_Latency = 20;
1363						SendAllChat( m_GHost->m_Language->SettingLatencyToMinimum( "20" ) );
1364					}
1365					else if( m_Latency >= 500 )
1366					{
1367						m_Latency = 500;
1368						SendAllChat( m_GHost->m_Language->SettingLatencyToMaximum( "500" ) );
1369					}
1370					else
1371						SendAllChat( m_GHost->m_Language->SettingLatencyTo( UTIL_ToString( m_Latency ) ) );
1372				}
1373			}
1374
1375			//
1376			// !LOCK
1377			//
1378
1379			else if( Command == "lock" && ( RootAdminCheck || IsOwner( User ) ) )
1380			{
1381				if ( m_GHost->m_HideCommands )
1382					HideCommand = true;
1383
1384				SendAllChat( m_GHost->m_Language->GameLocked( ) );
1385				m_Locked = true;
1386			}
1387
1388			//
1389			// !MESSAGES
1390			//
1391
1392			else if( Command == "messages" )
1393			{
1394				if ( m_GHost->m_HideCommands )
1395					HideCommand = true;
1396
1397				if( Payload == "on" )
1398				{
1399					SendAllChat( m_GHost->m_Language->LocalAdminMessagesEnabled( ) );
1400					m_LocalAdminMessages = true;
1401				}
1402				else if( Payload == "off" )
1403				{
1404					SendAllChat( m_GHost->m_Language->LocalAdminMessagesDisabled( ) );
1405					m_LocalAdminMessages = false;
1406				}
1407			}
1408
1409			//
1410			// !MUTE
1411			//
1412
1413			else if( Command == "mute" )
1414			{
1415				if ( m_GHost->m_HideCommands )
1416					HideCommand = true;
1417
1418				CGamePlayer *LastMatch = NULL;
1419				uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
1420
1421				if( Matches == 0 )
1422					SendAllChat( m_GHost->m_Language->UnableToMuteNoMatchesFound( Payload ) );
1423				else if( Matches == 1 )
1424				{
1425					SendAllChat( m_GHost->m_Language->MutedPlayer( LastMatch->GetName( ), User ) );
1426					LastMatch->SetMuted( true );
1427				}
1428				else
1429					SendAllChat( m_GHost->m_Language->UnableToMuteFoundMoreThanOneMatch( Payload ) );
1430			}
1431
1432			//
1433			// !MUTEALL
1434			//
1435
1436			else if( Command == "muteall" && m_GameLoaded )
1437			{
1438				if ( m_GHost->m_HideCommands )
1439					HideCommand = true;
1440
1441				SendAllChat( m_GHost->m_Language->GlobalChatMuted( ) );
1442				m_MuteAll = true;
1443			}
1444
1445			//
1446			// !OPEN (open slot)
1447			//
1448
1449			else if( Command == "open" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
1450			{
1451				if ( m_GHost->m_HideCommands )
1452					HideCommand = true;
1453
1454				// open as many slots as specified, e.g. "5 10" opens slots 5 and 10
1455
1456				stringstream SS;
1457				SS << Payload;
1458
1459				while( !SS.eof( ) )
1460				{
1461					uint32_t SID;
1462					SS >> SID;
1463
1464					if( SS.fail( ) )
1465					{
1466						CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to open command" );
1467						forward(new CFwdData(FWD_GENERAL, "GAME: " + m_GameName + " - Bad input to open command", 0, 0));
1468						break;
1469					}
1470					else
1471						OpenSlot( (unsigned char)( SID - 1 ), true );
1472				}
1473			}
1474
1475			//
1476			// !OPENALL
1477			//
1478
1479			else if( Command == "openall" && !m_GameLoading && !m_GameLoaded )
1480			{
1481				if ( m_GHost->m_HideCommands )
1482					HideCommand = true;
1483
1484				OpenAllSlots( );
1485			}
1486
1487			//
1488			// !OWNER (set game owner)
1489			//
1490
1491			else if( Command == "owner" )
1492			{
1493				if( RootAdminCheck || IsOwner( User ) || !GetPlayerFromName( m_OwnerName, false ) )
1494				{
1495					if ( m_GHost->m_HideCommands )
1496					HideCommand = true;
1497
1498					if( !Payload.empty( ) )
1499					{
1500						SendAllChat( m_GHost->m_Language->SettingGameOwnerTo( Payload ) );
1501						m_OwnerName = Payload;
1502					}
1503					else
1504					{
1505						SendAllChat( m_GHost->m_Language->SettingGameOwnerTo( User ) );
1506						m_OwnerName = User;
1507					}
1508				}
1509				else
1510					SendAllChat( m_GHost->m_Language->UnableToSetGameOwner( m_OwnerName ) );
1511			}
1512
1513			//
1514			// !PING
1515			//
1516
1517			else if( Command == "ping" )
1518			{
1519				if ( m_GHost->m_HideCommands )
1520					HideCommand = true;
1521
1522				// kick players with ping higher than payload if payload isn't empty
1523				// we only do this if the game hasn't started since we don't want to kick players from a game in progress
1524
1525				uint32_t Kicked = 0;
1526				uint32_t KickPing = 0;
1527				string Pings;
1528				uint32_t Matches = 0;
1529
1530				if (!Payload.empty())
1531				{
1532					CGamePlayer *LastMatch = NULL;
1533					Matches = GetPlayerFromNamePartial( Payload , &LastMatch );
1534
1535					if( Matches == 0 )
1536						CONSOLE_Print("No matches");
1537
1538					else if( Matches == 1 )
1539					{
1540						Pings = LastMatch->GetName( );
1541						Pings +=": ";
1542						if( LastMatch->GetNumPings( ) > 0 )
1543						{
1544							Pings += UTIL_ToString( LastMatch->GetPing( m_GHost->m_LCPings ) );
1545							Pings +=" ms";
1546						} else
1547							Pings += "N/A";
1548
1549						SendAllChat(Pings);
1550					}
1551					else
1552						CONSOLE_Print("Found more than one match");
1553				}
1554
1555				if( !m_GameLoading && !m_GameLoaded && !Payload.empty( ) )
1556					KickPing = UTIL_ToUInt32( Payload );
1557				
1558				if (Payload.empty() || Matches == 0 )
1559				{
1560				// copy the m_Players vector so we can sort by descending ping so it's easier to find players with high pings
1561
1562				vector<CGamePlayer *> SortedPlayers = m_Players;
1563				sort( SortedPlayers.begin( ), SortedPlayers.end( ), CGamePlayerSortDescByPing( ) );
1564				for( vector<CGamePlayer *> :: iterator i = SortedPlayers.begin( ); i != SortedPlayers.end( ); ++i )
1565				{
1566					Pings += (*i)->GetNameTerminated( );
1567					Pings += ": ";
1568
1569					if( (*i)->GetNumPings( ) > 0 )
1570					{
1571						Pings += UTIL_ToString( (*i)->GetPing( m_GHost->m_LCPings ) );
1572
1573						if( !m_GameLoading && !m_GameLoaded && !(*i)->GetReserved( ) && KickPing > 0 && (*i)->GetPing( m_GHost->m_LCPings ) > KickPing )
1574						{
1575							(*i)->SetDeleteMe( true );
1576							(*i)->SetLeftReason( "was kicked for excessive ping " + UTIL_ToString( (*i)->GetPing( m_GHost->m_LCPings ) ) + " >…

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