PageRenderTime 59ms 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

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

  1. /*
  2. Copyright [2008] [Trevor Hogan]
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/
  13. */
  14. #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 + "] m…

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