PageRenderTime 65ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/ghost-legacy/bnet.cpp

http://ghostcb.googlecode.com/
C++ | 2772 lines | 2326 code | 282 blank | 164 comment | 437 complexity | 2b307f4d48eb7d68265f6a840643a6c8 MD5 | raw file
  1. /*
  2. Copyright [2008] [Trevor Hogan]
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/
  13. */
  14. #include "ghost.h"
  15. #include "util.h"
  16. #include "config.h"
  17. #include "language.h"
  18. #include "socket.h"
  19. #include "commandpacket.h"
  20. #include "ghostdb.h"
  21. #include "bncsutilinterface.h"
  22. #include "bnlsclient.h"
  23. #include "bnetprotocol.h"
  24. #include "bnet.h"
  25. #include "map.h"
  26. #include "packed.h"
  27. #include "savegame.h"
  28. #include "replay.h"
  29. #include "gameprotocol.h"
  30. #include "game_base.h"
  31. #include <boost/filesystem.hpp>
  32. using namespace boost :: filesystem;
  33. //
  34. // CBNET
  35. //
  36. CBNET :: CBNET( CGHost *nGHost, string nServer, string nServerAlias, string nBNLSServer, uint16_t nBNLSPort, uint32_t nBNLSWardenCookie, string nCDKeyROC, string nCDKeyTFT, string nCountryAbbrev, string nCountry, uint32_t nLocaleID, string nUserName, string nUserPassword, string nFirstChannel, string nRootAdmin, string nLANRootAdmin, char nCommandTrigger, bool nHoldFriends, bool nHoldClan, bool nPublicCommands, unsigned char nWar3Version, BYTEARRAY nEXEVersion, BYTEARRAY nEXEVersionHash, string nPasswordHashType, string nPVPGNRealmName, uint32_t nMaxMessageLength, uint32_t nHostCounterID )
  37. {
  38. // todotodo: append path seperator to Warcraft3Path if needed
  39. m_GHost = nGHost;
  40. m_Socket = new CTCPClient( );
  41. m_Protocol = new CBNETProtocol( );
  42. m_BNLSClient = NULL;
  43. m_BNCSUtil = new CBNCSUtilInterface( nUserName, nUserPassword );
  44. m_CallableAdminList = m_GHost->m_DB->ThreadedAdminList( nServer );
  45. m_CallableBanList = m_GHost->m_DB->ThreadedBanList( nServer );
  46. m_Exiting = false;
  47. m_Server = nServer;
  48. string LowerServer = m_Server;
  49. transform( LowerServer.begin( ), LowerServer.end( ), LowerServer.begin( ), (int(*)(int))tolower );
  50. if( !nServerAlias.empty( ) )
  51. m_ServerAlias = nServerAlias;
  52. else if( LowerServer == "useast.battle.net" )
  53. m_ServerAlias = "USEast";
  54. else if( LowerServer == "uswest.battle.net" )
  55. m_ServerAlias = "USWest";
  56. else if( LowerServer == "asia.battle.net" )
  57. m_ServerAlias = "Asia";
  58. else if( LowerServer == "europe.battle.net" )
  59. m_ServerAlias = "Europe";
  60. else
  61. m_ServerAlias = m_Server;
  62. if( nPasswordHashType == "pvpgn" && !nBNLSServer.empty( ) )
  63. {
  64. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] pvpgn connection found with a configured BNLS server, ignoring BNLS server" );
  65. nBNLSServer.clear( );
  66. nBNLSPort = 0;
  67. nBNLSWardenCookie = 0;
  68. }
  69. m_BNLSServer = nBNLSServer;
  70. m_BNLSPort = nBNLSPort;
  71. m_BNLSWardenCookie = nBNLSWardenCookie;
  72. m_CDKeyROC = nCDKeyROC;
  73. m_CDKeyTFT = nCDKeyTFT;
  74. // remove dashes and spaces from CD keys and convert to uppercase
  75. m_CDKeyROC.erase( remove( m_CDKeyROC.begin( ), m_CDKeyROC.end( ), '-' ), m_CDKeyROC.end( ) );
  76. m_CDKeyTFT.erase( remove( m_CDKeyTFT.begin( ), m_CDKeyTFT.end( ), '-' ), m_CDKeyTFT.end( ) );
  77. m_CDKeyROC.erase( remove( m_CDKeyROC.begin( ), m_CDKeyROC.end( ), ' ' ), m_CDKeyROC.end( ) );
  78. m_CDKeyTFT.erase( remove( m_CDKeyTFT.begin( ), m_CDKeyTFT.end( ), ' ' ), m_CDKeyTFT.end( ) );
  79. transform( m_CDKeyROC.begin( ), m_CDKeyROC.end( ), m_CDKeyROC.begin( ), (int(*)(int))toupper );
  80. transform( m_CDKeyTFT.begin( ), m_CDKeyTFT.end( ), m_CDKeyTFT.begin( ), (int(*)(int))toupper );
  81. if( m_CDKeyROC.size( ) != 26 )
  82. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] warning - your ROC CD key is not 26 characters long and is probably invalid" );
  83. if( m_GHost->m_TFT && m_CDKeyTFT.size( ) != 26 )
  84. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] warning - your TFT CD key is not 26 characters long and is probably invalid" );
  85. m_CountryAbbrev = nCountryAbbrev;
  86. m_Country = nCountry;
  87. m_LocaleID = nLocaleID;
  88. m_UserName = nUserName;
  89. m_UserPassword = nUserPassword;
  90. m_FirstChannel = nFirstChannel;
  91. m_RootAdmin = nRootAdmin;
  92. transform( m_RootAdmin.begin( ), m_RootAdmin.end( ), m_RootAdmin.begin( ), (int(*)(int))tolower );
  93. m_LANRootAdmin = nLANRootAdmin;
  94. transform( m_LANRootAdmin.begin( ), m_LANRootAdmin.end( ), m_LANRootAdmin.begin( ), (int(*)(int))tolower );
  95. m_CommandTrigger = nCommandTrigger;
  96. m_War3Version = nWar3Version;
  97. m_EXEVersion = nEXEVersion;
  98. m_EXEVersionHash = nEXEVersionHash;
  99. m_PasswordHashType = nPasswordHashType;
  100. m_PVPGNRealmName = nPVPGNRealmName;
  101. m_MaxMessageLength = nMaxMessageLength;
  102. m_HostCounterID = nHostCounterID;
  103. m_LastDisconnectedTime = 0;
  104. m_LastConnectionAttemptTime = 0;
  105. m_LastNullTime = 0;
  106. m_LastOutPacketTicks = 0;
  107. m_LastOutPacketSize = 0;
  108. m_LastAdminRefreshTime = GetTime( );
  109. m_LastBanRefreshTime = GetTime( );
  110. m_FirstConnect = true;
  111. m_WaitingToConnect = true;
  112. m_LoggedIn = false;
  113. m_InChat = false;
  114. m_HoldFriends = nHoldFriends;
  115. m_HoldClan = nHoldClan;
  116. m_PublicCommands = nPublicCommands;
  117. }
  118. CBNET :: ~CBNET( )
  119. {
  120. delete m_Socket;
  121. delete m_Protocol;
  122. delete m_BNLSClient;
  123. while( !m_Packets.empty( ) )
  124. {
  125. delete m_Packets.front( );
  126. m_Packets.pop( );
  127. }
  128. delete m_BNCSUtil;
  129. for( vector<CIncomingFriendList *> :: iterator i = m_Friends.begin( ); i != m_Friends.end( ); i++ )
  130. delete *i;
  131. for( vector<CIncomingClanList *> :: iterator i = m_Clans.begin( ); i != m_Clans.end( ); i++ )
  132. delete *i;
  133. for( vector<PairedAdminCount> :: iterator i = m_PairedAdminCounts.begin( ); i != m_PairedAdminCounts.end( ); i++ )
  134. m_GHost->m_Callables.push_back( i->second );
  135. for( vector<PairedAdminAdd> :: iterator i = m_PairedAdminAdds.begin( ); i != m_PairedAdminAdds.end( ); i++ )
  136. m_GHost->m_Callables.push_back( i->second );
  137. for( vector<PairedAdminRemove> :: iterator i = m_PairedAdminRemoves.begin( ); i != m_PairedAdminRemoves.end( ); i++ )
  138. m_GHost->m_Callables.push_back( i->second );
  139. for( vector<PairedBanCount> :: iterator i = m_PairedBanCounts.begin( ); i != m_PairedBanCounts.end( ); i++ )
  140. m_GHost->m_Callables.push_back( i->second );
  141. for( vector<PairedBanAdd> :: iterator i = m_PairedBanAdds.begin( ); i != m_PairedBanAdds.end( ); i++ )
  142. m_GHost->m_Callables.push_back( i->second );
  143. for( vector<PairedBanRemove> :: iterator i = m_PairedBanRemoves.begin( ); i != m_PairedBanRemoves.end( ); i++ )
  144. m_GHost->m_Callables.push_back( i->second );
  145. for( vector<PairedGPSCheck> :: iterator i = m_PairedGPSChecks.begin( ); i != m_PairedGPSChecks.end( ); i++ )
  146. m_GHost->m_Callables.push_back( i->second );
  147. for( vector<PairedDPSCheck> :: iterator i = m_PairedDPSChecks.begin( ); i != m_PairedDPSChecks.end( ); i++ )
  148. m_GHost->m_Callables.push_back( i->second );
  149. if( m_CallableAdminList )
  150. m_GHost->m_Callables.push_back( m_CallableAdminList );
  151. if( m_CallableBanList )
  152. m_GHost->m_Callables.push_back( m_CallableBanList );
  153. for( vector<CDBBan *> :: iterator i = m_Bans.begin( ); i != m_Bans.end( ); i++ )
  154. delete *i;
  155. }
  156. BYTEARRAY CBNET :: GetUniqueName( )
  157. {
  158. return m_Protocol->GetUniqueName( );
  159. }
  160. vector<pair<string, int> > CBNET :: GetFriends( )
  161. {
  162. vector<pair<string, int> > result;
  163. for( vector<CIncomingFriendList *> :: iterator i = m_Friends.begin( ); i != m_Friends.end( ); i++ )
  164. result.push_back(pair<string, int>((*i)->GetAccount( ), (*i)->GetArea( ) ) );
  165. return result;
  166. }
  167. vector<pair<string, int> > CBNET :: GetClan( )
  168. {
  169. vector<pair<string, int> > result;
  170. int k = 0;
  171. for( vector<CIncomingClanList *> :: iterator i = m_Clans.begin( ); i != m_Clans.end( ) && k < 50; i++, k++ )
  172. result.push_back(pair<string, int>((*i)->GetName( ), (*i)->GetRawStatus( ) ) );
  173. return result;
  174. }
  175. vector<pair<string, int> > CBNET :: GetBans( )
  176. {
  177. vector<pair<string, int> > result;
  178. int k = 0;
  179. for( vector<CDBBan *> :: iterator i = m_Bans.begin( ); i != m_Bans.end( ) && k < 100; i++, k++ )
  180. result.push_back(pair<string, int>((*i)->GetName( ), 0 ) );
  181. return result;
  182. }
  183. vector<pair<string, int> > CBNET :: GetAdmins( )
  184. {
  185. vector<pair<string, int> > result;
  186. vector<string> roots = UTIL_Tokenize( m_RootAdmin, ' ' );
  187. for( vector<string> :: iterator i = roots.begin( ); i != roots.end( ); i++ )
  188. result.push_back(pair<string, int>( *i, 2 ) );
  189. for( vector<string> :: iterator i = m_Admins.begin( ); i != m_Admins.end( ); i++ )
  190. result.push_back(pair<string, int>( *i, 0 ) );
  191. return result;
  192. }
  193. unsigned int CBNET :: SetFD( void *fd, void *send_fd, int *nfds )
  194. {
  195. unsigned int NumFDs = 0;
  196. if( !m_Socket->HasError( ) && m_Socket->GetConnected( ) )
  197. {
  198. m_Socket->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
  199. NumFDs++;
  200. if( m_BNLSClient )
  201. NumFDs += m_BNLSClient->SetFD( fd, send_fd, nfds );
  202. }
  203. return NumFDs;
  204. }
  205. bool CBNET :: Update( void *fd, void *send_fd )
  206. {
  207. //
  208. // update callables
  209. //
  210. for( vector<PairedAdminCount> :: iterator i = m_PairedAdminCounts.begin( ); i != m_PairedAdminCounts.end( ); )
  211. {
  212. if( i->second->GetReady( ) )
  213. {
  214. uint32_t Count = i->second->GetResult( );
  215. if( Count == 0 )
  216. QueueChatCommand( m_GHost->m_Language->ThereAreNoAdmins( m_Server ), i->first, !i->first.empty( ), false );
  217. else if( Count == 1 )
  218. QueueChatCommand( m_GHost->m_Language->ThereIsAdmin( m_Server ), i->first, !i->first.empty( ), false );
  219. else
  220. QueueChatCommand( m_GHost->m_Language->ThereAreAdmins( m_Server, UTIL_ToString( Count ) ), i->first, !i->first.empty( ), false );
  221. m_GHost->m_DB->RecoverCallable( i->second );
  222. delete i->second;
  223. i = m_PairedAdminCounts.erase( i );
  224. }
  225. else
  226. i++;
  227. }
  228. for( vector<PairedAdminAdd> :: iterator i = m_PairedAdminAdds.begin( ); i != m_PairedAdminAdds.end( ); )
  229. {
  230. if( i->second->GetReady( ) )
  231. {
  232. if( i->second->GetResult( ) )
  233. {
  234. AddAdmin( i->second->GetUser( ) );
  235. QueueChatCommand( m_GHost->m_Language->AddedUserToAdminDatabase( m_Server, i->second->GetUser( ) ), i->first, !i->first.empty( ), false );
  236. }
  237. else
  238. QueueChatCommand( m_GHost->m_Language->ErrorAddingUserToAdminDatabase( m_Server, i->second->GetUser( ) ), i->first, !i->first.empty( ), false );
  239. m_GHost->m_DB->RecoverCallable( i->second );
  240. delete i->second;
  241. i = m_PairedAdminAdds.erase( i );
  242. }
  243. else
  244. i++;
  245. }
  246. for( vector<PairedAdminRemove> :: iterator i = m_PairedAdminRemoves.begin( ); i != m_PairedAdminRemoves.end( ); )
  247. {
  248. if( i->second->GetReady( ) )
  249. {
  250. if( i->second->GetResult( ) )
  251. {
  252. RemoveAdmin( i->second->GetUser( ) );
  253. QueueChatCommand( m_GHost->m_Language->DeletedUserFromAdminDatabase( m_Server, i->second->GetUser( ) ), i->first, !i->first.empty( ), false );
  254. }
  255. else
  256. QueueChatCommand( m_GHost->m_Language->ErrorDeletingUserFromAdminDatabase( m_Server, i->second->GetUser( ) ), i->first, !i->first.empty( ), false );
  257. m_GHost->m_DB->RecoverCallable( i->second );
  258. delete i->second;
  259. i = m_PairedAdminRemoves.erase( i );
  260. }
  261. else
  262. i++;
  263. }
  264. for( vector<PairedBanCount> :: iterator i = m_PairedBanCounts.begin( ); i != m_PairedBanCounts.end( ); )
  265. {
  266. if( i->second->GetReady( ) )
  267. {
  268. uint32_t Count = i->second->GetResult( );
  269. if( Count == 0 )
  270. QueueChatCommand( m_GHost->m_Language->ThereAreNoBannedUsers( m_Server ), i->first, !i->first.empty( ), false );
  271. else if( Count == 1 )
  272. QueueChatCommand( m_GHost->m_Language->ThereIsBannedUser( m_Server ), i->first, !i->first.empty( ), false );
  273. else
  274. QueueChatCommand( m_GHost->m_Language->ThereAreBannedUsers( m_Server, UTIL_ToString( Count ) ), i->first, !i->first.empty( ), false );
  275. m_GHost->m_DB->RecoverCallable( i->second );
  276. delete i->second;
  277. i = m_PairedBanCounts.erase( i );
  278. }
  279. else
  280. i++;
  281. }
  282. for( vector<PairedBanAdd> :: iterator i = m_PairedBanAdds.begin( ); i != m_PairedBanAdds.end( ); )
  283. {
  284. if( i->second->GetReady( ) )
  285. {
  286. if( i->second->GetResult( ) )
  287. {
  288. AddBan( i->second->GetUser( ), i->second->GetIP( ), i->second->GetGameName( ), i->second->GetAdmin( ), i->second->GetReason( ) );
  289. QueueChatCommand( m_GHost->m_Language->BannedUser( i->second->GetServer( ), i->second->GetUser( ) ), i->first, !i->first.empty( ), false );
  290. }
  291. else
  292. QueueChatCommand( m_GHost->m_Language->ErrorBanningUser( i->second->GetServer( ), i->second->GetUser( ) ), i->first, !i->first.empty( ), false );
  293. m_GHost->m_DB->RecoverCallable( i->second );
  294. delete i->second;
  295. i = m_PairedBanAdds.erase( i );
  296. }
  297. else
  298. i++;
  299. }
  300. for( vector<PairedBanRemove> :: iterator i = m_PairedBanRemoves.begin( ); i != m_PairedBanRemoves.end( ); )
  301. {
  302. if( i->second->GetReady( ) )
  303. {
  304. if( i->second->GetResult( ) )
  305. {
  306. RemoveBan( i->second->GetUser( ) );
  307. QueueChatCommand( m_GHost->m_Language->UnbannedUser( i->second->GetUser( ) ), i->first, !i->first.empty( ), false );
  308. }
  309. else
  310. QueueChatCommand( m_GHost->m_Language->ErrorUnbanningUser( i->second->GetUser( ) ), i->first, !i->first.empty( ), false );
  311. m_GHost->m_DB->RecoverCallable( i->second );
  312. delete i->second;
  313. i = m_PairedBanRemoves.erase( i );
  314. }
  315. else
  316. i++;
  317. }
  318. for( vector<PairedGPSCheck> :: iterator i = m_PairedGPSChecks.begin( ); i != m_PairedGPSChecks.end( ); )
  319. {
  320. if( i->second->GetReady( ) )
  321. {
  322. CDBGamePlayerSummary *GamePlayerSummary = i->second->GetResult( );
  323. if( GamePlayerSummary )
  324. QueueChatCommand( 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( ) ) ), i->first, !i->first.empty( ), false );
  325. else
  326. QueueChatCommand( m_GHost->m_Language->HasntPlayedGamesWithThisBot( i->second->GetName( ) ), i->first, !i->first.empty( ), false );
  327. m_GHost->m_DB->RecoverCallable( i->second );
  328. delete i->second;
  329. i = m_PairedGPSChecks.erase( i );
  330. }
  331. else
  332. i++;
  333. }
  334. for( vector<PairedDPSCheck> :: iterator i = m_PairedDPSChecks.begin( ); i != m_PairedDPSChecks.end( ); )
  335. {
  336. if( i->second->GetReady( ) )
  337. {
  338. CDBDotAPlayerSummary *DotAPlayerSummary = i->second->GetResult( );
  339. if( DotAPlayerSummary )
  340. {
  341. string Summary = m_GHost->m_Language->HasPlayedDotAGamesWithThisBot( i->second->GetName( ),
  342. UTIL_ToString( DotAPlayerSummary->GetTotalGames( ) ),
  343. UTIL_ToString( DotAPlayerSummary->GetTotalWins( ) ),
  344. UTIL_ToString( DotAPlayerSummary->GetTotalLosses( ) ),
  345. UTIL_ToString( DotAPlayerSummary->GetTotalKills( ) ),
  346. UTIL_ToString( DotAPlayerSummary->GetTotalDeaths( ) ),
  347. UTIL_ToString( DotAPlayerSummary->GetTotalCreepKills( ) ),
  348. UTIL_ToString( DotAPlayerSummary->GetTotalCreepDenies( ) ),
  349. UTIL_ToString( DotAPlayerSummary->GetTotalAssists( ) ),
  350. UTIL_ToString( DotAPlayerSummary->GetTotalNeutralKills( ) ),
  351. UTIL_ToString( DotAPlayerSummary->GetTotalTowerKills( ) ),
  352. UTIL_ToString( DotAPlayerSummary->GetTotalRaxKills( ) ),
  353. UTIL_ToString( DotAPlayerSummary->GetTotalCourierKills( ) ),
  354. UTIL_ToString( DotAPlayerSummary->GetAvgKills( ), 2 ),
  355. UTIL_ToString( DotAPlayerSummary->GetAvgDeaths( ), 2 ),
  356. UTIL_ToString( DotAPlayerSummary->GetAvgCreepKills( ), 2 ),
  357. UTIL_ToString( DotAPlayerSummary->GetAvgCreepDenies( ), 2 ),
  358. UTIL_ToString( DotAPlayerSummary->GetAvgAssists( ), 2 ),
  359. UTIL_ToString( DotAPlayerSummary->GetAvgNeutralKills( ), 2 ),
  360. UTIL_ToString( DotAPlayerSummary->GetAvgTowerKills( ), 2 ),
  361. UTIL_ToString( DotAPlayerSummary->GetAvgRaxKills( ), 2 ),
  362. UTIL_ToString( DotAPlayerSummary->GetAvgCourierKills( ), 2 ) );
  363. QueueChatCommand( Summary, i->first, !i->first.empty( ), false );
  364. }
  365. else
  366. QueueChatCommand( m_GHost->m_Language->HasntPlayedDotAGamesWithThisBot( i->second->GetName( ) ), i->first, !i->first.empty( ), false );
  367. m_GHost->m_DB->RecoverCallable( i->second );
  368. delete i->second;
  369. i = m_PairedDPSChecks.erase( i );
  370. }
  371. else
  372. i++;
  373. }
  374. // refresh the admin list every 5 minutes
  375. if( !m_CallableAdminList && GetTime( ) - m_LastAdminRefreshTime >= 300 )
  376. m_CallableAdminList = m_GHost->m_DB->ThreadedAdminList( m_Server );
  377. if( m_CallableAdminList && m_CallableAdminList->GetReady( ) )
  378. {
  379. // CONSOLE_Print( "[BNET: " + m_ServerAlias + "] refreshed admin list (" + UTIL_ToString( m_Admins.size( ) ) + " -> " + UTIL_ToString( m_CallableAdminList->GetResult( ).size( ) ) + " admins)" );
  380. m_Admins = m_CallableAdminList->GetResult( );
  381. m_GHost->m_DB->RecoverCallable( m_CallableAdminList );
  382. delete m_CallableAdminList;
  383. m_CallableAdminList = NULL;
  384. m_LastAdminRefreshTime = GetTime( );
  385. }
  386. // refresh the ban list every 60 minutes
  387. if( !m_CallableBanList && GetTime( ) - m_LastBanRefreshTime >= 3600 )
  388. m_CallableBanList = m_GHost->m_DB->ThreadedBanList( m_Server );
  389. if( m_CallableBanList && m_CallableBanList->GetReady( ) )
  390. {
  391. // CONSOLE_Print( "[BNET: " + m_ServerAlias + "] refreshed ban list (" + UTIL_ToString( m_Bans.size( ) ) + " -> " + UTIL_ToString( m_CallableBanList->GetResult( ).size( ) ) + " bans)" );
  392. for( vector<CDBBan *> :: iterator i = m_Bans.begin( ); i != m_Bans.end( ); i++ )
  393. delete *i;
  394. m_Bans = m_CallableBanList->GetResult( );
  395. m_GHost->m_DB->RecoverCallable( m_CallableBanList );
  396. delete m_CallableBanList;
  397. m_CallableBanList = NULL;
  398. m_LastBanRefreshTime = GetTime( );
  399. }
  400. // we return at the end of each if statement so we don't have to deal with errors related to the order of the if statements
  401. // that means it might take a few ms longer to complete a task involving multiple steps (in this case, reconnecting) due to blocking or sleeping
  402. // but it's not a big deal at all, maybe 100ms in the worst possible case (based on a 50ms blocking time)
  403. if( m_Socket->HasError( ) )
  404. {
  405. // the socket has an error
  406. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] disconnected from battle.net due to socket error" );
  407. if( m_Socket->GetError( ) == ECONNRESET && GetTime( ) - m_LastConnectionAttemptTime <= 15 )
  408. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] warning - you are probably temporarily IP banned from battle.net" );
  409. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] waiting 90 seconds to reconnect" );
  410. m_GHost->EventBNETDisconnected( this );
  411. delete m_BNLSClient;
  412. m_BNLSClient = NULL;
  413. m_BNCSUtil->Reset( m_UserName, m_UserPassword );
  414. m_Socket->Reset( );
  415. m_LastDisconnectedTime = GetTime( );
  416. m_LoggedIn = false;
  417. m_InChat = false;
  418. m_WaitingToConnect = true;
  419. return m_Exiting;
  420. }
  421. if( !m_Socket->GetConnecting( ) && !m_Socket->GetConnected( ) && !m_WaitingToConnect )
  422. {
  423. // the socket was disconnected
  424. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] disconnected from battle.net" );
  425. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] waiting 90 seconds to reconnect" );
  426. m_GHost->EventBNETDisconnected( this );
  427. delete m_BNLSClient;
  428. m_BNLSClient = NULL;
  429. m_BNCSUtil->Reset( m_UserName, m_UserPassword );
  430. m_Socket->Reset( );
  431. m_LastDisconnectedTime = GetTime( );
  432. m_LoggedIn = false;
  433. m_InChat = false;
  434. m_WaitingToConnect = true;
  435. return m_Exiting;
  436. }
  437. if( m_Socket->GetConnected( ) )
  438. {
  439. // the socket is connected and everything appears to be working properly
  440. m_Socket->DoRecv( (fd_set *)fd );
  441. ExtractPackets( );
  442. ProcessPackets( );
  443. // update the BNLS client
  444. if( m_BNLSClient )
  445. {
  446. if( m_BNLSClient->Update( fd, send_fd ) )
  447. {
  448. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] deleting BNLS client" );
  449. delete m_BNLSClient;
  450. m_BNLSClient = NULL;
  451. }
  452. else
  453. {
  454. BYTEARRAY WardenResponse = m_BNLSClient->GetWardenResponse( );
  455. if( !WardenResponse.empty( ) )
  456. m_Socket->PutBytes( m_Protocol->SEND_SID_WARDEN( WardenResponse ) );
  457. }
  458. }
  459. // check if at least one packet is waiting to be sent and if we've waited long enough to prevent flooding
  460. // this formula has changed many times but currently we wait 1 second if the last packet was "small", 3.5 seconds if it was "medium", and 4 seconds if it was "big"
  461. uint32_t WaitTicks = 0;
  462. if( m_LastOutPacketSize < 10 )
  463. WaitTicks = 1000;
  464. else if( m_LastOutPacketSize < 100 )
  465. WaitTicks = 3500;
  466. else
  467. WaitTicks = 4000;
  468. if( !m_OutPackets.empty( ) && GetTicks( ) - m_LastOutPacketTicks >= WaitTicks )
  469. {
  470. if( m_OutPackets.size( ) > 7 )
  471. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] packet queue warning - there are " + UTIL_ToString( m_OutPackets.size( ) ) + " packets waiting to be sent" );
  472. m_Socket->PutBytes( m_OutPackets.front( ) );
  473. m_LastOutPacketSize = m_OutPackets.front( ).size( );
  474. m_OutPackets.pop( );
  475. m_LastOutPacketTicks = GetTicks( );
  476. }
  477. // send a null packet every 60 seconds to detect disconnects
  478. if( GetTime( ) - m_LastNullTime >= 60 && GetTicks( ) - m_LastOutPacketTicks >= 60000 )
  479. {
  480. m_Socket->PutBytes( m_Protocol->SEND_SID_NULL( ) );
  481. m_LastNullTime = GetTime( );
  482. }
  483. m_Socket->DoSend( (fd_set *)send_fd );
  484. return m_Exiting;
  485. }
  486. if( m_Socket->GetConnecting( ) )
  487. {
  488. // we are currently attempting to connect to battle.net
  489. if( m_Socket->CheckConnect( ) )
  490. {
  491. // the connection attempt completed
  492. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] connected" );
  493. m_GHost->EventBNETConnected( this );
  494. m_Socket->PutBytes( m_Protocol->SEND_PROTOCOL_INITIALIZE_SELECTOR( ) );
  495. m_Socket->PutBytes( m_Protocol->SEND_SID_AUTH_INFO( m_War3Version, m_GHost->m_TFT, m_LocaleID, m_CountryAbbrev, m_Country ) );
  496. m_Socket->DoSend( (fd_set *)send_fd );
  497. m_LastNullTime = GetTime( );
  498. m_LastOutPacketTicks = GetTicks( );
  499. while( !m_OutPackets.empty( ) )
  500. m_OutPackets.pop( );
  501. return m_Exiting;
  502. }
  503. else if( GetTime( ) - m_LastConnectionAttemptTime >= 15 )
  504. {
  505. // the connection attempt timed out (15 seconds)
  506. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] connect timed out" );
  507. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] waiting 90 seconds to reconnect" );
  508. m_GHost->EventBNETConnectTimedOut( this );
  509. m_Socket->Reset( );
  510. m_LastDisconnectedTime = GetTime( );
  511. m_WaitingToConnect = true;
  512. return m_Exiting;
  513. }
  514. }
  515. if( !m_Socket->GetConnecting( ) && !m_Socket->GetConnected( ) && ( m_FirstConnect || GetTime( ) - m_LastDisconnectedTime >= 90 ) )
  516. {
  517. // attempt to connect to battle.net
  518. m_FirstConnect = false;
  519. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] connecting to server [" + m_Server + "] on port 6112" );
  520. m_GHost->EventBNETConnecting( this );
  521. if( !m_GHost->m_BindAddress.empty( ) )
  522. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] attempting to bind to address [" + m_GHost->m_BindAddress + "]" );
  523. if( m_ServerIP.empty( ) )
  524. {
  525. m_Socket->Connect( m_GHost->m_BindAddress, m_Server, 6112 );
  526. if( !m_Socket->HasError( ) )
  527. {
  528. m_ServerIP = m_Socket->GetIPString( );
  529. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] resolved and cached server IP address " + m_ServerIP );
  530. }
  531. }
  532. else
  533. {
  534. // use cached server IP address since resolving takes time and is blocking
  535. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] using cached server IP address " + m_ServerIP );
  536. m_Socket->Connect( m_GHost->m_BindAddress, m_ServerIP, 6112 );
  537. }
  538. m_WaitingToConnect = false;
  539. m_LastConnectionAttemptTime = GetTime( );
  540. return m_Exiting;
  541. }
  542. return m_Exiting;
  543. }
  544. void CBNET :: ExtractPackets( )
  545. {
  546. // extract as many packets as possible from the socket's receive buffer and put them in the m_Packets queue
  547. string *RecvBuffer = m_Socket->GetBytes( );
  548. BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) );
  549. // a packet is at least 4 bytes so loop as long as the buffer contains 4 bytes
  550. while( Bytes.size( ) >= 4 )
  551. {
  552. // byte 0 is always 255
  553. if( Bytes[0] == BNET_HEADER_CONSTANT )
  554. {
  555. // bytes 2 and 3 contain the length of the packet
  556. uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 );
  557. if( Length >= 4 )
  558. {
  559. if( Bytes.size( ) >= Length )
  560. {
  561. m_Packets.push( new CCommandPacket( BNET_HEADER_CONSTANT, Bytes[1], BYTEARRAY( Bytes.begin( ), Bytes.begin( ) + Length ) ) );
  562. *RecvBuffer = RecvBuffer->substr( Length );
  563. Bytes = BYTEARRAY( Bytes.begin( ) + Length, Bytes.end( ) );
  564. }
  565. else
  566. return;
  567. }
  568. else
  569. {
  570. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] error - received invalid packet from battle.net (bad length), disconnecting" );
  571. m_Socket->Disconnect( );
  572. return;
  573. }
  574. }
  575. else
  576. {
  577. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] error - received invalid packet from battle.net (bad header constant), disconnecting" );
  578. m_Socket->Disconnect( );
  579. return;
  580. }
  581. }
  582. }
  583. void CBNET :: ProcessPackets( )
  584. {
  585. CIncomingGameHost *GameHost = NULL;
  586. CIncomingChatEvent *ChatEvent = NULL;
  587. BYTEARRAY WardenData;
  588. vector<CIncomingFriendList *> Friends;
  589. vector<CIncomingClanList *> Clans;
  590. // process all the received packets in the m_Packets queue
  591. // this normally means sending some kind of response
  592. while( !m_Packets.empty( ) )
  593. {
  594. CCommandPacket *Packet = m_Packets.front( );
  595. m_Packets.pop( );
  596. if( Packet->GetPacketType( ) == BNET_HEADER_CONSTANT )
  597. {
  598. switch( Packet->GetID( ) )
  599. {
  600. case CBNETProtocol :: SID_NULL:
  601. // warning: we do not respond to NULL packets with a NULL packet of our own
  602. // this is because PVPGN servers are programmed to respond to NULL packets so it will create a vicious cycle of useless traffic
  603. // official battle.net servers do not respond to NULL packets
  604. m_Protocol->RECEIVE_SID_NULL( Packet->GetData( ) );
  605. break;
  606. case CBNETProtocol :: SID_GETADVLISTEX:
  607. GameHost = m_Protocol->RECEIVE_SID_GETADVLISTEX( Packet->GetData( ) );
  608. if( GameHost )
  609. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] joining game [" + GameHost->GetGameName( ) + "]", GetRealmId( ), false );
  610. delete GameHost;
  611. GameHost = NULL;
  612. break;
  613. case CBNETProtocol :: SID_ENTERCHAT:
  614. if( m_Protocol->RECEIVE_SID_ENTERCHAT( Packet->GetData( ) ) )
  615. {
  616. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] joining channel [" + m_FirstChannel + "]", GetRealmId( ), false );
  617. m_InChat = true;
  618. m_Socket->PutBytes( m_Protocol->SEND_SID_JOINCHANNEL( m_FirstChannel ) );
  619. }
  620. break;
  621. case CBNETProtocol :: SID_CHATEVENT:
  622. ChatEvent = m_Protocol->RECEIVE_SID_CHATEVENT( Packet->GetData( ) );
  623. if( ChatEvent )
  624. ProcessChatEvent( ChatEvent );
  625. delete ChatEvent;
  626. ChatEvent = NULL;
  627. break;
  628. case CBNETProtocol :: SID_CHECKAD:
  629. m_Protocol->RECEIVE_SID_CHECKAD( Packet->GetData( ) );
  630. break;
  631. case CBNETProtocol :: SID_STARTADVEX3:
  632. if( m_Protocol->RECEIVE_SID_STARTADVEX3( Packet->GetData( ) ) )
  633. {
  634. m_InChat = false;
  635. m_GHost->EventBNETGameRefreshed( this );
  636. }
  637. else
  638. {
  639. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] startadvex3 failed" );
  640. m_GHost->EventBNETGameRefreshFailed( this );
  641. }
  642. break;
  643. case CBNETProtocol :: SID_PING:
  644. m_Socket->PutBytes( m_Protocol->SEND_SID_PING( m_Protocol->RECEIVE_SID_PING( Packet->GetData( ) ) ) );
  645. break;
  646. case CBNETProtocol :: SID_AUTH_INFO:
  647. if( m_Protocol->RECEIVE_SID_AUTH_INFO( Packet->GetData( ) ) )
  648. {
  649. if( m_BNCSUtil->HELP_SID_AUTH_CHECK( m_GHost->m_TFT, m_GHost->m_Warcraft3Path, m_CDKeyROC, m_CDKeyTFT, m_Protocol->GetValueStringFormulaString( ), m_Protocol->GetIX86VerFileNameString( ), m_Protocol->GetClientToken( ), m_Protocol->GetServerToken( ) ) )
  650. {
  651. // override the exe information generated by bncsutil if specified in the config file
  652. // apparently this is useful for pvpgn users
  653. if( m_EXEVersion.size( ) == 4 )
  654. {
  655. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] using custom exe version bnet_custom_exeversion = " + UTIL_ToString( m_EXEVersion[0] ) + " " + UTIL_ToString( m_EXEVersion[1] ) + " " + UTIL_ToString( m_EXEVersion[2] ) + " " + UTIL_ToString( m_EXEVersion[3] ) );
  656. m_BNCSUtil->SetEXEVersion( m_EXEVersion );
  657. }
  658. if( m_EXEVersionHash.size( ) == 4 )
  659. {
  660. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] using custom exe version hash bnet_custom_exeversionhash = " + UTIL_ToString( m_EXEVersionHash[0] ) + " " + UTIL_ToString( m_EXEVersionHash[1] ) + " " + UTIL_ToString( m_EXEVersionHash[2] ) + " " + UTIL_ToString( m_EXEVersionHash[3] ) );
  661. m_BNCSUtil->SetEXEVersionHash( m_EXEVersionHash );
  662. }
  663. if( m_GHost->m_TFT )
  664. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] attempting to auth as Warcraft III: The Frozen Throne" );
  665. else
  666. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] attempting to auth as Warcraft III: Reign of Chaos" );
  667. m_Socket->PutBytes( m_Protocol->SEND_SID_AUTH_CHECK( m_GHost->m_TFT, m_Protocol->GetClientToken( ), m_BNCSUtil->GetEXEVersion( ), m_BNCSUtil->GetEXEVersionHash( ), m_BNCSUtil->GetKeyInfoROC( ), m_BNCSUtil->GetKeyInfoTFT( ), m_BNCSUtil->GetEXEInfo( ), "GHost" ) );
  668. // the Warden seed is the first 4 bytes of the ROC key hash
  669. // initialize the Warden handler
  670. if( !m_BNLSServer.empty( ) )
  671. {
  672. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] creating BNLS client" );
  673. delete m_BNLSClient;
  674. m_BNLSClient = new CBNLSClient( m_BNLSServer, m_BNLSPort, m_BNLSWardenCookie );
  675. m_BNLSClient->QueueWardenSeed( UTIL_ByteArrayToUInt32( m_BNCSUtil->GetKeyInfoROC( ), false, 16 ) );
  676. }
  677. }
  678. else
  679. {
  680. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon failed - bncsutil key hash failed (check your Warcraft 3 path and cd keys), disconnecting" );
  681. m_Socket->Disconnect( );
  682. delete Packet;
  683. return;
  684. }
  685. }
  686. break;
  687. case CBNETProtocol :: SID_AUTH_CHECK:
  688. if( m_Protocol->RECEIVE_SID_AUTH_CHECK( Packet->GetData( ) ) )
  689. {
  690. // cd keys accepted
  691. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] cd keys accepted" );
  692. m_BNCSUtil->HELP_SID_AUTH_ACCOUNTLOGON( );
  693. m_Socket->PutBytes( m_Protocol->SEND_SID_AUTH_ACCOUNTLOGON( m_BNCSUtil->GetClientKey( ), m_UserName ) );
  694. }
  695. else
  696. {
  697. // cd keys not accepted
  698. switch( UTIL_ByteArrayToUInt32( m_Protocol->GetKeyState( ), false ) )
  699. {
  700. case CBNETProtocol :: KR_ROC_KEY_IN_USE:
  701. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon failed - ROC CD key in use by user [" + m_Protocol->GetKeyStateDescription( ) + "], disconnecting" );
  702. break;
  703. case CBNETProtocol :: KR_TFT_KEY_IN_USE:
  704. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon failed - TFT CD key in use by user [" + m_Protocol->GetKeyStateDescription( ) + "], disconnecting" );
  705. break;
  706. case CBNETProtocol :: KR_OLD_GAME_VERSION:
  707. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon failed - game version is too old, disconnecting" );
  708. break;
  709. case CBNETProtocol :: KR_INVALID_VERSION:
  710. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon failed - game version is invalid, disconnecting" );
  711. break;
  712. default:
  713. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon failed - cd keys not accepted, disconnecting" );
  714. break;
  715. }
  716. m_Socket->Disconnect( );
  717. delete Packet;
  718. return;
  719. }
  720. break;
  721. case CBNETProtocol :: SID_AUTH_ACCOUNTLOGON:
  722. if( m_Protocol->RECEIVE_SID_AUTH_ACCOUNTLOGON( Packet->GetData( ) ) )
  723. {
  724. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] username [" + m_UserName + "] accepted" );
  725. if( m_PasswordHashType == "pvpgn" )
  726. {
  727. // pvpgn logon
  728. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] using pvpgn logon type (for pvpgn servers only)" );
  729. m_BNCSUtil->HELP_PvPGNPasswordHash( m_UserPassword );
  730. m_Socket->PutBytes( m_Protocol->SEND_SID_AUTH_ACCOUNTLOGONPROOF( m_BNCSUtil->GetPvPGNPasswordHash( ) ) );
  731. }
  732. else
  733. {
  734. // battle.net logon
  735. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] using battle.net logon type (for official battle.net servers only)" );
  736. m_BNCSUtil->HELP_SID_AUTH_ACCOUNTLOGONPROOF( m_Protocol->GetSalt( ), m_Protocol->GetServerPublicKey( ) );
  737. m_Socket->PutBytes( m_Protocol->SEND_SID_AUTH_ACCOUNTLOGONPROOF( m_BNCSUtil->GetM1( ) ) );
  738. }
  739. }
  740. else
  741. {
  742. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon failed - invalid username, disconnecting" );
  743. m_Socket->Disconnect( );
  744. delete Packet;
  745. return;
  746. }
  747. break;
  748. case CBNETProtocol :: SID_AUTH_ACCOUNTLOGONPROOF:
  749. if( m_Protocol->RECEIVE_SID_AUTH_ACCOUNTLOGONPROOF( Packet->GetData( ) ) )
  750. {
  751. // logon successful
  752. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon successful" );
  753. m_LoggedIn = true;
  754. m_GHost->EventBNETLoggedIn( this );
  755. m_Socket->PutBytes( m_Protocol->SEND_SID_NETGAMEPORT( m_GHost->m_HostPort ) );
  756. m_Socket->PutBytes( m_Protocol->SEND_SID_ENTERCHAT( ) );
  757. m_Socket->PutBytes( m_Protocol->SEND_SID_FRIENDSLIST( ) );
  758. m_Socket->PutBytes( m_Protocol->SEND_SID_CLANMEMBERLIST( ) );
  759. }
  760. else
  761. {
  762. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] logon failed - invalid password, disconnecting" );
  763. // try to figure out if the user might be using the wrong logon type since too many people are confused by this
  764. string Server = m_Server;
  765. transform( Server.begin( ), Server.end( ), Server.begin( ), (int(*)(int))tolower );
  766. if( m_PasswordHashType == "pvpgn" && ( Server == "useast.battle.net" || Server == "uswest.battle.net" || Server == "asia.battle.net" || Server == "europe.battle.net" ) )
  767. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] it looks like you're trying to connect to a battle.net server using a pvpgn logon type, check your config file's \"battle.net custom data\" section" );
  768. else if( m_PasswordHashType != "pvpgn" && ( Server != "useast.battle.net" && Server != "uswest.battle.net" && Server != "asia.battle.net" && Server != "europe.battle.net" ) )
  769. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] it looks like you're trying to connect to a pvpgn server using a battle.net logon type, check your config file's \"battle.net custom data\" section" );
  770. m_Socket->Disconnect( );
  771. delete Packet;
  772. return;
  773. }
  774. break;
  775. case CBNETProtocol :: SID_WARDEN:
  776. WardenData = m_Protocol->RECEIVE_SID_WARDEN( Packet->GetData( ) );
  777. if( m_BNLSClient )
  778. m_BNLSClient->QueueWardenRaw( WardenData );
  779. else
  780. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] warning - received warden packet but no BNLS server is available, you will be kicked from battle.net soon" );
  781. break;
  782. case CBNETProtocol :: SID_FRIENDSLIST:
  783. Friends = m_Protocol->RECEIVE_SID_FRIENDSLIST( Packet->GetData( ) );
  784. for( vector<CIncomingFriendList *> :: iterator i = m_Friends.begin( ); i != m_Friends.end( ); i++ )
  785. delete *i;
  786. m_Friends = Friends;
  787. break;
  788. case CBNETProtocol :: SID_CLANMEMBERLIST:
  789. vector<CIncomingClanList *> Clans = m_Protocol->RECEIVE_SID_CLANMEMBERLIST( Packet->GetData( ) );
  790. for( vector<CIncomingClanList *> :: iterator i = m_Clans.begin( ); i != m_Clans.end( ); i++ )
  791. delete *i;
  792. m_Clans = Clans;
  793. break;
  794. }
  795. }
  796. delete Packet;
  797. }
  798. }
  799. void CBNET :: ProcessChatEvent( CIncomingChatEvent *chatEvent )
  800. {
  801. CBNETProtocol :: IncomingChatEvent Event = chatEvent->GetChatEvent( );
  802. uint32_t UserFlags = chatEvent->GetUserFlags( );
  803. bool Whisper = ( Event == CBNETProtocol :: EID_WHISPER );
  804. bool WhisperResponses = ( m_GHost->m_WhisperResponses );
  805. string User = chatEvent->GetUser( );
  806. string Message = chatEvent->GetMessage( );
  807. if( Event == CBNETProtocol :: EID_SHOWUSER )
  808. CONSOLE_AddChannelUser( User, GetRealmId( ), UserFlags );
  809. else if( Event == CBNETProtocol :: EID_USERFLAGS )
  810. CONSOLE_UpdateChannelUser( User, GetRealmId( ), UserFlags );
  811. else if( Event == CBNETProtocol :: EID_JOIN )
  812. CONSOLE_AddChannelUser( User, GetRealmId( ), UserFlags );
  813. else if( Event == CBNETProtocol :: EID_LEAVE )
  814. CONSOLE_RemoveChannelUser( User, GetRealmId( ));
  815. else if( Event == CBNETProtocol :: EID_WHISPER )
  816. {
  817. m_ReplyTarget = User;
  818. CONSOLE_Print( "[WHISPER: " + m_ServerAlias + "] [" + User + "] " + Message, GetRealmId( ), false );
  819. m_GHost->EventBNETWhisper( this, User, Message );
  820. }
  821. else if( Event == CBNETProtocol :: EID_TALK )
  822. {
  823. CONSOLE_Print( "[LOCAL: " + m_ServerAlias + "] [" + User + "] " + Message, GetRealmId( ), false );
  824. m_GHost->EventBNETChat( this, User, Message );
  825. }
  826. else if( Event == CBNETProtocol :: EID_BROADCAST )
  827. {
  828. CONSOLE_Print( "[BROADCAST: " + m_ServerAlias + "] " + Message, GetRealmId( ), false );
  829. m_GHost->EventBNETBROADCAST( this, User, Message );
  830. }
  831. else if( Event == CBNETProtocol :: EID_CHANNEL )
  832. {
  833. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] joined channel [" + Message + "]", GetRealmId( ), false );
  834. m_CurrentChannel = Message;
  835. CONSOLE_ChangeChannel( Message, GetRealmId( ) );
  836. CONSOLE_RemoveChannelUsers( GetRealmId( ) );
  837. CONSOLE_AddChannelUser( m_UserName, GetRealmId( ), UserFlags );
  838. m_GHost->EventBNETCHANNEL( this, User, Message );
  839. }
  840. else if( Event == CBNETProtocol :: EID_WHISPERSENT )
  841. {
  842. CONSOLE_Print ( "[WHISPERED: " + m_ServerAlias + "] [" + User + "] " + Message, GetRealmId( ), false );
  843. m_GHost->EventBNETWHISPERSENT( this, User, Message );
  844. }
  845. else if( Event == CBNETProtocol :: EID_CHANNELFULL )
  846. {
  847. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] channel is full", GetRealmId( ), false );
  848. m_GHost->EventBNETCHANNELFULL( this, User, Message );
  849. }
  850. else if( Event == CBNETProtocol :: EID_CHANNELDOESNOTEXIST )
  851. {
  852. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] channel does not exist", GetRealmId( ), false );
  853. m_GHost->EventBNETCHANNELDOESNOTEXIST( this, User, Message );
  854. }
  855. else if( Event == CBNETProtocol :: EID_CHANNELRESTRICTED )
  856. {
  857. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] channel restricted", GetRealmId( ), false );
  858. m_GHost->EventBNETCHANNELRESTRICTED( this, User, Message );
  859. }
  860. else if( Event == CBNETProtocol :: EID_ERROR )
  861. {
  862. CONSOLE_Print( "[ERROR: " + m_ServerAlias + "] " + Message, GetRealmId( ), false );
  863. m_GHost->EventBNETError( this, User, Message );
  864. }
  865. else if( Event == CBNETProtocol :: EID_EMOTE )
  866. {
  867. CONSOLE_Print( "[EMOTE: " + m_ServerAlias + "] [" + User + "] " + Message, GetRealmId( ), false );
  868. m_GHost->EventBNETEmote( this, User, Message );
  869. }
  870. if( Event == CBNETProtocol :: EID_WHISPER || Event == CBNETProtocol :: EID_TALK || Event == 29 )
  871. {
  872. // handle spoof checking for current game
  873. // this case covers whispers - we assume that anyone who sends a whisper to the bot with message "spoofcheck" should be considered spoof checked
  874. // note that this means you can whisper "spoofcheck" even in a public game to manually spoofcheck if the /whois fails
  875. if( Event == CBNETProtocol :: EID_WHISPER && m_GHost->m_CurrentGame )
  876. {
  877. if( Message == "s" || Message == "sc" || Message == "spoof" || Message == "check" || Message == "spoofcheck" )
  878. m_GHost->m_CurrentGame->AddToSpoofed( m_Server, User, true );
  879. else if( Message.find( m_GHost->m_CurrentGame->GetGameName( ) ) != string :: npos )
  880. {
  881. // look for messages like "entered a Warcraft III The Frozen Throne game called XYZ"
  882. // we don't look for the English part of the text anymore because we want this to work with multiple languages
  883. // it's a pretty safe bet that anyone whispering the bot with a message containing the game name is a valid spoofcheck
  884. if( m_PasswordHashType == "pvpgn" && User == m_PVPGNRealmName )
  885. {
  886. // the equivalent pvpgn message is: [PvPGN Realm] Your friend abc has entered a Warcraft III Frozen Throne game named "xyz".
  887. vector<string> Tokens = UTIL_Tokenize( Message, ' ' );
  888. if( Tokens.size( ) >= 3 )
  889. m_GHost->m_CurrentGame->AddToSpoofed( m_Server, Tokens[2], false );
  890. }
  891. else
  892. m_GHost->m_CurrentGame->AddToSpoofed( m_Server, User, false );
  893. }
  894. }
  895. // handle bot commands
  896. if( Message == "?trigger" && ( IsAdmin( User ) || IsRootAdmin( User ) || ( m_PublicCommands && m_OutPackets.size( ) <= 3 ) ) )
  897. QueueChatCommand( m_GHost->m_Language->CommandTrigger( string( 1, m_CommandTrigger ) ), User, Whisper, WhisperResponses );
  898. else if( !Message.empty( ) && Message[0] == m_CommandTrigger )
  899. {
  900. // extract the command trigger, the command, and the payload
  901. // e.g. "!say hello world" -> command: "say", payload: "hello world"
  902. string Command;
  903. string Payload;
  904. string :: size_type PayloadStart = Message.find( " " );
  905. if( PayloadStart != string :: npos )
  906. {
  907. Command = Message.substr( 1, PayloadStart - 1 );
  908. Payload = Message.substr( PayloadStart + 1 );
  909. }
  910. else
  911. Command = Message.substr( 1 );
  912. transform( Command.begin( ), Command.end( ), Command.begin( ), (int(*)(int))tolower );
  913. if( IsAdmin( User ) || IsRootAdmin( User ) )
  914. {
  915. if( User == "" )
  916. User = m_UserName;
  917. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] admin [" + User + "] sent command [" + Message + "]" );
  918. /*****************
  919. * ADMIN COMMANDS *
  920. ******************/
  921. //
  922. // !ADDADMIN
  923. //
  924. if( Command == "addadmin" && !Payload.empty( ) )
  925. {
  926. if( IsRootAdmin( User ) )
  927. {
  928. if( IsAdmin( Payload ) )
  929. QueueChatCommand( m_GHost->m_Language->UserIsAlreadyAnAdmin( m_Server, Payload ), User, Whisper, WhisperResponses );
  930. else
  931. m_PairedAdminAdds.push_back( PairedAdminAdd( Whisper ? User : string( ), m_GHost->m_DB->ThreadedAdminAdd( m_Server, Payload ) ) );
  932. }
  933. else
  934. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  935. }
  936. //
  937. // !ADDBAN
  938. // !BAN
  939. //
  940. if( ( Command == "addban" || Command == "ban" ) && !Payload.empty( ) )
  941. {
  942. // extract the victim and the reason
  943. // e.g. "Varlock leaver after dying" -> victim: "Varlock", reason: "leaver after dying"
  944. string Victim;
  945. string Reason;
  946. stringstream SS;
  947. SS << Payload;
  948. SS >> Victim;
  949. if( !SS.eof( ) )
  950. {
  951. getline( SS, Reason );
  952. string :: size_type Start = Reason.find_first_not_of( " " );
  953. if( Start != string :: npos )
  954. Reason = Reason.substr( Start );
  955. }
  956. if( IsBannedName( Victim ) )
  957. QueueChatCommand( m_GHost->m_Language->UserIsAlreadyBanned( m_Server, Victim ), User, Whisper, false );
  958. else
  959. m_PairedBanAdds.push_back( PairedBanAdd( Whisper ? User : string( ), m_GHost->m_DB->ThreadedBanAdd( m_Server, Victim, string( ), string( ), User, Reason ) ) );
  960. }
  961. //
  962. // !ANNOUNCE
  963. //
  964. if( Command == "announce" && m_GHost->m_CurrentGame && !m_GHost->m_CurrentGame->GetCountDownStarted( ) )
  965. {
  966. if( Payload.empty( ) || Payload == "off" )
  967. {
  968. QueueChatCommand( m_GHost->m_Language->AnnounceMessageDisabled( ), User, Whisper, WhisperResponses );
  969. m_GHost->m_CurrentGame->SetAnnounce( 0, string( ) );
  970. }
  971. else
  972. {
  973. // extract the interval and the message
  974. // e.g. "30 hello everyone" -> interval: "30", message: "hello everyone"
  975. uint32_t Interval;
  976. string Message;
  977. stringstream SS;
  978. SS << Payload;
  979. SS >> Interval;
  980. if( SS.fail( ) || Interval == 0 )
  981. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #1 to announce command" );
  982. else
  983. {
  984. if( SS.eof( ) )
  985. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] missing input #2 to announce command" );
  986. else
  987. {
  988. getline( SS, Message );
  989. string :: size_type Start = Message.find_first_not_of( " " );
  990. if( Start != string :: npos )
  991. Message = Message.substr( Start );
  992. QueueChatCommand( m_GHost->m_Language->AnnounceMessageEnabled( ), User, Whisper, WhisperResponses );
  993. m_GHost->m_CurrentGame->SetAnnounce( Interval, Message );
  994. }
  995. }
  996. }
  997. }
  998. //
  999. // !AUTOHOST
  1000. //
  1001. if( Command == "autohost" )
  1002. {
  1003. if( IsRootAdmin( User ) )
  1004. {
  1005. if( Payload.empty( ) || Payload == "off" )
  1006. {
  1007. QueueChatCommand( m_GHost->m_Language->AutoHostDisabled( ), User, Whisper, WhisperResponses );
  1008. m_GHost->m_AutoHostGameName.clear( );
  1009. m_GHost->m_AutoHostOwner.clear( );
  1010. m_GHost->m_AutoHostServer.clear( );
  1011. m_GHost->m_AutoHostMaximumGames = 0;
  1012. m_GHost->m_AutoHostAutoStartPlayers = 0;
  1013. m_GHost->m_LastAutoHostTime = GetTime( );
  1014. m_GHost->m_AutoHostMatchMaking = false;
  1015. m_GHost->m_AutoHostMinimumScore = 0.0;
  1016. m_GHost->m_AutoHostMaximumScore = 0.0;
  1017. }
  1018. else
  1019. {
  1020. // extract the maximum games, auto start players, and the game name
  1021. // e.g. "5 10 BattleShips Pro" -> maximum games: "5", auto start players: "10", game name: "BattleShips Pro"
  1022. uint32_t MaximumGames;
  1023. uint32_t AutoStartPlayers;
  1024. string GameName;
  1025. stringstream SS;
  1026. SS << Payload;
  1027. SS >> MaximumGames;
  1028. if( SS.fail( ) || MaximumGames == 0 )
  1029. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #1 to autohost command" );
  1030. else
  1031. {
  1032. SS >> AutoStartPlayers;
  1033. if( SS.fail( ) || AutoStartPlayers == 0 )
  1034. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #2 to autohost command" );
  1035. else
  1036. {
  1037. if( SS.eof( ) )
  1038. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] missing input #3 to autohost command" );
  1039. else
  1040. {
  1041. getline( SS, GameName );
  1042. string :: size_type Start = GameName.find_first_not_of( " " );
  1043. if( Start != string :: npos )
  1044. GameName = GameName.substr( Start );
  1045. QueueChatCommand( m_GHost->m_Language->AutoHostEnabled( ), User, Whisper, WhisperResponses );
  1046. delete m_GHost->m_AutoHostMap;
  1047. m_GHost->m_AutoHostMap = new CMap( *m_GHost->m_Map );
  1048. m_GHost->m_AutoHostGameName = GameName;
  1049. m_GHost->m_AutoHostOwner = User;
  1050. m_GHost->m_AutoHostServer = m_Server;
  1051. m_GHost->m_AutoHostMaximumGames = MaximumGames;
  1052. m_GHost->m_AutoHostAutoStartPlayers = AutoStartPlayers;
  1053. m_GHost->m_LastAutoHostTime = GetTime( );
  1054. m_GHost->m_AutoHostMatchMaking = false;
  1055. m_GHost->m_AutoHostMinimumScore = 0.0;
  1056. m_GHost->m_AutoHostMaximumScore = 0.0;
  1057. }
  1058. }
  1059. }
  1060. }
  1061. }
  1062. else
  1063. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1064. }
  1065. //
  1066. // !AUTOHOSTMM
  1067. //
  1068. if( Command == "autohostmm" )
  1069. {
  1070. if( IsRootAdmin( User ) )
  1071. {
  1072. if( Payload.empty( ) || Payload == "off" )
  1073. {
  1074. QueueChatCommand( m_GHost->m_Language->AutoHostDisabled( ), User, Whisper, WhisperResponses );
  1075. m_GHost->m_AutoHostGameName.clear( );
  1076. m_GHost->m_AutoHostOwner.clear( );
  1077. m_GHost->m_AutoHostServer.clear( );
  1078. m_GHost->m_AutoHostMaximumGames = 0;
  1079. m_GHost->m_AutoHostAutoStartPlayers = 0;
  1080. m_GHost->m_LastAutoHostTime = GetTime( );
  1081. m_GHost->m_AutoHostMatchMaking = false;
  1082. m_GHost->m_AutoHostMinimumScore = 0.0;
  1083. m_GHost->m_AutoHostMaximumScore = 0.0;
  1084. }
  1085. else
  1086. {
  1087. // extract the maximum games, auto start players, minimum score, maximum score, and the game name
  1088. // e.g. "5 10 800 1200 BattleShips Pro" -> maximum games: "5", auto start players: "10", minimum score: "800", maximum score: "1200", game name: "BattleShips Pro"
  1089. uint32_t MaximumGames;
  1090. uint32_t AutoStartPlayers;
  1091. double MinimumScore;
  1092. double MaximumScore;
  1093. string GameName;
  1094. stringstream SS;
  1095. SS << Payload;
  1096. SS >> MaximumGames;
  1097. if( SS.fail( ) || MaximumGames == 0 )
  1098. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #1 to autohostmm command" );
  1099. else
  1100. {
  1101. SS >> AutoStartPlayers;
  1102. if( SS.fail( ) || AutoStartPlayers == 0 )
  1103. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #2 to autohostmm command" );
  1104. else
  1105. {
  1106. SS >> MinimumScore;
  1107. if( SS.fail( ) )
  1108. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #3 to autohostmm command" );
  1109. else
  1110. {
  1111. SS >> MaximumScore;
  1112. if( SS.fail( ) )
  1113. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #4 to autohostmm command" );
  1114. else
  1115. {
  1116. if( SS.eof( ) )
  1117. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] missing input #5 to autohostmm command" );
  1118. else
  1119. {
  1120. getline( SS, GameName );
  1121. string :: size_type Start = GameName.find_first_not_of( " " );
  1122. if( Start != string :: npos )
  1123. GameName = GameName.substr( Start );
  1124. QueueChatCommand( m_GHost->m_Language->AutoHostEnabled( ), User, Whisper, WhisperResponses );
  1125. delete m_GHost->m_AutoHostMap;
  1126. m_GHost->m_AutoHostMap = new CMap( *m_GHost->m_Map );
  1127. m_GHost->m_AutoHostGameName = GameName;
  1128. m_GHost->m_AutoHostOwner = User;
  1129. m_GHost->m_AutoHostServer = m_Server;
  1130. m_GHost->m_AutoHostMaximumGames = MaximumGames;
  1131. m_GHost->m_AutoHostAutoStartPlayers = AutoStartPlayers;
  1132. m_GHost->m_LastAutoHostTime = GetTime( );
  1133. m_GHost->m_AutoHostMatchMaking = true;
  1134. m_GHost->m_AutoHostMinimumScore = MinimumScore;
  1135. m_GHost->m_AutoHostMaximumScore = MaximumScore;
  1136. }
  1137. }
  1138. }
  1139. }
  1140. }
  1141. }
  1142. }
  1143. else
  1144. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1145. }
  1146. //
  1147. // !AUTOSTART
  1148. //
  1149. if( Command == "autostart" && m_GHost->m_CurrentGame && !m_GHost->m_CurrentGame->GetCountDownStarted( ) )
  1150. {
  1151. if( Payload.empty( ) || Payload == "off" )
  1152. {
  1153. QueueChatCommand( m_GHost->m_Language->AutoStartDisabled( ), User, Whisper, WhisperResponses );
  1154. m_GHost->m_CurrentGame->SetAutoStartPlayers( 0 );
  1155. }
  1156. else
  1157. {
  1158. uint32_t AutoStartPlayers = UTIL_ToUInt32( Payload );
  1159. if( AutoStartPlayers != 0 )
  1160. {
  1161. QueueChatCommand( m_GHost->m_Language->AutoStartEnabled( UTIL_ToString( AutoStartPlayers ) ), User, Whisper, WhisperResponses );
  1162. m_GHost->m_CurrentGame->SetAutoStartPlayers( AutoStartPlayers );
  1163. }
  1164. }
  1165. }
  1166. //
  1167. // !CHANNEL (change channel)
  1168. //
  1169. if( Command == "channel" && !Payload.empty( ) )
  1170. QueueChatCommand( "/join " + Payload );
  1171. //
  1172. // !CHECKADMIN
  1173. //
  1174. if( Command == "checkadmin" && !Payload.empty( ) )
  1175. {
  1176. if( IsRootAdmin( User ) )
  1177. {
  1178. if( IsAdmin( Payload ) )
  1179. QueueChatCommand( m_GHost->m_Language->UserIsAnAdmin( m_Server, Payload ), User, Whisper, WhisperResponses );
  1180. else
  1181. QueueChatCommand( m_GHost->m_Language->UserIsNotAnAdmin( m_Server, Payload ), User, Whisper, WhisperResponses );
  1182. }
  1183. else
  1184. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1185. }
  1186. //
  1187. // !CHECKBAN
  1188. //
  1189. if( Command == "checkban" && !Payload.empty( ) )
  1190. {
  1191. CDBBan *Ban = IsBannedName( Payload );
  1192. if( Ban )
  1193. QueueChatCommand( m_GHost->m_Language->UserWasBannedOnByBecause( m_Server, Payload, Ban->GetDate( ), Ban->GetAdmin( ), Ban->GetReason( ) ), User, Whisper, WhisperResponses );
  1194. else
  1195. QueueChatCommand( m_GHost->m_Language->UserIsNotBanned( m_Server, Payload ), User, Whisper, WhisperResponses );
  1196. }
  1197. //
  1198. // !CLOSE (close slot)
  1199. //
  1200. if( Command == "close" && !Payload.empty( ) && m_GHost->m_CurrentGame )
  1201. {
  1202. if( !m_GHost->m_CurrentGame->GetLocked( ) )
  1203. {
  1204. // close as many slots as specified, e.g. "5 10" closes slots 5 and 10
  1205. stringstream SS;
  1206. SS << Payload;
  1207. while( !SS.eof( ) )
  1208. {
  1209. uint32_t SID;
  1210. SS >> SID;
  1211. if( SS.fail( ) )
  1212. {
  1213. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input to close command" );
  1214. break;
  1215. }
  1216. else
  1217. m_GHost->m_CurrentGame->CloseSlot( (unsigned char)( SID - 1 ), true );
  1218. }
  1219. }
  1220. else
  1221. QueueChatCommand( m_GHost->m_Language->TheGameIsLockedBNET( ), User, Whisper, WhisperResponses );
  1222. }
  1223. //
  1224. // !CLOSEALL
  1225. //
  1226. if( Command == "closeall" && m_GHost->m_CurrentGame )
  1227. {
  1228. if( !m_GHost->m_CurrentGame->GetLocked( ) )
  1229. m_GHost->m_CurrentGame->CloseAllSlots( );
  1230. else
  1231. QueueChatCommand( m_GHost->m_Language->TheGameIsLockedBNET( ), User, Whisper, WhisperResponses );
  1232. }
  1233. //
  1234. // !COUNTADMINS
  1235. //
  1236. if( Command == "countadmins" )
  1237. {
  1238. if( IsRootAdmin( User ) )
  1239. m_PairedAdminCounts.push_back( PairedAdminCount( Whisper ? User : string( ), m_GHost->m_DB->ThreadedAdminCount( m_Server ) ) );
  1240. else
  1241. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1242. }
  1243. //
  1244. // !COUNTBANS
  1245. //
  1246. if( Command == "countbans" )
  1247. m_PairedBanCounts.push_back( PairedBanCount( Whisper ? User : string( ), m_GHost->m_DB->ThreadedBanCount( m_Server ) ) );
  1248. //
  1249. // !DBSTATUS
  1250. //
  1251. if( Command == "dbstatus" )
  1252. QueueChatCommand( m_GHost->m_DB->GetStatus( ), User, Whisper, WhisperResponses );
  1253. //
  1254. // !DELADMIN
  1255. //
  1256. if( Command == "deladmin" && !Payload.empty( ) )
  1257. {
  1258. if( IsRootAdmin( User ) )
  1259. {
  1260. if( !IsAdmin( Payload ) )
  1261. QueueChatCommand( m_GHost->m_Language->UserIsNotAnAdmin( m_Server, Payload ), User, Whisper, WhisperResponses );
  1262. else
  1263. m_PairedAdminRemoves.push_back( PairedAdminRemove( Whisper ? User : string( ), m_GHost->m_DB->ThreadedAdminRemove( m_Server, Payload ) ) );
  1264. }
  1265. else
  1266. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1267. }
  1268. //
  1269. // !DELBAN
  1270. // !UNBAN
  1271. //
  1272. if( ( Command == "delban" || Command == "unban" ) && !Payload.empty( ) )
  1273. m_PairedBanRemoves.push_back( PairedBanRemove( Whisper ? User : string( ), m_GHost->m_DB->ThreadedBanRemove( Payload ) ) );
  1274. //
  1275. // !DISABLE
  1276. //
  1277. if( Command == "disable" )
  1278. {
  1279. if( IsRootAdmin( User ) )
  1280. {
  1281. QueueChatCommand( m_GHost->m_Language->BotDisabled( ), User, Whisper, WhisperResponses );
  1282. m_GHost->m_Enabled = false;
  1283. }
  1284. else
  1285. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1286. }
  1287. //
  1288. // !DOWNLOADS
  1289. //
  1290. if( Command == "downloads" && !Payload.empty( ) )
  1291. {
  1292. uint32_t Downloads = UTIL_ToUInt32( Payload );
  1293. if( Downloads == 0 )
  1294. {
  1295. QueueChatCommand( m_GHost->m_Language->MapDownloadsDisabled( ), User, Whisper, WhisperResponses );
  1296. m_GHost->m_AllowDownloads = 0;
  1297. }
  1298. else if( Downloads == 1 )
  1299. {
  1300. QueueChatCommand( m_GHost->m_Language->MapDownloadsEnabled( ), User, Whisper, WhisperResponses );
  1301. m_GHost->m_AllowDownloads = 1;
  1302. }
  1303. else if( Downloads == 2 )
  1304. {
  1305. QueueChatCommand( m_GHost->m_Language->MapDownloadsConditional( ), User, Whisper, WhisperResponses );
  1306. m_GHost->m_AllowDownloads = 2;
  1307. }
  1308. }
  1309. //
  1310. // !EMOTE
  1311. // shade0o - Added in this command myself to aloud emotes since cant ".say /me" anymore
  1312. // *(GCBC)*
  1313. if( ( Command == "emote" || Command == "em" ) && !Payload.empty( ) )
  1314. QueueChatCommand( "/me " + Payload );
  1315. //
  1316. // !ENABLE
  1317. //
  1318. if( Command == "enable" )
  1319. {
  1320. if( IsRootAdmin( User ) )
  1321. {
  1322. QueueChatCommand( m_GHost->m_Language->BotEnabled( ), User, Whisper, WhisperResponses );
  1323. m_GHost->m_Enabled = true;
  1324. }
  1325. else
  1326. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1327. }
  1328. //
  1329. // !END
  1330. //
  1331. if( Command == "end" && !Payload.empty( ) )
  1332. {
  1333. // todotodo: what if a game ends just as you're typing this command and the numbering changes?
  1334. uint32_t GameNumber = UTIL_ToUInt32( Payload ) - 1;
  1335. if( GameNumber < m_GHost->m_Games.size( ) )
  1336. {
  1337. // if the game owner is still in the game only allow the root admin to end the game
  1338. if( m_GHost->m_Games[GameNumber]->GetPlayerFromName( m_GHost->m_Games[GameNumber]->GetOwnerName( ), false ) && !IsRootAdmin( User ) )
  1339. QueueChatCommand( m_GHost->m_Language->CantEndGameOwnerIsStillPlaying( m_GHost->m_Games[GameNumber]->GetOwnerName( ) ), User, Whisper, false );
  1340. else
  1341. {
  1342. QueueChatCommand( m_GHost->m_Language->EndingGame( m_GHost->m_Games[GameNumber]->GetDescription( ) ), User, Whisper, false );
  1343. CONSOLE_Print( "[GAME: " + m_GHost->m_Games[GameNumber]->GetGameName( ) + "] is over (admin ended game)" );
  1344. m_GHost->m_Games[GameNumber]->StopPlayers( "was disconnected (admin ended game)" );
  1345. }
  1346. }
  1347. else
  1348. QueueChatCommand( m_GHost->m_Language->GameNumberDoesntExist( Payload ), User, Whisper, WhisperResponses );
  1349. }
  1350. //
  1351. // !ENFORCESG
  1352. //
  1353. if( Command == "enforcesg" && !Payload.empty( ) )
  1354. {
  1355. // only load files in the current directory just to be safe
  1356. if( Payload.find( "/" ) != string :: npos || Payload.find( "\\" ) != string :: npos )
  1357. QueueChatCommand( m_GHost->m_Language->UnableToLoadReplaysOutside( ), User, Whisper, WhisperResponses );
  1358. else
  1359. {
  1360. string File = m_GHost->m_ReplayPath + Payload + ".w3g";
  1361. if( UTIL_FileExists( File ) )
  1362. {
  1363. QueueChatCommand( m_GHost->m_Language->LoadingReplay( File ), User, Whisper, WhisperResponses );
  1364. CReplay *Replay = new CReplay( );
  1365. Replay->Load( File, false );
  1366. Replay->ParseReplay( false );
  1367. m_GHost->m_EnforcePlayers = Replay->GetPlayers( );
  1368. delete Replay;
  1369. }
  1370. else
  1371. QueueChatCommand( m_GHost->m_Language->UnableToLoadReplayDoesntExist( File ), User, Whisper, WhisperResponses );
  1372. }
  1373. }
  1374. //
  1375. // !EXIT
  1376. // !QUIT
  1377. //
  1378. if( Command == "exit" || Command == "quit" )
  1379. {
  1380. if( IsRootAdmin( User ) )
  1381. {
  1382. if( Payload == "nice" )
  1383. m_GHost->m_ExitingNice = true;
  1384. else if( Payload == "force" )
  1385. m_Exiting = true;
  1386. else
  1387. {
  1388. if( m_GHost->m_CurrentGame || !m_GHost->m_Games.empty( ) )
  1389. QueueChatCommand( m_GHost->m_Language->AtLeastOneGameActiveUseForceToShutdown( ), User, Whisper, WhisperResponses );
  1390. else
  1391. m_Exiting = true;
  1392. }
  1393. }
  1394. else
  1395. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1396. }
  1397. //
  1398. // !GETCLAN
  1399. //
  1400. if( Command == "getclan" )
  1401. {
  1402. SendGetClanList( );
  1403. QueueChatCommand( m_GHost->m_Language->UpdatingClanList( ), User, Whisper, WhisperResponses );
  1404. }
  1405. //
  1406. // !GETFRIENDS
  1407. //
  1408. if( Command == "getfriends" )
  1409. {
  1410. SendGetFriendsList( );
  1411. QueueChatCommand( m_GHost->m_Language->UpdatingFriendsList( ), User, Whisper, WhisperResponses );
  1412. }
  1413. //
  1414. // !GETGAME
  1415. //
  1416. if( Command == "getgame" && !Payload.empty( ) )
  1417. {
  1418. uint32_t GameNumber = UTIL_ToUInt32( Payload ) - 1;
  1419. if( GameNumber < m_GHost->m_Games.size( ) )
  1420. QueueChatCommand( m_GHost->m_Language->GameNumberIs( Payload, m_GHost->m_Games[GameNumber]->GetDescription( ) ), User, Whisper, WhisperResponses );
  1421. else
  1422. QueueChatCommand( m_GHost->m_Language->GameNumberDoesntExist( Payload ), User, Whisper, WhisperResponses );
  1423. }
  1424. //
  1425. // !GETGAMES
  1426. //
  1427. if( Command == "getgames" )
  1428. {
  1429. if( m_GHost->m_CurrentGame )
  1430. QueueChatCommand( m_GHost->m_Language->GameIsInTheLobby( m_GHost->m_CurrentGame->GetDescription( ), UTIL_ToString( m_GHost->m_Games.size( ) ), UTIL_ToString( m_GHost->m_MaxGames ) ), User, Whisper, WhisperResponses );
  1431. else
  1432. QueueChatCommand( m_GHost->m_Language->ThereIsNoGameInTheLobby( UTIL_ToString( m_GHost->m_Games.size( ) ), UTIL_ToString( m_GHost->m_MaxGames ) ), User, Whisper, WhisperResponses );
  1433. }
  1434. //
  1435. // !HOLD (hold a slot for someone)
  1436. //
  1437. if( Command == "hold" && !Payload.empty( ) && m_GHost->m_CurrentGame )
  1438. {
  1439. // hold as many players as specified, e.g. "Varlock Kilranin" holds players "Varlock" and "Kilranin"
  1440. stringstream SS;
  1441. SS << Payload;
  1442. while( !SS.eof( ) )
  1443. {
  1444. string HoldName;
  1445. SS >> HoldName;
  1446. if( SS.fail( ) )
  1447. {
  1448. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input to hold command" );
  1449. break;
  1450. }
  1451. else
  1452. {
  1453. QueueChatCommand( m_GHost->m_Language->AddedPlayerToTheHoldList( HoldName ), User, Whisper, WhisperResponses );
  1454. m_GHost->m_CurrentGame->AddToReserved( HoldName );
  1455. }
  1456. }
  1457. }
  1458. //
  1459. // !HOSTSG
  1460. //
  1461. if( Command == "hostsg" && !Payload.empty( ) )
  1462. m_GHost->CreateGame( m_GHost->m_Map, GAME_PRIVATE, true, Payload, User, User, m_Server, Whisper );
  1463. //
  1464. // !LOAD (load config file)
  1465. //
  1466. if( Command == "load" )
  1467. {
  1468. if( Payload.empty( ) )
  1469. QueueChatCommand( m_GHost->m_Language->CurrentlyLoadedMapCFGIs( m_GHost->m_Map->GetCFGFile( ) ), User, Whisper, WhisperResponses );
  1470. else
  1471. {
  1472. string FoundMapConfigs;
  1473. try
  1474. {
  1475. path MapCFGPath( m_GHost->m_MapCFGPath );
  1476. string Pattern = Payload;
  1477. transform( Pattern.begin( ), Pattern.end( ), Pattern.begin( ), (int(*)(int))tolower );
  1478. if( !exists( MapCFGPath ) )
  1479. {
  1480. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] error listing map configs - map config path doesn't exist" );
  1481. QueueChatCommand( m_GHost->m_Language->ErrorListingMapConfigs( ), User, Whisper, WhisperResponses );
  1482. }
  1483. else
  1484. {
  1485. directory_iterator EndIterator;
  1486. path LastMatch;
  1487. uint32_t Matches = 0;
  1488. for( directory_iterator i( MapCFGPath ); i != EndIterator; i++ )
  1489. {
  1490. string FileName = i->filename( );
  1491. string Stem = i->path( ).stem( );
  1492. transform( FileName.begin( ), FileName.end( ), FileName.begin( ), (int(*)(int))tolower );
  1493. transform( Stem.begin( ), Stem.end( ), Stem.begin( ), (int(*)(int))tolower );
  1494. if( !is_directory( i->status( ) ) && i->path( ).extension( ) == ".cfg" && FileName.find( Pattern ) != string :: npos )
  1495. {
  1496. LastMatch = i->path( );
  1497. Matches++;
  1498. if( FoundMapConfigs.empty( ) )
  1499. FoundMapConfigs = i->filename( );
  1500. else
  1501. FoundMapConfigs += ", " + i->filename( );
  1502. // if the pattern matches the filename exactly, with or without extension, stop any further matching
  1503. if( FileName == Pattern || Stem == Pattern )
  1504. {
  1505. Matches = 1;
  1506. break;
  1507. }
  1508. }
  1509. }
  1510. if( Matches == 0 )
  1511. QueueChatCommand( m_GHost->m_Language->NoMapConfigsFound( ), User, Whisper, WhisperResponses );
  1512. else if( Matches == 1 )
  1513. {
  1514. string File = LastMatch.filename( );
  1515. QueueChatCommand( m_GHost->m_Language->LoadingConfigFile( m_GHost->m_MapCFGPath + File ), User, Whisper, WhisperResponses );
  1516. CConfig MapCFG;
  1517. MapCFG.Read( LastMatch.string( ) );
  1518. m_GHost->m_Map->Load( &MapCFG, m_GHost->m_MapCFGPath + File );
  1519. }
  1520. else
  1521. QueueChatCommand( m_GHost->m_Language->FoundMapConfigs( FoundMapConfigs ), User, Whisper, WhisperResponses );
  1522. }
  1523. }
  1524. catch( const exception &ex )
  1525. {
  1526. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] error listing map configs - caught exception [" + ex.what( ) + "]" );
  1527. QueueChatCommand( m_GHost->m_Language->ErrorListingMapConfigs( ), User, Whisper, WhisperResponses );
  1528. }
  1529. }
  1530. }
  1531. //
  1532. // !LOADSG
  1533. //
  1534. if( Command == "loadsg" && !Payload.empty( ) )
  1535. {
  1536. // only load files in the current directory just to be safe
  1537. if( Payload.find( "/" ) != string :: npos || Payload.find( "\\" ) != string :: npos )
  1538. QueueChatCommand( m_GHost->m_Language->UnableToLoadSaveGamesOutside( ), User, Whisper, WhisperResponses );
  1539. else
  1540. {
  1541. string File = m_GHost->m_SaveGamePath + Payload + ".w3z";
  1542. string FileNoPath = Payload + ".w3z";
  1543. if( UTIL_FileExists( File ) )
  1544. {
  1545. if( m_GHost->m_CurrentGame )
  1546. QueueChatCommand( m_GHost->m_Language->UnableToLoadSaveGameGameInLobby( ), User, Whisper, WhisperResponses );
  1547. else
  1548. {
  1549. QueueChatCommand( m_GHost->m_Language->LoadingSaveGame( File ), User, Whisper, WhisperResponses );
  1550. m_GHost->m_SaveGame->Load( File, false );
  1551. m_GHost->m_SaveGame->ParseSaveGame( );
  1552. m_GHost->m_SaveGame->SetFileName( File );
  1553. m_GHost->m_SaveGame->SetFileNameNoPath( FileNoPath );
  1554. }
  1555. }
  1556. else
  1557. QueueChatCommand( m_GHost->m_Language->UnableToLoadSaveGameDoesntExist( File ), User, Whisper, WhisperResponses );
  1558. }
  1559. }
  1560. //
  1561. // !MAP (load map file)
  1562. //
  1563. if( Command == "map" )
  1564. {
  1565. if( Payload.empty( ) )
  1566. QueueChatCommand( m_GHost->m_Language->CurrentlyLoadedMapCFGIs( m_GHost->m_Map->GetCFGFile( ) ), User, Whisper, WhisperResponses );
  1567. else
  1568. {
  1569. string FoundMaps;
  1570. try
  1571. {
  1572. path MapPath( m_GHost->m_MapPath );
  1573. string Pattern = Payload;
  1574. transform( Pattern.begin( ), Pattern.end( ), Pattern.begin( ), (int(*)(int))tolower );
  1575. if( !exists( MapPath ) )
  1576. {
  1577. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] error listing maps - map path doesn't exist" );
  1578. QueueChatCommand( m_GHost->m_Language->ErrorListingMaps( ), User, Whisper, WhisperResponses );
  1579. }
  1580. else
  1581. {
  1582. directory_iterator EndIterator;
  1583. path LastMatch;
  1584. uint32_t Matches = 0;
  1585. for( directory_iterator i( MapPath ); i != EndIterator; i++ )
  1586. {
  1587. string FileName = i->filename( );
  1588. string Stem = i->path( ).stem( );
  1589. transform( FileName.begin( ), FileName.end( ), FileName.begin( ), (int(*)(int))tolower );
  1590. transform( Stem.begin( ), Stem.end( ), Stem.begin( ), (int(*)(int))tolower );
  1591. if( !is_directory( i->status( ) ) && FileName.find( Pattern ) != string :: npos )
  1592. {
  1593. LastMatch = i->path( );
  1594. Matches++;
  1595. if( FoundMaps.empty( ) )
  1596. FoundMaps = i->filename( );
  1597. else
  1598. FoundMaps += ", " + i->filename( );
  1599. // if the pattern matches the filename exactly, with or without extension, stop any further matching
  1600. if( FileName == Pattern || Stem == Pattern )
  1601. {
  1602. Matches = 1;
  1603. break;
  1604. }
  1605. }
  1606. }
  1607. if( Matches == 0 )
  1608. QueueChatCommand( m_GHost->m_Language->NoMapsFound( ), User, Whisper, WhisperResponses );
  1609. else if( Matches == 1 )
  1610. {
  1611. string File = LastMatch.filename( );
  1612. QueueChatCommand( m_GHost->m_Language->LoadingConfigFile( File ), User, Whisper, WhisperResponses );
  1613. // hackhack: create a config file in memory with the required information to load the map
  1614. CConfig MapCFG;
  1615. MapCFG.Set( "map_path", "Maps\\Download\\" + File );
  1616. MapCFG.Set( "map_localpath", File );
  1617. m_GHost->m_Map->Load( &MapCFG, File );
  1618. }
  1619. else
  1620. QueueChatCommand( m_GHost->m_Language->FoundMaps( FoundMaps ), User, Whisper, WhisperResponses );
  1621. }
  1622. }
  1623. catch( const exception &ex )
  1624. {
  1625. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] error listing maps - caught exception [" + ex.what( ) + "]" );
  1626. QueueChatCommand( m_GHost->m_Language->ErrorListingMaps( ), User, Whisper, WhisperResponses );
  1627. }
  1628. }
  1629. }
  1630. //
  1631. // !OPEN (open slot)
  1632. //
  1633. if( Command == "open" && !Payload.empty( ) && m_GHost->m_CurrentGame )
  1634. {
  1635. if( !m_GHost->m_CurrentGame->GetLocked( ) )
  1636. {
  1637. // open as many slots as specified, e.g. "5 10" opens slots 5 and 10
  1638. stringstream SS;
  1639. SS << Payload;
  1640. while( !SS.eof( ) )
  1641. {
  1642. uint32_t SID;
  1643. SS >> SID;
  1644. if( SS.fail( ) )
  1645. {
  1646. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input to open command" );
  1647. break;
  1648. }
  1649. else
  1650. m_GHost->m_CurrentGame->OpenSlot( (unsigned char)( SID - 1 ), true );
  1651. }
  1652. }
  1653. else
  1654. QueueChatCommand( m_GHost->m_Language->TheGameIsLockedBNET( ), User, Whisper, WhisperResponses );
  1655. }
  1656. //
  1657. // !OPENALL
  1658. //
  1659. if( Command == "openall" && m_GHost->m_CurrentGame )
  1660. {
  1661. if( !m_GHost->m_CurrentGame->GetLocked( ) )
  1662. m_GHost->m_CurrentGame->OpenAllSlots( );
  1663. else
  1664. QueueChatCommand( m_GHost->m_Language->TheGameIsLockedBNET( ), User, Whisper, WhisperResponses );
  1665. }
  1666. //
  1667. // !PRIV (host private game)
  1668. //
  1669. if( Command == "priv" && !Payload.empty( ) )
  1670. m_GHost->CreateGame( m_GHost->m_Map, GAME_PRIVATE, false, Payload, User, User, m_Server, Whisper );
  1671. //
  1672. // !PRIVBY (host private game by other player)
  1673. //
  1674. if( Command == "privby" && !Payload.empty( ) )
  1675. {
  1676. // extract the owner and the game name
  1677. // e.g. "Varlock dota 6.54b arem ~~~" -> owner: "Varlock", game name: "dota 6.54b arem ~~~"
  1678. string Owner;
  1679. string GameName;
  1680. string :: size_type GameNameStart = Payload.find( " " );
  1681. if( GameNameStart != string :: npos )
  1682. {
  1683. Owner = Payload.substr( 0, GameNameStart );
  1684. GameName = Payload.substr( GameNameStart + 1 );
  1685. m_GHost->CreateGame( m_GHost->m_Map, GAME_PRIVATE, false, GameName, Owner, User, m_Server, Whisper );
  1686. }
  1687. }
  1688. //
  1689. // !PUB (host public game)
  1690. //
  1691. if( Command == "pub" && !Payload.empty( ) )
  1692. m_GHost->CreateGame( m_GHost->m_Map, GAME_PUBLIC, false, Payload, User, User, m_Server, Whisper );
  1693. //
  1694. // !PUBBY (host public game by other player)
  1695. //
  1696. if( Command == "pubby" && !Payload.empty( ) )
  1697. {
  1698. // extract the owner and the game name
  1699. // e.g. "Varlock dota 6.54b arem ~~~" -> owner: "Varlock", game name: "dota 6.54b arem ~~~"
  1700. string Owner;
  1701. string GameName;
  1702. string :: size_type GameNameStart = Payload.find( " " );
  1703. if( GameNameStart != string :: npos )
  1704. {
  1705. Owner = Payload.substr( 0, GameNameStart );
  1706. GameName = Payload.substr( GameNameStart + 1 );
  1707. m_GHost->CreateGame( m_GHost->m_Map, GAME_PUBLIC, false, GameName, Owner, User, m_Server, Whisper );
  1708. }
  1709. }
  1710. //
  1711. // !RELOAD
  1712. //
  1713. if( Command == "reload" )
  1714. {
  1715. if( IsRootAdmin( User ) )
  1716. {
  1717. QueueChatCommand( m_GHost->m_Language->ReloadingConfigurationFiles( ), User, Whisper, WhisperResponses );
  1718. m_GHost->ReloadConfigs( );
  1719. }
  1720. else
  1721. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1722. }
  1723. //
  1724. // !SAY
  1725. //
  1726. if( Command == "say" && !Payload.empty( ) )
  1727. {
  1728. // shade0o - completly redone this to stop "/" unless you are rootadmin
  1729. if( Payload.find( "/" ) != string :: npos )
  1730. {
  1731. if( IsRootAdmin( User ) )
  1732. QueueChatCommand( Payload );
  1733. }
  1734. else
  1735. QueueChatCommand( Payload );
  1736. }
  1737. //
  1738. // !SAYGAME
  1739. //
  1740. if( Command == "saygame" && !Payload.empty( ) )
  1741. {
  1742. if( IsRootAdmin( User ) )
  1743. {
  1744. // extract the game number and the message
  1745. // e.g. "3 hello everyone" -> game number: "3", message: "hello everyone"
  1746. uint32_t GameNumber;
  1747. string Message;
  1748. stringstream SS;
  1749. SS << Payload;
  1750. SS >> GameNumber;
  1751. if( SS.fail( ) )
  1752. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #1 to saygame command" );
  1753. else
  1754. {
  1755. if( SS.eof( ) )
  1756. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] missing input #2 to saygame command" );
  1757. else
  1758. {
  1759. getline( SS, Message );
  1760. string :: size_type Start = Message.find_first_not_of( " " );
  1761. if( Start != string :: npos )
  1762. Message = Message.substr( Start );
  1763. if( GameNumber - 1 < m_GHost->m_Games.size( ) )
  1764. m_GHost->m_Games[GameNumber - 1]->SendAllChat( "ADMIN: " + Message );
  1765. else
  1766. QueueChatCommand( m_GHost->m_Language->GameNumberDoesntExist( UTIL_ToString( GameNumber ) ), User, Whisper, WhisperResponses );
  1767. }
  1768. }
  1769. }
  1770. else
  1771. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1772. }
  1773. //
  1774. // !SAYGAMES
  1775. //
  1776. if( Command == "saygames" && !Payload.empty( ) )
  1777. {
  1778. if( IsRootAdmin( User ) )
  1779. {
  1780. if( m_GHost->m_CurrentGame )
  1781. m_GHost->m_CurrentGame->SendAllChat( Payload );
  1782. for( vector<CBaseGame *> :: iterator i = m_GHost->m_Games.begin( ); i != m_GHost->m_Games.end( ); i++ )
  1783. (*i)->SendAllChat( "ADMIN: " + Payload );
  1784. }
  1785. else
  1786. QueueChatCommand( m_GHost->m_Language->YouDontHaveAccessToThatCommand( ), User, Whisper, WhisperResponses );
  1787. }
  1788. //
  1789. // !SP
  1790. //
  1791. if( Command == "sp" && m_GHost->m_CurrentGame && !m_GHost->m_CurrentGame->GetCountDownStarted( ) )
  1792. {
  1793. if( !m_GHost->m_CurrentGame->GetLocked( ) )
  1794. {
  1795. m_GHost->m_CurrentGame->SendAllChat( m_GHost->m_Language->ShufflingPlayers( ) );
  1796. m_GHost->m_CurrentGame->ShuffleSlots( );
  1797. }
  1798. else
  1799. QueueChatCommand( m_GHost->m_Language->TheGameIsLockedBNET( ), User, Whisper, WhisperResponses );
  1800. }
  1801. //
  1802. // !START
  1803. //
  1804. if( Command == "start" && m_GHost->m_CurrentGame && !m_GHost->m_CurrentGame->GetCountDownStarted( ) && m_GHost->m_CurrentGame->GetNumHumanPlayers( ) > 0 )
  1805. {
  1806. if( !m_GHost->m_CurrentGame->GetLocked( ) )
  1807. {
  1808. // if the player sent "!start force" skip the checks and start the countdown
  1809. // otherwise check that the game is ready to start
  1810. if( Payload == "force" )
  1811. m_GHost->m_CurrentGame->StartCountDown( true );
  1812. else
  1813. m_GHost->m_CurrentGame->StartCountDown( false );
  1814. }
  1815. else
  1816. QueueChatCommand( m_GHost->m_Language->TheGameIsLockedBNET( ), User, Whisper, WhisperResponses );
  1817. }
  1818. //
  1819. // !SWAP (swap slots)
  1820. //
  1821. if( Command == "swap" && !Payload.empty( ) && m_GHost->m_CurrentGame )
  1822. {
  1823. if( !m_GHost->m_CurrentGame->GetLocked( ) )
  1824. {
  1825. uint32_t SID1;
  1826. uint32_t SID2;
  1827. stringstream SS;
  1828. SS << Payload;
  1829. SS >> SID1;
  1830. if( SS.fail( ) )
  1831. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #1 to swap command" );
  1832. else
  1833. {
  1834. if( SS.eof( ) )
  1835. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] missing input #2 to swap command" );
  1836. else
  1837. {
  1838. SS >> SID2;
  1839. if( SS.fail( ) )
  1840. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] bad input #2 to swap command" );
  1841. else
  1842. m_GHost->m_CurrentGame->SwapSlots( (unsigned char)( SID1 - 1 ), (unsigned char)( SID2 - 1 ) );
  1843. }
  1844. }
  1845. }
  1846. else
  1847. QueueChatCommand( m_GHost->m_Language->TheGameIsLockedBNET( ), User, Whisper, WhisperResponses );
  1848. }
  1849. //
  1850. // !UNHOST
  1851. //
  1852. if( Command == "unhost" )
  1853. {
  1854. if( m_GHost->m_CurrentGame )
  1855. {
  1856. if( m_GHost->m_CurrentGame->GetCountDownStarted( ) )
  1857. QueueChatCommand( m_GHost->m_Language->UnableToUnhostGameCountdownStarted( m_GHost->m_CurrentGame->GetDescription( ) ), User, Whisper, false );
  1858. // if the game owner is still in the game only allow the root admin to unhost the game
  1859. else if( m_GHost->m_CurrentGame->GetPlayerFromName( m_GHost->m_CurrentGame->GetOwnerName( ), false ) && !IsRootAdmin( User ) )
  1860. QueueChatCommand( m_GHost->m_Language->CantUnhostGameOwnerIsPresent( m_GHost->m_CurrentGame->GetOwnerName( ) ), User, Whisper, false );
  1861. else
  1862. {
  1863. QueueChatCommand( m_GHost->m_Language->UnhostingGame( m_GHost->m_CurrentGame->GetDescription( ) ), User, Whisper, false );
  1864. m_GHost->m_CurrentGame->SetExiting( true );
  1865. }
  1866. }
  1867. else
  1868. QueueChatCommand( m_GHost->m_Language->UnableToUnhostGameNoGameInLobby( ), User, Whisper, WhisperResponses );
  1869. }
  1870. //
  1871. // !WARDENSTATUS
  1872. //
  1873. if( Command == "wardenstatus" )
  1874. {
  1875. if( m_BNLSClient )
  1876. QueueChatCommand( "WARDEN STATUS --- " + UTIL_ToString( m_BNLSClient->GetTotalWardenIn( ) ) + " requests received, " + UTIL_ToString( m_BNLSClient->GetTotalWardenOut( ) ) + " responses sent.", User, Whisper, WhisperResponses );
  1877. else
  1878. QueueChatCommand( "WARDEN STATUS --- Not connected to BNLS server.", User, Whisper, WhisperResponses );
  1879. }
  1880. //
  1881. // !USENORMALCOUNTDOWN
  1882. //
  1883. if ( Command == "usenormalcountdown" && !Payload.empty( ) )
  1884. {
  1885. if ( Payload == "on" )
  1886. {
  1887. m_GHost->m_UseNormalCountDown = true;
  1888. QueueChatCommand("Normal wc3 countdown enabled");
  1889. }
  1890. else if ( Payload == "off" )
  1891. {
  1892. m_GHost->m_UseNormalCountDown = false;
  1893. QueueChatCommand("Normal wc3 countdown disabled");
  1894. }
  1895. }
  1896. }
  1897. else
  1898. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] non-admin [" + User + "] sent command [" + Message + "]" );
  1899. /*********************
  1900. * NON ADMIN COMMANDS *
  1901. *********************/
  1902. // don't respond to non admins if there are more than 3 messages already in the queue
  1903. // this prevents malicious users from filling up the bot's chat queue and crippling the bot
  1904. // in some cases the queue may be full of legitimate messages but we don't really care if the bot ignores one of these commands once in awhile
  1905. // e.g. when several users join a game at the same time and cause multiple /whois messages to be queued at once
  1906. if( IsAdmin( User ) || IsRootAdmin( User ) || ( m_PublicCommands && m_OutPackets.size( ) <= 3 ) )
  1907. {
  1908. //
  1909. // !STATS
  1910. //
  1911. if( Command == "stats" )
  1912. {
  1913. string StatsUser = User;
  1914. if( !Payload.empty( ) )
  1915. StatsUser = Payload;
  1916. // check for potential abuse
  1917. if( !StatsUser.empty( ) && StatsUser.size( ) < 16 && StatsUser[0] != '/' )
  1918. m_PairedGPSChecks.push_back( PairedGPSCheck( Whisper ? User : string( ), m_GHost->m_DB->ThreadedGamePlayerSummaryCheck( StatsUser ) ) );
  1919. }
  1920. //
  1921. // !STATSDOTA
  1922. //
  1923. if( Command == "statsdota" )
  1924. {
  1925. string StatsUser = User;
  1926. if( !Payload.empty( ) )
  1927. StatsUser = Payload;
  1928. // check for potential abuse
  1929. if( !StatsUser.empty( ) && StatsUser.size( ) < 16 && StatsUser[0] != '/' )
  1930. m_PairedDPSChecks.push_back( PairedDPSCheck( Whisper ? User : string( ), m_GHost->m_DB->ThreadedDotAPlayerSummaryCheck( StatsUser ) ) );
  1931. }
  1932. //
  1933. // !VERSION
  1934. //
  1935. if( Command == "version" )
  1936. {
  1937. if( IsAdmin( User ) || IsRootAdmin( User ) )
  1938. QueueChatCommand( m_GHost->m_Language->VersionAdmin( m_GHost->m_Version ), User, Whisper, WhisperResponses );
  1939. else
  1940. QueueChatCommand( m_GHost->m_Language->VersionNotAdmin( m_GHost->m_Version ), User, Whisper, WhisperResponses );
  1941. }
  1942. }
  1943. }
  1944. }
  1945. else if( Event == CBNETProtocol :: EID_INFO )
  1946. {
  1947. CONSOLE_Print( "[INFO: " + m_ServerAlias + "] " + Message, GetRealmId( ), false );
  1948. m_GHost->EventBNETInfo( this, User, Message );
  1949. // extract the first word which we hope is the username
  1950. // this is not necessarily true though since info messages also include channel MOTD's and such
  1951. string UserName;
  1952. string :: size_type Split = Message.find( " " );
  1953. if( Split != string :: npos )
  1954. UserName = Message.substr( 0, Split );
  1955. else
  1956. UserName = Message.substr( 0 );
  1957. // handle spoof checking for current game
  1958. // this case covers whois results which are used when hosting a public game (we send out a "/whois [player]" for each player)
  1959. // at all times you can still /w the bot with "spoofcheck" to manually spoof check
  1960. if( m_GHost->m_CurrentGame && m_GHost->m_CurrentGame->GetPlayerFromName( UserName, true ) )
  1961. {
  1962. if( Message.find( "is away" ) != string :: npos )
  1963. m_GHost->m_CurrentGame->SendAllChat( m_GHost->m_Language->SpoofPossibleIsAway( UserName ) );
  1964. else if( Message.find( "is unavailable" ) != string :: npos )
  1965. m_GHost->m_CurrentGame->SendAllChat( m_GHost->m_Language->SpoofPossibleIsUnavailable( UserName ) );
  1966. else if( Message.find( "is refusing messages" ) != string :: npos )
  1967. m_GHost->m_CurrentGame->SendAllChat( m_GHost->m_Language->SpoofPossibleIsRefusingMessages( UserName ) );
  1968. else if( Message.find( "is using Warcraft III The Frozen Throne in the channel" ) != string :: npos )
  1969. m_GHost->m_CurrentGame->SendAllChat( m_GHost->m_Language->SpoofDetectedIsNotInGame( UserName ) );
  1970. else if( Message.find( "is using Warcraft III The Frozen Throne in channel" ) != string :: npos )
  1971. m_GHost->m_CurrentGame->SendAllChat( m_GHost->m_Language->SpoofDetectedIsNotInGame( UserName ) );
  1972. else if( Message.find( "is using Warcraft III The Frozen Throne in a private channel" ) != string :: npos )
  1973. m_GHost->m_CurrentGame->SendAllChat( m_GHost->m_Language->SpoofDetectedIsInPrivateChannel( UserName ) );
  1974. if( Message.find( "is using Warcraft III The Frozen Throne in game" ) != string :: npos || Message.find( "is using Warcraft III Frozen Throne and is currently in game" ) != string :: npos )
  1975. {
  1976. // check both the current game name and the last game name against the /whois response
  1977. // this is because when the game is rehosted, players who joined recently will be in the previous game according to battle.net
  1978. // note: if the game is rehosted more than once it is possible (but unlikely) for a false positive because only two game names are checked
  1979. if( Message.find( m_GHost->m_CurrentGame->GetGameName( ) ) != string :: npos || Message.find( m_GHost->m_CurrentGame->GetLastGameName( ) ) != string :: npos )
  1980. m_GHost->m_CurrentGame->AddToSpoofed( m_Server, UserName, false );
  1981. else
  1982. m_GHost->m_CurrentGame->SendAllChat( m_GHost->m_Language->SpoofDetectedIsInAnotherGame( UserName ) );
  1983. }
  1984. }
  1985. }
  1986. }
  1987. void CBNET :: SendJoinChannel( string channel )
  1988. {
  1989. if( m_LoggedIn && m_InChat )
  1990. m_Socket->PutBytes( m_Protocol->SEND_SID_JOINCHANNEL( channel ) );
  1991. }
  1992. void CBNET :: SendGetFriendsList( )
  1993. {
  1994. if( m_LoggedIn )
  1995. m_Socket->PutBytes( m_Protocol->SEND_SID_FRIENDSLIST( ) );
  1996. }
  1997. void CBNET :: SendGetClanList( )
  1998. {
  1999. if( m_LoggedIn )
  2000. m_Socket->PutBytes( m_Protocol->SEND_SID_CLANMEMBERLIST( ) );
  2001. }
  2002. void CBNET :: QueueEnterChat( )
  2003. {
  2004. if( m_LoggedIn )
  2005. m_OutPackets.push( m_Protocol->SEND_SID_ENTERCHAT( ) );
  2006. }
  2007. void CBNET :: QueueChatCommand( string chatCommand, bool hidden )
  2008. {
  2009. if( chatCommand.empty( ) )
  2010. return;
  2011. if( m_LoggedIn )
  2012. {
  2013. if( m_PasswordHashType == "pvpgn" && chatCommand.size( ) > m_MaxMessageLength )
  2014. chatCommand = chatCommand.substr( 0, m_MaxMessageLength );
  2015. if( chatCommand.size( ) > 255 )
  2016. chatCommand = chatCommand.substr( 0, 255 );
  2017. if( m_OutPackets.size( ) > 10 )
  2018. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] attempted to queue chat command [" + chatCommand + "] but there are too many (" + UTIL_ToString( m_OutPackets.size( ) ) + ") packets queued, discarding" );
  2019. else
  2020. {
  2021. bool whisper = false;
  2022. int r = 0;
  2023. if ( chatCommand.size( ) > 3 )
  2024. {
  2025. if ( chatCommand.substr( 0, 3 ) == "/w " ) { whisper = true; r = 3; }
  2026. else if ( chatCommand.substr( 0, 9 ) == "/whisper " ) { whisper = true; r = 9; }
  2027. else if ( chatCommand.substr( 0, 5 ) == "/f m " ) { whisper = true; r = 5; }
  2028. else if ( chatCommand.substr( 0, 7 ) == "/f msg " ) { whisper = true; r = 7; }
  2029. else if ( chatCommand.substr( 0, 11 ) == "/friends m " ) { whisper = true; r = 11; }
  2030. else if ( chatCommand.substr( 0, 13 ) == "/friends msg " ) { whisper = true; r = 13; }
  2031. }
  2032. if ( whisper )
  2033. {
  2034. if ( r == 3 || r == 9 )
  2035. {
  2036. int nameEndpos = chatCommand.find_first_of( " ", r );
  2037. if ( nameEndpos != -1 )
  2038. {
  2039. string target = chatCommand.substr( r, nameEndpos - r );
  2040. }
  2041. }
  2042. }
  2043. else if (!hidden) CONSOLE_Print( "[LOCAL: " + m_ServerAlias + "] [" + m_UserName + "] " + chatCommand, GetRealmId( ), false );
  2044. m_OutPackets.push( m_Protocol->SEND_SID_CHATCOMMAND( chatCommand ) );
  2045. }
  2046. }
  2047. }
  2048. void CBNET :: QueueChatCommand( string chatCommand, string user, bool whisper, bool whisperresponses )
  2049. {
  2050. if( chatCommand.empty( ) )
  2051. return;
  2052. // if whisper is true send the chat command as a whisper to user, otherwise just queue the chat command
  2053. if( whisper || whisperresponses )
  2054. QueueChatCommand( "/w " + user + " " + chatCommand );
  2055. else
  2056. QueueChatCommand( chatCommand );
  2057. }
  2058. void CBNET :: QueueGameCreate( unsigned char state, string gameName, string hostName, CMap *map, CSaveGame *savegame, uint32_t hostCounter )
  2059. {
  2060. if( m_LoggedIn && map )
  2061. {
  2062. if( !m_CurrentChannel.empty( ) )
  2063. m_FirstChannel = m_CurrentChannel;
  2064. m_InChat = false;
  2065. // a game creation message is just a game refresh message with upTime = 0
  2066. QueueGameRefresh( state, gameName, hostName, map, savegame, 0, hostCounter );
  2067. }
  2068. }
  2069. void CBNET :: QueueGameRefresh( unsigned char state, string gameName, string hostName, CMap *map, CSaveGame *saveGame, uint32_t upTime, uint32_t hostCounter )
  2070. {
  2071. if( hostName.empty( ) )
  2072. {
  2073. BYTEARRAY UniqueName = m_Protocol->GetUniqueName( );
  2074. hostName = string( UniqueName.begin( ), UniqueName.end( ) );
  2075. }
  2076. if( m_LoggedIn && map )
  2077. {
  2078. // construct a fixed host counter which will be used to identify players from this realm
  2079. // the fixed host counter's 4 most significant bits will contain a 4 bit ID (0-15)
  2080. // the rest of the fixed host counter will contain the 28 least significant bits of the actual host counter
  2081. // 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
  2082. // when a player joins a game we can obtain the ID from the received host counter
  2083. // note: LAN broadcasts use an ID of 0, battle.net refreshes use an ID of 1-10, the rest are unused
  2084. uint32_t FixedHostCounter = ( hostCounter & 0x0FFFFFFF ) | ( m_HostCounterID << 28 );
  2085. if( saveGame )
  2086. {
  2087. uint32_t MapGameType = MAPGAMETYPE_SAVEDGAME;
  2088. // the state should always be private when creating a saved game
  2089. if( state == GAME_PRIVATE )
  2090. MapGameType |= MAPGAMETYPE_PRIVATEGAME;
  2091. // use an invalid map width/height to indicate reconnectable games
  2092. BYTEARRAY MapWidth;
  2093. MapWidth.push_back( 192 );
  2094. MapWidth.push_back( 7 );
  2095. BYTEARRAY MapHeight;
  2096. MapHeight.push_back( 192 );
  2097. MapHeight.push_back( 7 );
  2098. if( m_GHost->m_Reconnect )
  2099. m_OutPackets.push( m_Protocol->SEND_SID_STARTADVEX3( state, UTIL_CreateByteArray( MapGameType, false ), map->GetMapGameFlags( ), MapWidth, MapHeight, gameName, hostName, upTime, "Save\\Multiplayer\\" + saveGame->GetFileNameNoPath( ), saveGame->GetMagicNumber( ), map->GetMapSHA1( ), FixedHostCounter ) );
  2100. else
  2101. m_OutPackets.push( m_Protocol->SEND_SID_STARTADVEX3( state, UTIL_CreateByteArray( MapGameType, false ), map->GetMapGameFlags( ), UTIL_CreateByteArray( (uint16_t)0, false ), UTIL_CreateByteArray( (uint16_t)0, false ), gameName, hostName, upTime, "Save\\Multiplayer\\" + saveGame->GetFileNameNoPath( ), saveGame->GetMagicNumber( ), map->GetMapSHA1( ), FixedHostCounter ) );
  2102. }
  2103. else
  2104. {
  2105. uint32_t MapGameType = map->GetMapGameType( );
  2106. MapGameType |= MAPGAMETYPE_UNKNOWN0;
  2107. if( state == GAME_PRIVATE )
  2108. MapGameType |= MAPGAMETYPE_PRIVATEGAME;
  2109. // use an invalid map width/height to indicate reconnectable games
  2110. BYTEARRAY MapWidth;
  2111. MapWidth.push_back( 192 );
  2112. MapWidth.push_back( 7 );
  2113. BYTEARRAY MapHeight;
  2114. MapHeight.push_back( 192 );
  2115. MapHeight.push_back( 7 );
  2116. if( m_GHost->m_Reconnect )
  2117. m_OutPackets.push( m_Protocol->SEND_SID_STARTADVEX3( state, UTIL_CreateByteArray( MapGameType, false ), map->GetMapGameFlags( ), MapWidth, MapHeight, gameName, hostName, upTime, map->GetMapPath( ), map->GetMapCRC( ), map->GetMapSHA1( ), FixedHostCounter ) );
  2118. else
  2119. m_OutPackets.push( m_Protocol->SEND_SID_STARTADVEX3( state, UTIL_CreateByteArray( MapGameType, false ), map->GetMapGameFlags( ), map->GetMapWidth( ), map->GetMapHeight( ), gameName, hostName, upTime, map->GetMapPath( ), map->GetMapCRC( ), map->GetMapSHA1( ), FixedHostCounter ) );
  2120. }
  2121. }
  2122. }
  2123. void CBNET :: QueueGameUncreate( )
  2124. {
  2125. if( m_LoggedIn )
  2126. m_OutPackets.push( m_Protocol->SEND_SID_STOPADV( ) );
  2127. }
  2128. void CBNET :: UnqueuePackets( unsigned char type )
  2129. {
  2130. queue<BYTEARRAY> Packets;
  2131. uint32_t Unqueued = 0;
  2132. while( !m_OutPackets.empty( ) )
  2133. {
  2134. // todotodo: it's very inefficient to have to copy all these packets while searching the queue
  2135. BYTEARRAY Packet = m_OutPackets.front( );
  2136. m_OutPackets.pop( );
  2137. if( Packet.size( ) >= 2 && Packet[1] == type )
  2138. Unqueued++;
  2139. else
  2140. Packets.push( Packet );
  2141. }
  2142. m_OutPackets = Packets;
  2143. if( Unqueued > 0 )
  2144. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] unqueued " + UTIL_ToString( Unqueued ) + " packets of type " + UTIL_ToString( type ) );
  2145. }
  2146. void CBNET :: UnqueueChatCommand( string chatCommand )
  2147. {
  2148. // hackhack: this is ugly code
  2149. // generate the packet that would be sent for this chat command
  2150. // then search the queue for that exact packet
  2151. BYTEARRAY PacketToUnqueue = m_Protocol->SEND_SID_CHATCOMMAND( chatCommand );
  2152. queue<BYTEARRAY> Packets;
  2153. uint32_t Unqueued = 0;
  2154. while( !m_OutPackets.empty( ) )
  2155. {
  2156. // todotodo: it's very inefficient to have to copy all these packets while searching the queue
  2157. BYTEARRAY Packet = m_OutPackets.front( );
  2158. m_OutPackets.pop( );
  2159. if( Packet == PacketToUnqueue )
  2160. Unqueued++;
  2161. else
  2162. Packets.push( Packet );
  2163. }
  2164. m_OutPackets = Packets;
  2165. if( Unqueued > 0 )
  2166. CONSOLE_Print( "[BNET: " + m_ServerAlias + "] unqueued " + UTIL_ToString( Unqueued ) + " chat command packets" );
  2167. }
  2168. void CBNET :: UnqueueGameRefreshes( )
  2169. {
  2170. UnqueuePackets( CBNETProtocol :: SID_STARTADVEX3 );
  2171. }
  2172. void CBNET :: RequestListUpdates( )
  2173. {
  2174. SendGetFriendsList( );
  2175. SendGetClanList( );
  2176. }
  2177. bool CBNET :: IsAdmin( string name )
  2178. {
  2179. transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
  2180. for( vector<string> :: iterator i = m_Admins.begin( ); i != m_Admins.end( ); i++ )
  2181. {
  2182. if( *i == name )
  2183. return true;
  2184. }
  2185. return false;
  2186. }
  2187. bool CBNET :: IsRootAdmin( string name )
  2188. {
  2189. if( name == "" )
  2190. return true;
  2191. // m_RootAdmin was already transformed to lower case in the constructor
  2192. transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
  2193. // updated to permit multiple root admins seperated by a space, e.g. "Varlock Kilranin Instinct121"
  2194. // note: this function gets called frequently so it would be better to parse the root admins just once and store them in a list somewhere
  2195. // however, it's hardly worth optimizing at this point since the code's already written
  2196. stringstream SS;
  2197. string s;
  2198. SS << m_RootAdmin;
  2199. while( !SS.eof( ) )
  2200. {
  2201. SS >> s;
  2202. if( name == s )
  2203. return true;
  2204. }
  2205. return false;
  2206. }
  2207. bool CBNET :: IsLANRootAdmin( string name )
  2208. {
  2209. // m_LANRootAdmin was already transformed to lower case in the constructor
  2210. transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
  2211. // updated to permit multiple root admins seperated by a space, e.g. "Varlock Kilranin Instinct121"
  2212. // note: this function gets called frequently so it would be better to parse the root admins just once and store them in a list somewhere
  2213. // however, it's hardly worth optimizing at this point since the code's already written
  2214. stringstream SS;
  2215. string s;
  2216. SS << m_LANRootAdmin;
  2217. while( !SS.eof( ) )
  2218. {
  2219. SS >> s;
  2220. if( name == s )
  2221. return true;
  2222. }
  2223. return false;
  2224. }
  2225. CDBBan *CBNET :: IsBannedName( string name )
  2226. {
  2227. transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
  2228. // todotodo: optimize this - maybe use a map?
  2229. for( vector<CDBBan *> :: iterator i = m_Bans.begin( ); i != m_Bans.end( ); i++ )
  2230. {
  2231. if( (*i)->GetName( ) == name )
  2232. return *i;
  2233. }
  2234. return NULL;
  2235. }
  2236. CDBBan *CBNET :: IsBannedIP( string ip )
  2237. {
  2238. // todotodo: optimize this - maybe use a map?
  2239. for( vector<CDBBan *> :: iterator i = m_Bans.begin( ); i != m_Bans.end( ); i++ )
  2240. {
  2241. if( (*i)->GetIP( ) == ip )
  2242. return *i;
  2243. }
  2244. return NULL;
  2245. }
  2246. void CBNET :: AddAdmin( string name )
  2247. {
  2248. transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
  2249. m_Admins.push_back( name );
  2250. }
  2251. void CBNET :: AddBan( string name, string ip, string gamename, string admin, string reason )
  2252. {
  2253. transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
  2254. m_Bans.push_back( new CDBBan( m_Server, name, ip, "N/A", gamename, admin, reason ) );
  2255. }
  2256. void CBNET :: RemoveAdmin( string name )
  2257. {
  2258. transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
  2259. for( vector<string> :: iterator i = m_Admins.begin( ); i != m_Admins.end( ); )
  2260. {
  2261. if( *i == name )
  2262. i = m_Admins.erase( i );
  2263. else
  2264. i++;
  2265. }
  2266. }
  2267. void CBNET :: RemoveBan( string name )
  2268. {
  2269. transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
  2270. for( vector<CDBBan *> :: iterator i = m_Bans.begin( ); i != m_Bans.end( ); )
  2271. {
  2272. if( (*i)->GetName( ) == name )
  2273. i = m_Bans.erase( i );
  2274. else
  2275. i++;
  2276. }
  2277. }
  2278. void CBNET :: HoldFriends( CBaseGame *game )
  2279. {
  2280. if( game )
  2281. {
  2282. for( vector<CIncomingFriendList *> :: iterator i = m_Friends.begin( ); i != m_Friends.end( ); i++ )
  2283. game->AddToReserved( (*i)->GetAccount( ) );
  2284. }
  2285. }
  2286. void CBNET :: HoldClan( CBaseGame *game )
  2287. {
  2288. if( game )
  2289. {
  2290. for( vector<CIncomingClanList *> :: iterator i = m_Clans.begin( ); i != m_Clans.end( ); i++ )
  2291. game->AddToReserved( (*i)->GetName( ) );
  2292. }
  2293. }
  2294. void CBNET :: HiddenGhostCommand( string Message )
  2295. {
  2296. // "" ok or not? revert to m_UserName?
  2297. CIncomingChatEvent temp( CBNETProtocol::IncomingChatEvent(29), 0, 0, "", Message );
  2298. ProcessChatEvent( &temp );
  2299. }