matcher.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "global.h"
  19. #include "matcher.h"
  20. #include "encrypt.h"
  21. #include "timezone.h"
  22. #include "debug.h"
  23. #ifdef _WINDOWS
  24. #define usleep(x) Sleep((x)/100000)
  25. #endif
  26. MatcherClass::MatcherClass(void)
  27. {
  28. m_lastRotation = 0;
  29. m_baseNick = "matcher";
  30. m_joinSuccess = false;
  31. done = 0;
  32. m_rotateLogs = false;
  33. quiet = false;
  34. }
  35. Wstring MatcherClass::getString(const Wstring& key)
  36. {
  37. Wstring res;
  38. Global.GetString(key, res);
  39. return res;
  40. }
  41. void logIt(const char *Txt)
  42. {
  43. // intentionally crash if we can't open it
  44. FILE *fp = fopen("logIt.txt", "w");
  45. fputs(Txt, fp);
  46. fclose(fp);
  47. }
  48. void MatcherClass::readLoop(void)
  49. {
  50. int delay = -1;
  51. Global.config.getInt("ROTATEDELAY", delay);
  52. DBGMSG("ROTATEDELAY: " << delay);
  53. do
  54. {
  55. static time_t lastLogTime = 0;
  56. time_t now = time(NULL);
  57. if (now > lastLogTime + 300)
  58. {
  59. lastLogTime = now;
  60. INFMSG("still here" << endl);
  61. }
  62. logIt("peerThink\n");
  63. peerThink(m_peer);
  64. logIt("peerIsConnected\n");
  65. if (peerIsConnected(m_peer))
  66. {
  67. logIt("checkMatches()\n");
  68. checkMatches();
  69. logIt("checkMatches() done\n");
  70. }
  71. else
  72. done = true;
  73. msleep(1);
  74. // rotate logs if it's time
  75. if (delay != -1)
  76. {
  77. #ifdef _UNIX
  78. Xtime xtime;
  79. time_t curtime;
  80. curtime = time(NULL);
  81. // get the number of seconds that have passed since midnight
  82. // of the current day.
  83. curtime -= TimezoneOffset();
  84. time_t timeofday = curtime % (delay);
  85. if ((timeofday > 0) && (timeofday <= 300))
  86. {
  87. rotateOutput();
  88. rotateParanoid();
  89. }
  90. #endif
  91. }
  92. }
  93. while (!done);
  94. DBGMSG("Bailing out of readLoop!" << endl);
  95. INFMSG("Bailing out of readLoop!" << endl);
  96. ERRMSG("Bailing out of readLoop!" << endl);
  97. }
  98. /////////////////////////////////////////////////////////////////////////////
  99. static void DisconnectedCallback ( PEER peer, const char * reason, void * param)
  100. {
  101. DBGMSG("Disconnected: " << reason);
  102. MatcherClass *matcher = (MatcherClass *)param;
  103. if (matcher)
  104. matcher->handleDisconnect( reason );
  105. }
  106. static void RoomMessageCallback ( PEER peer, RoomType roomType, const char * nick, const char * message, MessageType messageType, void * param)
  107. {
  108. DBGMSG("(PUBLIC) " << nick << ": " << message);
  109. MatcherClass *matcher = (MatcherClass *)param;
  110. if (matcher)
  111. matcher->handleRoomMessage( nick, message, messageType );
  112. }
  113. static void PlayerMessageCallback ( PEER peer, const char * nick, const char * message, MessageType messageType, void * param)
  114. {
  115. DBGMSG("(PRIVATE) " << nick << ": " << message);
  116. MatcherClass *matcher = (MatcherClass *)param;
  117. if (matcher)
  118. matcher->handlePlayerMessage( nick, message, messageType );
  119. }
  120. static void PlayerJoinedCallback ( PEER peer, RoomType roomType, const char * nick, void * param)
  121. {
  122. DBGMSG(nick << " joined the room");
  123. MatcherClass *matcher = (MatcherClass *)param;
  124. if (matcher)
  125. matcher->handlePlayerJoined( nick );
  126. }
  127. static void PlayerLeftCallback ( PEER peer, RoomType roomType, const char * nick, const char * reason, void * param)
  128. {
  129. DBGMSG(nick << " left the room");
  130. MatcherClass *matcher = (MatcherClass *)param;
  131. if (matcher)
  132. matcher->handlePlayerLeft( nick );
  133. }
  134. static void PlayerChangedNickCallback ( PEER peer, RoomType roomType, const char * oldNick, const char * newNick, void * param)
  135. {
  136. INFMSG(oldNick << " changed nicks to " << newNick);
  137. MatcherClass *matcher = (MatcherClass *)param;
  138. if (matcher)
  139. matcher->handlePlayerChangedNick( oldNick, newNick );
  140. }
  141. static void EnumPlayersCallback ( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick, int flags, void * param)
  142. {
  143. MatcherClass *matcher = (MatcherClass *)param;
  144. if (matcher)
  145. matcher->handlePlayerEnum( success == PEERTrue, index, nick, flags);
  146. }
  147. static int s_groupID = 0;
  148. static void ListGroupRoomsCallback ( PEER peer, PEERBool success, int groupID, SBServer server, const char * name, int numWaiting, int maxWaiting, int numGames, int numPlaying, void * param)
  149. {
  150. if (success && name && !strcasecmp(name, "QuickMatch"))
  151. {
  152. s_groupID = groupID;
  153. }
  154. }
  155. static void ConnectCallback ( PEER peer, PEERBool success, void * param)
  156. {
  157. MatcherClass *matcher = (MatcherClass *)param;
  158. if (matcher)
  159. matcher->handleConnect( success == PEERTrue );
  160. }
  161. static void JoinCallback ( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void * param)
  162. {
  163. MatcherClass *matcher = (MatcherClass *)param;
  164. if (matcher)
  165. matcher->handleJoin( success == PEERTrue );
  166. }
  167. static void NickErrorCallback ( PEER peer, int type, const char * badNick, void * param)
  168. {
  169. ERRMSG("Nick error with " << badNick);
  170. if(type == PEER_IN_USE)
  171. {
  172. int len = strlen(badNick);
  173. std::string nickStr = badNick;
  174. int newVal = 0;
  175. if (badNick[len-1] == '}' && badNick[len-3] == '{' && isdigit(badNick[len-2]))
  176. {
  177. newVal = badNick[len-2] - '0' + 1;
  178. nickStr.erase(len-3, 3);
  179. }
  180. nickStr.append("{");
  181. char tmp[2];
  182. tmp[0] = '0'+newVal;
  183. tmp[1] = '\0';
  184. nickStr.append(tmp);
  185. nickStr.append("}");
  186. DBGMSG("Nickname taken: was "<<badNick<<", new val = "<<newVal<<", new nick = "<<nickStr.c_str());
  187. if (newVal < 10)
  188. {
  189. // Retry the connect with a similar nick.
  190. peerRetryWithNick(peer, nickStr.c_str());
  191. }
  192. else
  193. {
  194. // Cancel the connect.
  195. peerRetryWithNick(peer, NULL);
  196. MatcherClass *matcher = (MatcherClass *)param;
  197. if (matcher)
  198. matcher->handleNickError( badNick );
  199. }
  200. }
  201. else
  202. {
  203. // Cancel the connect.
  204. peerRetryWithNick(peer, NULL);
  205. MatcherClass *matcher = (MatcherClass *)param;
  206. if (matcher)
  207. matcher->handleNickError( badNick );
  208. }
  209. }
  210. /////////////////////////////////////////////////////////////////////////////
  211. void callbackEach( CHAT chat, CHATBool success, int index, const char *channel,
  212. const char *topic, int numUsers, void *param )
  213. {
  214. DEBUG_LOG(("Chat channel success: %d\n", success));
  215. if (!success)
  216. {
  217. return;
  218. }
  219. DEBUG_LOG(("Channel[%d]: %s (%s), %d users\n",
  220. index, channel, topic, numUsers));
  221. }
  222. void callbackAll( CHAT chat, CHATBool success, int numChannels, const char **channels,
  223. const char **topics, int *numUsers, void *param )
  224. {
  225. DEBUG_LOG(("Chat channels success: %d\n", success));
  226. if (!success)
  227. {
  228. return;
  229. }
  230. DEBUG_LOG(("%d channels found\n", numChannels));
  231. for (int i=0; i<numChannels; ++i)
  232. {
  233. DEBUG_LOG(("Channel[%d]: %s (%s), %d users\n",
  234. i, channels[i], topics[i], numUsers[i]));
  235. }
  236. }
  237. void MatcherClass::handleConnect( bool success )
  238. {
  239. m_connectSuccess = success;
  240. //DEBUG_LOG(("Enumerating chat channels\n"));
  241. //chatEnumChannels( peerGetChat(m_peer), "", callbackEach, callbackAll, NULL, CHATTrue );
  242. //DEBUG_LOG(("Done enumerating chat channels\n"));
  243. }
  244. void MatcherClass::handleGroupRoomList( bool success, int groupID, const char *name )
  245. {
  246. }
  247. void MatcherClass::handleJoin( bool success )
  248. {
  249. m_joinSuccess = success;
  250. if (m_joinSuccess)
  251. {
  252. DBGMSG("Joined room - listing players");
  253. peerEnumPlayers(m_peer, GroupRoom, EnumPlayersCallback, this);
  254. }
  255. }
  256. void MatcherClass::handleNickError( const char *badNick )
  257. {
  258. exit(1);
  259. }
  260. static void AuthenticateCDKeyCallback
  261. (
  262. PEER peer,
  263. int result,
  264. const char * message,
  265. void * param
  266. )
  267. {
  268. bool *val = (bool *)param;
  269. if (val)
  270. {
  271. *val = (result >= 1);
  272. }
  273. }
  274. void MatcherClass::connectAndLoop(void)
  275. {
  276. // Game-specific initializations, if neccessary
  277. init();
  278. // Check for possible quit from init()-based self-tests
  279. if (done)
  280. return ;
  281. // Defaults.
  282. ////////////
  283. Wstring title = "gmtest";
  284. Wstring secretKey = "HA6zkS";
  285. Wstring serialNo = "";
  286. m_profileID = 0;
  287. Global.config.getString("Nick", m_baseNick, "LOGIN");
  288. DBGMSG("base nick is " << m_baseNick.get());
  289. m_baseNick.toLower();
  290. Global.config.getString("Title", title, "LOGIN");
  291. Global.config.getString("SecretKey", secretKey, "LOGIN");
  292. Global.config.getInt("ProfileID", m_profileID, "LOGIN");
  293. Global.config.getString("CDKey", serialNo, "LOGIN");
  294. PEERCallbacks callbacks;
  295. PEERBool pingRooms[NumRooms];
  296. PEERBool crossPingRooms[NumRooms];
  297. // Setup the callbacks.
  298. ///////////////////////
  299. memset(&callbacks, 0, sizeof(PEERCallbacks));
  300. callbacks.disconnected = DisconnectedCallback;
  301. callbacks.playerChangedNick = PlayerChangedNickCallback;
  302. callbacks.playerJoined = PlayerJoinedCallback;
  303. callbacks.playerLeft = PlayerLeftCallback;
  304. callbacks.roomMessage = RoomMessageCallback;
  305. callbacks.playerMessage = PlayerMessageCallback;
  306. callbacks.param = this;
  307. // Init.
  308. ////////
  309. m_peer = peerInitialize(&callbacks);
  310. if(!m_peer)
  311. {
  312. ERRMSG("Failed to init peer object" << endl);
  313. return;
  314. }
  315. // Ping/cross-ping in no room.
  316. /////////////////////////////////
  317. pingRooms[TitleRoom] = PEERFalse;
  318. pingRooms[GroupRoom] = PEERFalse;
  319. pingRooms[StagingRoom] = PEERFalse;
  320. crossPingRooms[TitleRoom] = PEERFalse;
  321. crossPingRooms[GroupRoom] = PEERFalse;
  322. crossPingRooms[StagingRoom] = PEERFalse;
  323. // Set the title.
  324. /////////////////
  325. if(!peerSetTitle(m_peer, title.get(), secretKey.get(), title.get(), secretKey.get(), 0, 30, PEERTrue, pingRooms, crossPingRooms))
  326. {
  327. peerShutdown(m_peer);
  328. ERRMSG("Failed to set the title" << endl);
  329. return;
  330. }
  331. // Connect.
  332. ///////////
  333. m_connectSuccess = false;
  334. m_nick = m_baseNick.get();
  335. peerConnect(m_peer, m_baseNick.get(), m_profileID, NickErrorCallback, ConnectCallback, this, PEERTrue);
  336. if(!m_connectSuccess)
  337. {
  338. peerShutdown(m_peer);
  339. ERRMSG("Failed to connect" << endl);
  340. return;
  341. }
  342. bool cdOk = false;
  343. peerAuthenticateCDKey(m_peer, serialNo.get(), AuthenticateCDKeyCallback, &cdOk, PEERTrue);
  344. if (!cdOk)
  345. {
  346. peerShutdown(m_peer);
  347. ERRMSG("Failed to auth CDKey " << serialNo.get() << endl);
  348. return;
  349. }
  350. m_groupID = 0;
  351. peerListGroupRooms(m_peer, NULL, ListGroupRoomsCallback, &m_groupID, PEERTrue);
  352. m_groupID = s_groupID;
  353. DBGMSG("QuickMatch room is " << m_groupID);
  354. // Join the title room.
  355. ///////////////////////
  356. peerJoinGroupRoom(m_peer, m_groupID, JoinCallback, this, PEERTrue);
  357. if(!m_joinSuccess)
  358. {
  359. peerDisconnect(m_peer);
  360. peerShutdown(m_peer);
  361. ERRMSG("Failed to join the title room" << endl);
  362. return;
  363. }
  364. // Connected, so lets do our thing
  365. readLoop();
  366. peerDisconnect(m_peer);
  367. peerShutdown(m_peer);
  368. }