DisconnectManager.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  24. #include "Common/Recorder.h"
  25. #include "GameClient/DisconnectMenu.h"
  26. #include "GameClient/InGameUI.h"
  27. #include "GameLogic/GameLogic.h"
  28. #include "GameNetwork/DisconnectManager.h"
  29. #include "GameNetwork/NetworkInterface.h"
  30. #include "GameNetwork/NetworkUtil.h"
  31. #include "GameNetwork/GameSpy/PingThread.h"
  32. #include "GameNetwork/GameSpy/GSConfig.h"
  33. #ifdef _INTERNAL
  34. // for occasional debugging...
  35. //#pragma optimize("", off)
  36. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  37. #endif
  38. DisconnectManager::DisconnectManager()
  39. {
  40. // Added By Sadullah Nader
  41. // Initializations missing and needed
  42. Int i;
  43. m_currentPacketRouterIndex = 0;
  44. m_lastFrame = 0;
  45. m_lastFrameTime = 0;
  46. m_lastKeepAliveSendTime = 0;
  47. m_haveNotifiedOtherPlayersOfCurrentFrame = FALSE;
  48. m_timeOfDisconnectScreenOn = 0;
  49. for( i = 0; i < MAX_SLOTS; ++i) {
  50. m_packetRouterFallback[i] = 0;
  51. }
  52. m_packetRouterTimeout = 0;
  53. for( i = 0; i < MAX_SLOTS -1; ++i) {
  54. m_playerTimeouts[i] = 0;
  55. }
  56. for( i = 0; i < MAX_SLOTS; ++i) {
  57. for (Int j = 0; j < MAX_SLOTS; ++j) {
  58. m_playerVotes[i][j].vote = FALSE;
  59. m_playerVotes[i][j].frame = 0;
  60. }
  61. }
  62. }
  63. DisconnectManager::~DisconnectManager() {
  64. }
  65. void DisconnectManager::init() {
  66. TheDisconnectMenu->hideScreen(); // make sure the screen starts out hidden.
  67. m_lastFrame = 0;
  68. m_lastFrameTime = -1;
  69. m_lastKeepAliveSendTime = -1;
  70. m_disconnectState = DISCONNECTSTATETYPE_SCREENOFF;
  71. m_currentPacketRouterIndex = 0;
  72. m_timeOfDisconnectScreenOn = 0;
  73. for (Int i = 0; i < MAX_SLOTS; ++i) {
  74. for (Int j = 0; j < MAX_SLOTS; ++j) {
  75. m_playerVotes[i][j].vote = FALSE;
  76. m_playerVotes[i][j].frame = 0;
  77. }
  78. }
  79. for (i = 0; i < MAX_SLOTS; ++i) {
  80. m_disconnectFrames[i] = 0;
  81. m_disconnectFramesReceived[i] = FALSE;
  82. }
  83. m_pingFrame = 0;
  84. m_pingsSent = 0;
  85. m_pingsRecieved = 0;
  86. }
  87. void DisconnectManager::update(ConnectionManager *conMgr) {
  88. if (m_lastFrameTime == -1) {
  89. m_lastFrameTime = timeGetTime();
  90. }
  91. // The game logic stalls on the frame we are currently waiting for commands on,
  92. // so we have to check for the current logic frame being one higher than
  93. // the last one we had the commands ready for.
  94. if (TheGameLogic->getFrame() == m_lastFrame) {
  95. time_t curTime = timeGetTime();
  96. if ((curTime - m_lastFrameTime) > TheGlobalData->m_networkDisconnectTime) {
  97. if (m_disconnectState == DISCONNECTSTATETYPE_SCREENOFF) {
  98. turnOnScreen(conMgr);
  99. }
  100. sendKeepAlive(conMgr);
  101. }
  102. } else {
  103. nextFrame(TheGameLogic->getFrame(), conMgr);
  104. }
  105. if (m_disconnectState != DISCONNECTSTATETYPE_SCREENOFF) {
  106. updateDisconnectStatus(conMgr);
  107. // check to see if we need to send pings
  108. if (m_pingFrame < TheGameLogic->getFrame())
  109. {
  110. time_t curTime = timeGetTime();
  111. if ((curTime - m_lastFrameTime) > 10000) /// @todo: plug in some better measure here
  112. {
  113. m_pingFrame = TheGameLogic->getFrame();
  114. m_pingsSent = 0;
  115. m_pingsRecieved = 0;
  116. // Send the pings
  117. if (ThePinger)
  118. {
  119. //use next ping server
  120. static int serverIndex = 0;
  121. serverIndex++;
  122. if( serverIndex >= TheGameSpyConfig->getPingServers().size() )
  123. serverIndex = 0; //wrap back to first ping server
  124. std::list<AsciiString>::iterator it = TheGameSpyConfig->getPingServers().begin();
  125. for( int i = 0; i < serverIndex; i++ )
  126. it++;
  127. PingRequest req;
  128. req.hostname = it->str();
  129. req.repetitions = 5;
  130. req.timeout = 2000;
  131. m_pingsSent = req.repetitions;
  132. ThePinger->addRequest(req);
  133. DEBUG_LOG(("DisconnectManager::update() - requesting %d pings of %d from %s\n",
  134. req.repetitions, req.timeout, req.hostname.c_str()));
  135. }
  136. }
  137. }
  138. // update the ping thread, tracking pings if we are on the same frame
  139. if (ThePinger)
  140. {
  141. PingResponse resp;
  142. while (ThePinger->getResponse(resp))
  143. {
  144. if (m_pingFrame != TheGameLogic->getFrame())
  145. {
  146. // wrong frame - we're not pinging yet
  147. DEBUG_LOG(("DisconnectManager::update() - discarding ping of %d from %s (%d reps)\n",
  148. resp.avgPing, resp.hostname.c_str(), resp.repetitions));
  149. }
  150. else
  151. {
  152. // right frame
  153. DEBUG_LOG(("DisconnectManager::update() - keeping ping of %d from %s (%d reps)\n",
  154. resp.avgPing, resp.hostname.c_str(), resp.repetitions));
  155. if (resp.avgPing < 2000)
  156. {
  157. m_pingsRecieved += resp.repetitions;
  158. }
  159. }
  160. }
  161. }
  162. }
  163. }
  164. UnsignedInt DisconnectManager::getPingFrame()
  165. {
  166. return m_pingFrame;
  167. }
  168. Int DisconnectManager::getPingsSent()
  169. {
  170. return m_pingsSent;
  171. }
  172. Int DisconnectManager::getPingsRecieved()
  173. {
  174. return m_pingsRecieved;
  175. }
  176. void DisconnectManager::updateDisconnectStatus(ConnectionManager *conMgr) {
  177. for (Int i = 0; i < MAX_SLOTS; ++i) {
  178. if (conMgr->isPlayerConnected(i)) {
  179. Int slot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
  180. if (slot != -1) {
  181. time_t curTime = timeGetTime();
  182. time_t newTime = TheGlobalData->m_networkPlayerTimeoutTime - (curTime - m_playerTimeouts[slot]);
  183. // if someone is more than 2/3 timed out, lets get our frame numbers sync'd up. Also if someone is voted out
  184. // lets do the same thing.
  185. if (m_haveNotifiedOtherPlayersOfCurrentFrame == FALSE) {
  186. if ((newTime < TheGlobalData->m_networkPlayerTimeoutTime / 3) || (isPlayerVotedOut(slot, conMgr) == TRUE)) {
  187. TheNetwork->notifyOthersOfCurrentFrame();
  188. m_haveNotifiedOtherPlayersOfCurrentFrame = TRUE;
  189. }
  190. DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - curTime = %d, m_timeOfDisconnectScreenOn = %d, curTime - m_timeOfDisconnectScreenOn = %d\n", curTime, m_timeOfDisconnectScreenOn, curTime - m_timeOfDisconnectScreenOn));
  191. if (m_timeOfDisconnectScreenOn != 0) {
  192. if ((curTime - m_timeOfDisconnectScreenOn) > TheGlobalData->m_networkDisconnectScreenNotifyTime) {
  193. TheNetwork->notifyOthersOfCurrentFrame();
  194. m_haveNotifiedOtherPlayersOfCurrentFrame = TRUE;
  195. }
  196. }
  197. }
  198. if ((newTime < 0) || (isPlayerVotedOut(slot, conMgr) == TRUE)) {
  199. newTime = 0;
  200. DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - player %d(translated slot %d) has been voted out or timed out\n", i, slot));
  201. if (allOnSameFrame(conMgr) == TRUE) {
  202. DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - all on same frame\n"));
  203. if (isLocalPlayerNextPacketRouter(conMgr) == TRUE) {
  204. DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - local player is next packet router\n"));
  205. DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - about to do the disconnect procedure for player %d\n", i));
  206. sendDisconnectCommand(i, conMgr);
  207. disconnectPlayer(i, conMgr);
  208. sendPlayerDestruct(i, conMgr);
  209. } else {
  210. DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - local player is not the next packet router\n"));
  211. }
  212. } else {
  213. DEBUG_LOG(("DisconnectManager::updateDisconnectStatus - not all on same frame\n"));
  214. }
  215. }
  216. TheDisconnectMenu->setPlayerTimeoutTime(slot, newTime);
  217. }
  218. }
  219. }
  220. }
  221. void DisconnectManager::updateWaitForPacketRouter(ConnectionManager *conMgr) {
  222. /*
  223. time_t curTime = timeGetTime();
  224. time_t newTime = TheGlobalData->m_networkPlayerTimeoutTime - (curTime - m_packetRouterTimeout);
  225. if (newTime < 0) {
  226. newTime = 0;
  227. // The guy that we were hoping would be the new packet router isn't. We're screwed, get out of the game.
  228. DEBUG_LOG(("DisconnectManager::updateWaitForPacketRouter - timed out waiting for new packet router, quitting game\n"));
  229. TheNetwork->quitGame();
  230. }
  231. TheDisconnectMenu->setPacketRouterTimeoutTime(newTime);
  232. */
  233. }
  234. void DisconnectManager::processDisconnectCommand(NetCommandRef *ref, ConnectionManager *conMgr) {
  235. NetCommandMsg *msg = ref->getCommand();
  236. if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTKEEPALIVE) {
  237. processDisconnectKeepAlive(msg, conMgr);
  238. } else if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTPLAYER) {
  239. processDisconnectPlayer(msg, conMgr);
  240. } else if (msg->getNetCommandType() == NETCOMMANDTYPE_PACKETROUTERQUERY) {
  241. processPacketRouterQuery(msg, conMgr);
  242. } else if (msg->getNetCommandType() == NETCOMMANDTYPE_PACKETROUTERACK) {
  243. processPacketRouterAck(msg, conMgr);
  244. } else if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTVOTE) {
  245. processDisconnectVote(msg, conMgr);
  246. } else if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) {
  247. processDisconnectFrame(msg, conMgr);
  248. } else if (msg->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTSCREENOFF) {
  249. processDisconnectScreenOff(msg, conMgr);
  250. }
  251. }
  252. void DisconnectManager::processDisconnectKeepAlive(NetCommandMsg *msg, ConnectionManager *conMgr) {
  253. NetDisconnectKeepAliveCommandMsg *cmdMsg = (NetDisconnectKeepAliveCommandMsg *)msg;
  254. Int slot = translatedSlotPosition(cmdMsg->getPlayerID(), conMgr->getLocalPlayerID());
  255. if (slot != -1) {
  256. resetPlayerTimeout(slot);
  257. }
  258. }
  259. void DisconnectManager::processDisconnectPlayer(NetCommandMsg *msg, ConnectionManager *conMgr) {
  260. NetDisconnectPlayerCommandMsg *cmdMsg = (NetDisconnectPlayerCommandMsg *)msg;
  261. DEBUG_LOG(("DisconnectManager::processDisconnectPlayer - Got disconnect player command from player %d. Disconnecting player %d on frame %d\n", msg->getPlayerID(), cmdMsg->getDisconnectSlot(), cmdMsg->getDisconnectFrame()));
  262. DEBUG_ASSERTCRASH(TheGameLogic->getFrame() == cmdMsg->getDisconnectFrame(), ("disconnecting player on the wrong frame!!!"));
  263. disconnectPlayer(cmdMsg->getDisconnectSlot(), conMgr);
  264. }
  265. void DisconnectManager::processPacketRouterQuery(NetCommandMsg *msg, ConnectionManager *conMgr) {
  266. NetPacketRouterQueryCommandMsg *cmdMsg = (NetPacketRouterQueryCommandMsg *)msg;
  267. DEBUG_LOG(("DisconnectManager::processPacketRouterQuery - got a packet router query command from player %d\n", msg->getPlayerID()));
  268. if (conMgr->getPacketRouterSlot() == conMgr->getLocalPlayerID()) {
  269. NetPacketRouterAckCommandMsg *ackmsg = newInstance(NetPacketRouterAckCommandMsg);
  270. ackmsg->setPlayerID(conMgr->getLocalPlayerID());
  271. if (DoesCommandRequireACommandID(ackmsg->getNetCommandType()) == TRUE) {
  272. ackmsg->setID(GenerateNextCommandID());
  273. }
  274. DEBUG_LOG(("DisconnectManager::processPacketRouterQuery - We are the new packet router, responding with an packet router ack. Local player is %d\n", ackmsg->getPlayerID()));
  275. conMgr->sendLocalCommandDirect(ackmsg, 1 << cmdMsg->getPlayerID());
  276. ackmsg->detach();
  277. } else {
  278. DEBUG_LOG(("DisconnectManager::processPacketRouterQuery - We are NOT the new packet router, these are not the droids you're looking for.\n"));
  279. }
  280. }
  281. void DisconnectManager::processPacketRouterAck(NetCommandMsg *msg, ConnectionManager *conMgr) {
  282. NetPacketRouterAckCommandMsg *cmdMsg = (NetPacketRouterAckCommandMsg *)msg;
  283. DEBUG_LOG(("DisconnectManager::processPacketRouterAck - got packet router ack command from player %d\n", msg->getPlayerID()));
  284. if (conMgr->getPacketRouterSlot() == cmdMsg->getPlayerID()) {
  285. DEBUG_LOG(("DisconnectManager::processPacketRouterAck - packet router command is from who it should be.\n"));
  286. resetPacketRouterTimeout();
  287. Int currentPacketRouterSlot = conMgr->getPacketRouterSlot();
  288. Int currentPacketRouterIndex = 0;
  289. while ((currentPacketRouterSlot != conMgr->getPacketRouterFallbackSlot(currentPacketRouterIndex)) && (currentPacketRouterIndex < MAX_SLOTS)) {
  290. ++currentPacketRouterIndex;
  291. }
  292. DEBUG_ASSERTCRASH((currentPacketRouterIndex < MAX_SLOTS), ("Invalid packet router index"));
  293. DEBUG_LOG(("DisconnectManager::processPacketRouterAck - New packet router confirmed, resending pending commands\n"));
  294. conMgr->resendPendingCommands();
  295. m_currentPacketRouterIndex = currentPacketRouterIndex;
  296. DEBUG_LOG(("DisconnectManager::processPacketRouterAck - Setting disconnect state to screen on.\n"));
  297. m_disconnectState = DISCONNECTSTATETYPE_SCREENON; ///< set it to screen on so that the next call to AllCommandsReady can set up everything for the next frame properly.
  298. }
  299. }
  300. void DisconnectManager::processDisconnectVote(NetCommandMsg *msg, ConnectionManager *conMgr) {
  301. NetDisconnectVoteCommandMsg *cmdMsg = (NetDisconnectVoteCommandMsg *)msg;
  302. DEBUG_LOG(("DisconnectManager::processDisconnectVote - Got a disconnect vote for player %d command from player %d\n", cmdMsg->getSlot(), cmdMsg->getPlayerID()));
  303. Int transSlot = translatedSlotPosition(msg->getPlayerID(), conMgr->getLocalPlayerID());
  304. if (isPlayerInGame(transSlot, conMgr) == FALSE) {
  305. // if they've been timed out, voted out, disconnected, don't count their vote.
  306. return;
  307. }
  308. applyDisconnectVote(cmdMsg->getSlot(), cmdMsg->getVoteFrame(), cmdMsg->getPlayerID(), conMgr);
  309. }
  310. void DisconnectManager::processDisconnectFrame(NetCommandMsg *msg, ConnectionManager *conMgr) {
  311. NetDisconnectFrameCommandMsg *cmdMsg = (NetDisconnectFrameCommandMsg *)msg;
  312. UnsignedInt playerID = cmdMsg->getPlayerID();
  313. if (m_disconnectFrames[playerID] >= cmdMsg->getDisconnectFrame()) {
  314. // this message isn't valid, we have a disconnect frame that is later than this already.
  315. return;
  316. }
  317. if (m_disconnectFramesReceived[playerID] == TRUE) {
  318. DEBUG_LOG(("DisconnectManager::processDisconnectFrame - Got two disconnect frames without an intervening disconnect screen off command from player %d. Frames are %d and %d\n", playerID, m_disconnectFrames[playerID], cmdMsg->getDisconnectFrame()));
  319. }
  320. DEBUG_LOG(("DisconnectManager::processDisconnectFrame - about to call resetPlayersVotes for player %d\n", playerID));
  321. resetPlayersVotes(playerID, cmdMsg->getDisconnectFrame()-1, conMgr);
  322. m_disconnectFrames[playerID] = cmdMsg->getDisconnectFrame();
  323. m_disconnectFramesReceived[playerID] = TRUE;
  324. DEBUG_LOG(("DisconnectManager::processDisconnectFrame - Got a disconnect frame for player %d, frame = %d, local player is %d, local disconnect frame = %d, command id = %d\n", cmdMsg->getPlayerID(), cmdMsg->getDisconnectFrame(), conMgr->getLocalPlayerID(), m_disconnectFrames[conMgr->getLocalPlayerID()], cmdMsg->getID()));
  325. if (playerID == conMgr->getLocalPlayerID()) {
  326. DEBUG_LOG(("DisconnectManager::processDisconnectFrame - player %d is the local player\n", playerID));
  327. // we just got the message from the local player, check to see if we need to send
  328. // commands to anyone we already have heard from.
  329. for (Int i = 0; i < MAX_SLOTS; ++i) {
  330. if (i != playerID) {
  331. Int transSlot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
  332. if (isPlayerInGame(transSlot, conMgr) == TRUE) {
  333. if ((m_disconnectFrames[i] < m_disconnectFrames[playerID]) && (m_disconnectFramesReceived[i] == TRUE)) {
  334. DEBUG_LOG(("DisconnectManager::processDisconnectFrame - I have more frames than player %d, my frame = %d, their frame = %d\n", i, m_disconnectFrames[conMgr->getLocalPlayerID()], m_disconnectFrames[i]));
  335. conMgr->sendFrameDataToPlayer(i, m_disconnectFrames[i]);
  336. }
  337. }
  338. }
  339. }
  340. } else if ((m_disconnectFrames[playerID] < m_disconnectFrames[conMgr->getLocalPlayerID()]) && (m_disconnectFramesReceived[playerID] == TRUE)) {
  341. DEBUG_LOG(("DisconnectManager::processDisconnectFrame - I have more frames than player %d, my frame = %d, their frame = %d\n", playerID, m_disconnectFrames[conMgr->getLocalPlayerID()], m_disconnectFrames[playerID]));
  342. conMgr->sendFrameDataToPlayer(playerID, m_disconnectFrames[playerID]);
  343. }
  344. }
  345. void DisconnectManager::processDisconnectScreenOff(NetCommandMsg *msg, ConnectionManager *conMgr) {
  346. NetDisconnectScreenOffCommandMsg *cmdMsg = (NetDisconnectScreenOffCommandMsg *)msg;
  347. UnsignedInt playerID = cmdMsg->getPlayerID();
  348. DEBUG_LOG(("DisconnectManager::processDisconnectScreenOff - got a screen off command from player %d for frame %d\n", cmdMsg->getPlayerID(), cmdMsg->getNewFrame()));
  349. if ((playerID < 0) || (playerID >= MAX_SLOTS)) {
  350. return;
  351. }
  352. UnsignedInt newFrame = cmdMsg->getNewFrame();
  353. if (newFrame >= m_disconnectFrames[playerID]) {
  354. DEBUG_LOG(("DisconnectManager::processDisconnectScreenOff - resetting the disconnect screen status for player %d\n", playerID));
  355. m_disconnectFramesReceived[playerID] = FALSE;
  356. m_disconnectFrames[playerID] = newFrame; // just in case we get packets out of order and the disconnect screen off message gets here before the disconnect frame message.
  357. DEBUG_LOG(("DisconnectManager::processDisconnectScreenOff - about to call resetPlayersVotes for player %d\n", playerID));
  358. resetPlayersVotes(playerID, cmdMsg->getNewFrame(), conMgr);
  359. }
  360. }
  361. void DisconnectManager::applyDisconnectVote(Int slot, UnsignedInt frame, Int fromSlot, ConnectionManager *conMgr) {
  362. m_playerVotes[slot][fromSlot].vote = TRUE;
  363. m_playerVotes[slot][fromSlot].frame = frame;
  364. Int numVotes = countVotesForPlayer(slot);
  365. DEBUG_LOG(("DisconnectManager::applyDisconnectVote - added a vote to disconnect slot %d, from slot %d, for frame %d, current votes are %d\n", slot, fromSlot, frame, numVotes));
  366. Int transSlot = translatedSlotPosition(slot, conMgr->getLocalPlayerID());
  367. if (transSlot != -1) {
  368. TheDisconnectMenu->updateVotes(transSlot, numVotes);
  369. }
  370. }
  371. void DisconnectManager::nextFrame(UnsignedInt frame, ConnectionManager *conMgr) {
  372. m_lastFrame = frame;
  373. m_lastFrameTime = timeGetTime();
  374. resetPlayerTimeouts(conMgr);
  375. }
  376. void DisconnectManager::allCommandsReady(UnsignedInt frame, ConnectionManager *conMgr, Bool waitForPacketRouter) {
  377. if (m_disconnectState != DISCONNECTSTATETYPE_SCREENOFF) {
  378. DEBUG_LOG(("DisconnectManager::allCommandsReady - setting screen state to off.\n"));
  379. TheDisconnectMenu->hideScreen();
  380. m_disconnectState = DISCONNECTSTATETYPE_SCREENOFF;
  381. TheNetwork->notifyOthersOfNewFrame(frame);
  382. // reset the votes since we're moving to a new frame.
  383. for (Int i = 0; i < MAX_SLOTS; ++i) {
  384. m_playerVotes[i][conMgr->getLocalPlayerID()].vote = FALSE;
  385. }
  386. DEBUG_LOG(("DisconnectManager::allCommandsReady - resetting m_timeOfDisconnectScreenOn\n"));
  387. m_timeOfDisconnectScreenOn = 0;
  388. }
  389. }
  390. Bool DisconnectManager::allowedToContinue() {
  391. if (m_disconnectState != DISCONNECTSTATETYPE_SCREENOFF) {
  392. return FALSE;
  393. }
  394. return TRUE;
  395. }
  396. void DisconnectManager::sendKeepAlive(ConnectionManager *conMgr) {
  397. time_t curTime = timeGetTime();
  398. if (((curTime - m_lastKeepAliveSendTime) > 500) || (m_lastKeepAliveSendTime == -1)) {
  399. NetDisconnectKeepAliveCommandMsg *msg = newInstance(NetDisconnectKeepAliveCommandMsg);
  400. msg->setPlayerID(conMgr->getLocalPlayerID());
  401. if (DoesCommandRequireACommandID(msg->getNetCommandType()) == TRUE) {
  402. msg->setID(GenerateNextCommandID());
  403. }
  404. conMgr->sendLocalCommandDirect(msg, 0xff ^ (1 << msg->getPlayerID()));
  405. msg->detach();
  406. m_lastKeepAliveSendTime = curTime;
  407. }
  408. }
  409. void DisconnectManager::populateDisconnectScreen(ConnectionManager *conMgr) {
  410. for (Int i = 0; i < MAX_SLOTS; ++i) {
  411. UnicodeString name = conMgr->getPlayerName(i);
  412. Int slot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
  413. if (slot != -1) {
  414. TheDisconnectMenu->setPlayerName(slot, name);
  415. Int numVotes = countVotesForPlayer(i);
  416. TheDisconnectMenu->updateVotes(slot, numVotes);
  417. }
  418. }
  419. }
  420. Int DisconnectManager::translatedSlotPosition(Int slot, Int localSlot) {
  421. if (slot < localSlot) {
  422. return slot;
  423. }
  424. if (slot == localSlot) {
  425. return -1;
  426. }
  427. return (slot - 1);
  428. }
  429. Int DisconnectManager::untranslatedSlotPosition(Int slot, Int localSlot) {
  430. if (slot == -1) {
  431. return localSlot;
  432. }
  433. if (slot < localSlot) {
  434. return slot;
  435. }
  436. return (slot + 1);
  437. }
  438. void DisconnectManager::resetPlayerTimeouts(ConnectionManager *conMgr) {
  439. // reset the player timeouts.
  440. for (Int i = 0; i < MAX_SLOTS; ++i) {
  441. Int slot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
  442. if (slot != -1) {
  443. resetPlayerTimeout(slot);
  444. }
  445. }
  446. }
  447. void DisconnectManager::resetPlayerTimeout(Int slot) {
  448. m_playerTimeouts[slot] = timeGetTime();
  449. }
  450. void DisconnectManager::resetPacketRouterTimeout() {
  451. m_packetRouterTimeout = timeGetTime();
  452. }
  453. void DisconnectManager::turnOnScreen(ConnectionManager *conMgr) {
  454. TheDisconnectMenu->showScreen();
  455. DEBUG_LOG(("DisconnectManager::turnOnScreen - turning on screen on frame %d\n", TheGameLogic->getFrame()));
  456. m_disconnectState = DISCONNECTSTATETYPE_SCREENON;
  457. m_lastKeepAliveSendTime = -1;
  458. populateDisconnectScreen(conMgr);
  459. resetPlayerTimeouts(conMgr);
  460. TheDisconnectMenu->hidePacketRouterTimeout();
  461. m_haveNotifiedOtherPlayersOfCurrentFrame = FALSE;
  462. m_timeOfDisconnectScreenOn = timeGetTime();
  463. DEBUG_LOG(("DisconnectManager::turnOnScreen - turned on screen at time %d\n", m_timeOfDisconnectScreenOn));
  464. }
  465. void DisconnectManager::disconnectPlayer(Int slot, ConnectionManager *conMgr) {
  466. DEBUG_LOG(("DisconnectManager::disconnectPlayer - Disconnecting slot number %d on frame %d\n", slot, TheGameLogic->getFrame()));
  467. DEBUG_ASSERTCRASH((slot >= 0) && (slot < MAX_SLOTS), ("Attempting to disconnect an invalid slot number"));
  468. if ((slot < 0) || (slot >= (MAX_SLOTS))) {
  469. return;
  470. }
  471. if (TheGameInfo)
  472. {
  473. GameSlot *gSlot = TheGameInfo->getSlot( slot );
  474. if (gSlot)
  475. {
  476. gSlot->markAsDisconnected();
  477. }
  478. }
  479. Int transSlot = translatedSlotPosition(slot, conMgr->getLocalPlayerID());
  480. if (transSlot != -1) {
  481. // Ignore any disconnect commands that tell us to disconnect ourselves.
  482. // Get the disconnecting player off the disconnect window.
  483. UnicodeString uname = conMgr->getPlayerName(slot);
  484. TheRecorder->logPlayerDisconnect(uname, slot);
  485. TheDisconnectMenu->removePlayer(transSlot, uname);
  486. PlayerLeaveCode retcode = conMgr->disconnectPlayer(slot);
  487. DEBUG_ASSERTCRASH((retcode != PLAYERLEAVECODE_UNKNOWN), ("Invalid player leave code"));
  488. if (retcode == PLAYERLEAVECODE_PACKETROUTER) {
  489. DEBUG_LOG(("DisconnectManager::disconnectPlayer - disconnecting player was packet router.\n"));
  490. conMgr->resendPendingCommands();
  491. }
  492. }
  493. }
  494. void DisconnectManager::sendDisconnectCommand(Int slot, ConnectionManager *conMgr) {
  495. DEBUG_LOG(("DisconnectManager::sendDisconnectCommand - Sending disconnect command for slot number %d\n", slot));
  496. DEBUG_ASSERTCRASH((slot >= 0) && (slot < MAX_SLOTS), ("Attempting to send a disconnect command for an invalid slot number"));
  497. if ((slot < 0) || (slot >= (MAX_SLOTS))) {
  498. return;
  499. }
  500. UnsignedInt disconnectFrame = getMaxDisconnectFrame();
  501. // Need to do the NetDisconnectPlayerCommandMsg creation and sending here.
  502. NetDisconnectPlayerCommandMsg *msg = newInstance(NetDisconnectPlayerCommandMsg);
  503. msg->setDisconnectSlot(slot);
  504. msg->setDisconnectFrame(disconnectFrame);
  505. msg->setPlayerID(conMgr->getLocalPlayerID());
  506. if (DoesCommandRequireACommandID(msg->getNetCommandType())) {
  507. msg->setID(GenerateNextCommandID());
  508. }
  509. conMgr->sendLocalCommand(msg);
  510. DEBUG_LOG(("DisconnectManager::sendDisconnectCommand - Sending disconnect command for slot number %d for frame %d\n", slot, disconnectFrame));
  511. msg->detach();
  512. }
  513. void DisconnectManager::sendVoteCommand(Int slot, ConnectionManager *conMgr) {
  514. NetDisconnectVoteCommandMsg *msg = newInstance(NetDisconnectVoteCommandMsg);
  515. msg->setPlayerID(conMgr->getLocalPlayerID());
  516. msg->setSlot(slot);
  517. msg->setVoteFrame(TheGameLogic->getFrame());
  518. if (DoesCommandRequireACommandID(msg->getNetCommandType()) == TRUE) {
  519. msg->setID(GenerateNextCommandID());
  520. }
  521. conMgr->sendLocalCommandDirect(msg, 0xff & ~(1 << conMgr->getLocalPlayerID()));
  522. msg->detach();
  523. }
  524. void DisconnectManager::voteForPlayerDisconnect(Int slot, ConnectionManager *conMgr) {
  525. Int transSlot = untranslatedSlotPosition(slot, conMgr->getLocalPlayerID());
  526. if (m_playerVotes[transSlot][conMgr->getLocalPlayerID()].vote == FALSE) {
  527. m_playerVotes[transSlot][conMgr->getLocalPlayerID()].vote = TRUE;
  528. sendVoteCommand(transSlot, conMgr);
  529. // we use the game logic frame cause we might not have sent out our own disconnect frame yet.
  530. applyDisconnectVote(transSlot, TheGameLogic->getFrame(), conMgr->getLocalPlayerID(), conMgr);
  531. }
  532. }
  533. void DisconnectManager::recalculatePacketRouterIndex(ConnectionManager *conMgr) {
  534. Int currentPacketRouterSlot = conMgr->getPacketRouterSlot();
  535. m_currentPacketRouterIndex = 0;
  536. while ((currentPacketRouterSlot != conMgr->getPacketRouterFallbackSlot(m_currentPacketRouterIndex)) && (m_currentPacketRouterIndex < MAX_SLOTS)) {
  537. ++m_currentPacketRouterIndex;
  538. }
  539. DEBUG_ASSERTCRASH((m_currentPacketRouterIndex < MAX_SLOTS), ("Invalid packet router index"));
  540. }
  541. Bool DisconnectManager::allOnSameFrame(ConnectionManager *conMgr) {
  542. Bool retval = TRUE;
  543. for (Int i = 0; (i < MAX_SLOTS) && (retval == TRUE); ++i) {
  544. Int transSlot = translatedSlotPosition(i, conMgr->getLocalPlayerID());
  545. if (transSlot == -1) {
  546. continue;
  547. }
  548. if ((conMgr->isPlayerConnected(i) == TRUE) && (isPlayerInGame(transSlot, conMgr) == TRUE)) {
  549. // ok, i is someone who is in the game and hasn't timed out yet or been voted out.
  550. if (m_disconnectFramesReceived[i] == FALSE) {
  551. // we don't know what frame they are on yet.
  552. retval = FALSE;
  553. }
  554. if ((m_disconnectFramesReceived[i] == TRUE) && (m_disconnectFrames[conMgr->getLocalPlayerID()] != m_disconnectFrames[i])) {
  555. // We know their frame, but they aren't on the same frame as us.
  556. retval = FALSE;
  557. }
  558. }
  559. }
  560. return retval;
  561. }
  562. Bool DisconnectManager::isLocalPlayerNextPacketRouter(ConnectionManager *conMgr) {
  563. UnsignedInt localSlot = conMgr->getLocalPlayerID();
  564. UnsignedInt packetRouterSlot = conMgr->getPacketRouterSlot();
  565. Int transSlot = translatedSlotPosition(packetRouterSlot, localSlot);
  566. // stop when we have found a packet router that is connected
  567. while ((transSlot != -1) && (isPlayerInGame(transSlot, conMgr) == FALSE)) {
  568. packetRouterSlot = conMgr->getNextPacketRouterSlot(packetRouterSlot);
  569. if ((packetRouterSlot >= MAX_SLOTS) || (packetRouterSlot < 0)) {
  570. // don't know who the next packet router is going to be,
  571. // so this game is not going to go anywhere anymore.
  572. DEBUG_CRASH(("no more players left to be the packet router, this shouldn't happen."));
  573. return FALSE;
  574. }
  575. transSlot = translatedSlotPosition(packetRouterSlot, localSlot);
  576. }
  577. if (packetRouterSlot == localSlot) {
  578. return TRUE;
  579. }
  580. return FALSE;
  581. }
  582. Bool DisconnectManager::hasPlayerTimedOut(Int slot) {
  583. if (slot == -1) {
  584. return FALSE;
  585. }
  586. time_t newTime = TheGlobalData->m_networkPlayerTimeoutTime - (timeGetTime() - m_playerTimeouts[slot]);
  587. if (newTime <= 0) {
  588. return TRUE;
  589. }
  590. return FALSE;
  591. }
  592. // this function assumes that we are the packet router. (or at least that
  593. // we will be after everyone is getting disconnected)
  594. void DisconnectManager::sendPlayerDestruct(Int slot, ConnectionManager *conMgr) {
  595. UnsignedShort currentID = 0;
  596. if (DoesCommandRequireACommandID(NETCOMMANDTYPE_DESTROYPLAYER))
  597. {
  598. currentID = GenerateNextCommandID();
  599. }
  600. DEBUG_LOG(("Queueing DestroyPlayer %d for frame %d on frame %d as command %d\n",
  601. slot, TheNetwork->getExecutionFrame()+1, TheGameLogic->getFrame(), currentID));
  602. NetDestroyPlayerCommandMsg *netmsg = newInstance(NetDestroyPlayerCommandMsg);
  603. netmsg->setExecutionFrame(TheNetwork->getExecutionFrame()+1);
  604. netmsg->setPlayerID(conMgr->getLocalPlayerID());
  605. netmsg->setID(currentID);
  606. netmsg->setPlayerIndex(slot);
  607. conMgr->sendLocalCommand(netmsg);
  608. netmsg->detach();
  609. }
  610. // the 'slot' variable is supposed to be a translated slot position. (translated slot meaning
  611. // that it is the player's position in the disconnect menu)
  612. Bool DisconnectManager::isPlayerVotedOut(Int slot, ConnectionManager *conMgr) {
  613. if (slot == -1) {
  614. // we can't vote out ourselves.
  615. return FALSE;
  616. }
  617. Int transSlot = untranslatedSlotPosition(slot, conMgr->getLocalPlayerID());
  618. Int numVotes = countVotesForPlayer(transSlot);
  619. if (numVotes >= (conMgr->getNumPlayers() - 1)) {
  620. return TRUE;
  621. }
  622. return FALSE;
  623. }
  624. UnsignedInt DisconnectManager::getMaxDisconnectFrame() {
  625. UnsignedInt retval = 0;
  626. for (Int i = 0; i < MAX_SLOTS; ++i) {
  627. if (m_disconnectFrames[i] > retval) {
  628. retval = m_disconnectFrames[i];
  629. }
  630. }
  631. return retval;
  632. }
  633. Bool DisconnectManager::isPlayerInGame(Int slot, ConnectionManager *conMgr) {
  634. Int transSlot = untranslatedSlotPosition(slot, conMgr->getLocalPlayerID());
  635. DEBUG_ASSERTCRASH((transSlot >= 0) && (transSlot < MAX_SLOTS), ("invalid slot number"));
  636. if (((transSlot < 0) || (transSlot >= MAX_SLOTS)) || conMgr->isPlayerConnected(transSlot) == FALSE) {
  637. return FALSE;
  638. }
  639. if (isPlayerVotedOut(slot, conMgr) == TRUE) {
  640. return FALSE;
  641. }
  642. if (hasPlayerTimedOut(slot) == TRUE) {
  643. return FALSE;
  644. }
  645. return TRUE;
  646. }
  647. void DisconnectManager::playerHasAdvancedAFrame(Int slot, UnsignedInt frame) {
  648. // if they have advanced beyond the frame they had been previously disconnecting on.
  649. if (frame >= m_disconnectFrames[slot]) {
  650. m_disconnectFrames[slot] = frame; // just in case we get a disconnect frame command after this is called.
  651. m_disconnectFramesReceived[slot] = FALSE;
  652. }
  653. }
  654. Int DisconnectManager::countVotesForPlayer(Int slot) {
  655. if ((slot < 0) || (slot >= MAX_SLOTS)) {
  656. return 0;
  657. }
  658. Int retval = 0;
  659. for (Int i = 0; i < MAX_SLOTS; ++i) {
  660. // using TheGameLogic->getFrame() cause we might not have sent our disconnect frame yet.
  661. if ((m_playerVotes[slot][i].vote == TRUE) && (m_playerVotes[slot][i].frame == TheGameLogic->getFrame())) {
  662. ++retval;
  663. }
  664. }
  665. return retval;
  666. }
  667. void DisconnectManager::resetPlayersVotes(Int playerID, UnsignedInt frame, ConnectionManager *conMgr) {
  668. DEBUG_LOG(("DisconnectManager::resetPlayersVotes - resetting player %d's votes on frame %d\n", playerID, frame));
  669. // we need to reset this player's votes that happened before or on the given frame.
  670. for(Int i = 0; i < MAX_SLOTS; ++i) {
  671. if (m_playerVotes[i][playerID].frame <= frame) {
  672. DEBUG_LOG(("DisconnectManager::resetPlayersVotes - resetting player %d's vote for player %d from frame %d on frame %d\n", playerID, i, m_playerVotes[i][playerID].frame, frame));
  673. m_playerVotes[i][playerID].vote = FALSE;
  674. }
  675. }
  676. Int numVotes = countVotesForPlayer(playerID);
  677. DEBUG_LOG(("DisconnectManager::resetPlayersVotes - after adjusting votes, player %d has %d votes\n", playerID, numVotes));
  678. Int transSlot = translatedSlotPosition(playerID, conMgr->getLocalPlayerID());
  679. if (transSlot != -1) {
  680. TheDisconnectMenu->updateVotes(transSlot, numVotes);
  681. }
  682. }