Connection.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  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. ////////////////////////////////////////////////////////////////////////////////
  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 "GameNetwork/Connection.h"
  25. #include "GameNetwork/NetworkUtil.h"
  26. #include "GameLogic/GameLogic.h"
  27. enum { MaxQuitFlushTime = 30000 }; // wait this many milliseconds at most to retry things before quitting
  28. /**
  29. * The constructor.
  30. */
  31. Connection::Connection() {
  32. m_transport = NULL;
  33. m_user = NULL;
  34. m_netCommandList = NULL;
  35. m_retryTime = 2000; // set retry time to 2 seconds.
  36. m_lastTimeSent = 0;
  37. m_frameGrouping = 1;
  38. m_isQuitting = false;
  39. m_quitTime = 0;
  40. // Added By Sadullah Nader
  41. // clearing out the latency tracker
  42. m_averageLatency = 0.0f;
  43. Int i;
  44. for(i = 0; i < CONNECTION_LATENCY_HISTORY_LENGTH; i++)
  45. {
  46. m_latencies[i] = 0.0f;
  47. }
  48. // End Add
  49. }
  50. /**
  51. * The destructor.
  52. */
  53. Connection::~Connection() {
  54. if (m_user != NULL) {
  55. m_user->deleteInstance();
  56. m_user = NULL;
  57. }
  58. if (m_netCommandList != NULL) {
  59. m_netCommandList->deleteInstance();
  60. m_netCommandList = NULL;
  61. }
  62. }
  63. /**
  64. * Initialize the connection and any subsystems.
  65. */
  66. void Connection::init() {
  67. m_transport = NULL;
  68. if (m_user != NULL) {
  69. m_user->deleteInstance();
  70. m_user = NULL;
  71. }
  72. if (m_netCommandList == NULL) {
  73. m_netCommandList = newInstance(NetCommandList);
  74. m_netCommandList->init();
  75. }
  76. m_netCommandList->reset();
  77. m_lastTimeSent = 0;
  78. m_frameGrouping = 1;
  79. m_numRetries = 0;
  80. m_retryMetricsTime = 0;
  81. for (Int i = 0; i < CONNECTION_LATENCY_HISTORY_LENGTH; ++i) {
  82. m_latencies[i] = 0;
  83. }
  84. m_averageLatency = 0;
  85. m_isQuitting = FALSE;
  86. m_quitTime = 0;
  87. }
  88. /**
  89. * Take the connection back to the initial state.
  90. */
  91. void Connection::reset() {
  92. init();
  93. }
  94. /**
  95. * Doesn't really do anything.
  96. */
  97. void Connection::update() {
  98. }
  99. /**
  100. * Attach the transport object that this connection should use.
  101. */
  102. void Connection::attachTransport(Transport *transport) {
  103. m_transport = transport;
  104. }
  105. /**
  106. * Assign this connection a user. This is the user to whome we send all our packetized goodies.
  107. */
  108. void Connection::setUser(User *user) {
  109. if (m_user != NULL) {
  110. m_user->deleteInstance();
  111. }
  112. m_user = user;
  113. }
  114. /**
  115. * Return the user object.
  116. */
  117. User * Connection::getUser() {
  118. return m_user;
  119. }
  120. /**
  121. * Add this network command to the send queue for this connection.
  122. * The relay is the mask specifying the people the person we are sending to should send to.
  123. * The relay mostly has to do with the packet router.
  124. */
  125. void Connection::sendNetCommandMsg(NetCommandMsg *msg, UnsignedByte relay) {
  126. static NetPacket *packet = NULL;
  127. // this is done so we don't have to allocate and delete a packet every time we send a message.
  128. if (packet == NULL) {
  129. packet = newInstance(NetPacket);
  130. }
  131. if (m_isQuitting)
  132. return;
  133. if (m_netCommandList != NULL) {
  134. // check to see if this command will fit in a packet. If not, we need to split it up.
  135. // we are splitting up the command here so that the retry logic will not try to
  136. // resend the ENTIRE command (i.e. multiple packets work of data) and only do the retry
  137. // one wrapper command at a time.
  138. packet->reset();
  139. NetCommandRef *tempref = NEW_NETCOMMANDREF(msg);
  140. Bool msgFits = packet->addCommand(tempref);
  141. tempref->deleteInstance(); // delete the temporary reference.
  142. tempref = NULL;
  143. if (!msgFits) {
  144. NetCommandRef *origref = NEW_NETCOMMANDREF(msg);
  145. origref->setRelay(relay);
  146. // the message doesn't fit in a single packet, need to split it up.
  147. NetPacketList packetList = NetPacket::ConstructBigCommandPacketList(origref);
  148. NetPacketListIter tempPacketPtr = packetList.begin();
  149. while (tempPacketPtr != packetList.end()) {
  150. NetPacket *tempPacket = (*tempPacketPtr);
  151. NetCommandList *list = tempPacket->getCommandList();
  152. NetCommandRef *ref1 = list->getFirstMessage();
  153. while (ref1 != NULL) {
  154. NetCommandRef *ref2 = m_netCommandList->addMessage(ref1->getCommand());
  155. ref2->setRelay(relay);
  156. ref1 = ref1->getNext();
  157. }
  158. tempPacket->deleteInstance();
  159. tempPacket = NULL;
  160. ++tempPacketPtr;
  161. list->deleteInstance();
  162. list = NULL;
  163. }
  164. origref->deleteInstance();
  165. origref = NULL;
  166. return;
  167. }
  168. // the message fits in a packet, add to the command list normally.
  169. NetCommandRef *ref = m_netCommandList->addMessage(msg);
  170. if (ref != NULL) {
  171. /*
  172. #if ((defined(_DEBUG)) || (defined(_INTERNAL)))
  173. if (msg->getNetCommandType() == NETCOMMANDTYPE_GAMECOMMAND) {
  174. DEBUG_LOG(("Connection::sendNetCommandMsg - added game command %d to net command list for frame %d.\n",
  175. msg->getID(), msg->getExecutionFrame()));
  176. } else if (msg->getNetCommandType() == NETCOMMANDTYPE_FRAMEINFO) {
  177. DEBUG_LOG(("Connection::sendNetCommandMsg - added frame info for frame %d\n", msg->getExecutionFrame()));
  178. }
  179. #endif // _DEBUG || _INTERNAL
  180. */
  181. ref->setRelay(relay);
  182. }
  183. }
  184. }
  185. void Connection::clearCommandsExceptFrom( Int playerIndex )
  186. {
  187. NetCommandRef *tmp = m_netCommandList->getFirstMessage();
  188. while (tmp)
  189. {
  190. NetCommandMsg *msg = tmp->getCommand();
  191. if (msg->getPlayerID() != playerIndex)
  192. {
  193. DEBUG_LOG(("Connection::clearCommandsExceptFrom(%d) - clearing a command from %d for frame %d\n",
  194. playerIndex, tmp->getCommand()->getPlayerID(), tmp->getCommand()->getExecutionFrame()));
  195. m_netCommandList->removeMessage(tmp);
  196. NetCommandRef *toDelete = tmp;
  197. tmp = tmp->getNext();
  198. toDelete->deleteInstance();
  199. } else {
  200. tmp = tmp->getNext();
  201. }
  202. }
  203. }
  204. Bool Connection::isQueueEmpty() {
  205. if (m_netCommandList->getFirstMessage() == NULL) {
  206. return TRUE;
  207. }
  208. return FALSE;
  209. }
  210. void Connection::setQuitting( void )
  211. {
  212. m_isQuitting = TRUE;
  213. m_quitTime = timeGetTime();
  214. DEBUG_LOG(("Connection::setQuitting() at time %d\n", m_quitTime));
  215. }
  216. /**
  217. * This is the good part. We take all the network commands queued up for this connection,
  218. * packetize them and put them on the transport's send queue for actual sending.
  219. */
  220. UnsignedInt Connection::doSend() {
  221. Int numpackets = 0;
  222. time_t curtime = timeGetTime();
  223. Bool couldQueue = TRUE;
  224. // Do this check first, since it's an important fail-safe
  225. if (m_isQuitting && curtime > m_quitTime + MaxQuitFlushTime)
  226. {
  227. DEBUG_LOG(("Timed out a quitting connection. Deleting all %d messages\n", m_netCommandList->length()));
  228. m_netCommandList->reset();
  229. return 0;
  230. }
  231. if ((curtime - m_lastTimeSent) < m_frameGrouping) {
  232. // DEBUG_LOG(("not sending packet, time = %d, m_lastFrameSent = %d, m_frameGrouping = %d\n", curtime, m_lastTimeSent, m_frameGrouping));
  233. return 0;
  234. }
  235. // iterate through all the messages and put them into a packet(s).
  236. NetCommandRef *msg = m_netCommandList->getFirstMessage();
  237. while ((msg != NULL) && couldQueue) {
  238. NetPacket *packet = newInstance(NetPacket);
  239. packet->init();
  240. packet->setAddress(m_user->GetIPAddr(), m_user->GetPort());
  241. Bool notDone = TRUE;
  242. // add the command messages until either we run out of messages or the packet is full.
  243. while ((msg != NULL) && notDone) {
  244. NetCommandRef *next = msg->getNext(); // Need this since msg could be deleted
  245. time_t timeLastSent = msg->getTimeLastSent();
  246. if (((curtime - timeLastSent) > m_retryTime) || (timeLastSent == -1)) {
  247. notDone = packet->addCommand(msg);
  248. if (notDone) {
  249. // the msg command was added to the packet.
  250. if (CommandRequiresAck(msg->getCommand())) {
  251. if (timeLastSent != -1) {
  252. ++m_numRetries;
  253. }
  254. doRetryMetrics();
  255. msg->setTimeLastSent(curtime);
  256. } else {
  257. m_netCommandList->removeMessage(msg);
  258. msg->deleteInstance();
  259. }
  260. }
  261. }
  262. msg = next;
  263. }
  264. if (msg != NULL) {
  265. DEBUG_LOG(("didn't finish sending all commands in connection\n"));
  266. }
  267. ++numpackets;
  268. /// @todo Make the act of giving the transport object a packet to send more efficient. Make the transport take a NetPacket object rather than the raw data, thus avoiding an extra memcpy.
  269. if (packet->getNumCommands() > 0) {
  270. // If the packet actually has any information to give, give it to the transport object
  271. // for transmission.
  272. couldQueue = m_transport->queueSend(packet->getAddr(), packet->getPort(), packet->getData(), packet->getLength());
  273. m_lastTimeSent = curtime;
  274. }
  275. if (packet != NULL) {
  276. packet->deleteInstance(); // delete the packet now that we're done with it.
  277. }
  278. }
  279. return numpackets;
  280. }
  281. NetCommandRef * Connection::processAck(NetAckStage1CommandMsg *msg) {
  282. return processAck(msg->getCommandID(), msg->getOriginalPlayerID());
  283. }
  284. NetCommandRef * Connection::processAck(NetAckBothCommandMsg *msg) {
  285. return processAck(msg->getCommandID(), msg->getOriginalPlayerID());
  286. }
  287. NetCommandRef * Connection::processAck(NetCommandMsg *msg) {
  288. if (msg->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE1) {
  289. NetAckStage1CommandMsg *ackmsg = (NetAckStage1CommandMsg *)msg;
  290. return processAck(ackmsg);
  291. }
  292. if (msg->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH) {
  293. NetAckBothCommandMsg *ackmsg = (NetAckBothCommandMsg *)msg;
  294. return processAck(ackmsg);
  295. }
  296. return NULL;
  297. }
  298. /**
  299. * The person we are sending to has ack'd one of the messages we sent him.
  300. * Take that message off the list of commands to send.
  301. */
  302. NetCommandRef * Connection::processAck(UnsignedShort commandID, UnsignedByte originalPlayerID) {
  303. NetCommandRef *temp = m_netCommandList->getFirstMessage();
  304. while ((temp != NULL) && ((temp->getCommand()->getID() != commandID) || (temp->getCommand()->getPlayerID() != originalPlayerID))) {
  305. // cycle through the commands till we find the one we need to remove.
  306. // Need to check for both the command ID and the player ID.
  307. temp = temp->getNext();
  308. }
  309. if (temp == NULL) {
  310. return NULL;
  311. }
  312. #if defined(_DEBUG) || defined(_INTERNAL)
  313. Bool doDebug = FALSE;
  314. if (temp->getCommand()->getNetCommandType() == NETCOMMANDTYPE_DISCONNECTFRAME) {
  315. doDebug = TRUE;
  316. }
  317. #endif
  318. Int index = temp->getCommand()->getID() % CONNECTION_LATENCY_HISTORY_LENGTH;
  319. m_averageLatency -= ((Real)(m_latencies[index])) / CONNECTION_LATENCY_HISTORY_LENGTH;
  320. Real lat = timeGetTime() - temp->getTimeLastSent();
  321. m_averageLatency += lat / CONNECTION_LATENCY_HISTORY_LENGTH;
  322. m_latencies[index] = lat;
  323. #if defined(_DEBUG) || defined(_INTERNAL)
  324. if (doDebug == TRUE) {
  325. DEBUG_LOG(("Connection::processAck - disconnect frame command %d found, removing from command list.\n", commandID));
  326. }
  327. #endif
  328. m_netCommandList->removeMessage(temp);
  329. return temp;
  330. }
  331. void Connection::setFrameGrouping(time_t frameGrouping) {
  332. m_frameGrouping = frameGrouping;
  333. // m_retryTime = frameGrouping * 4;
  334. }
  335. void Connection::doRetryMetrics() {
  336. static Int numSeconds = 0;
  337. time_t curTime = timeGetTime();
  338. if ((curTime - m_retryMetricsTime) > 10000) {
  339. m_retryMetricsTime = curTime;
  340. ++numSeconds;
  341. // DEBUG_LOG(("Retries in the last 10 seconds = %d, average latency = %fms\n", m_numRetries, m_averageLatency));
  342. m_numRetries = 0;
  343. // m_retryTime = m_averageLatency * 1.5;
  344. }
  345. }
  346. #if defined(_DEBUG) || (_INTERNAL)
  347. void Connection::debugPrintCommands() {
  348. NetCommandRef *ref = m_netCommandList->getFirstMessage();
  349. while (ref != NULL) {
  350. DEBUG_LOG(("Connection::debugPrintCommands - ID: %d\tType: %s\tRelay: 0x%X for frame %d\n",
  351. ref->getCommand()->getID(), GetAsciiNetCommandType(ref->getCommand()->getNetCommandType()).str(),
  352. ref->getRelay(), ref->getCommand()->getExecutionFrame()));
  353. ref = ref->getNext();
  354. }
  355. }
  356. #endif