NetCommandList.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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/NetCommandList.h"
  25. #include "GameNetwork/NetworkUtil.h"
  26. /**
  27. * Constructor.
  28. */
  29. NetCommandList::NetCommandList() {
  30. m_first = NULL;
  31. m_last = NULL;
  32. m_lastMessageInserted = NULL;
  33. }
  34. /**
  35. * Destructor.
  36. */
  37. NetCommandList::~NetCommandList() {
  38. reset();
  39. }
  40. /**
  41. * Append the given list of commands to this list.
  42. */
  43. void NetCommandList::appendList(NetCommandList *list) {
  44. if (list == NULL) {
  45. return;
  46. }
  47. // Need to do it this way because of the reference counting that needs to happen in appendMessage.
  48. NetCommandRef *msg = list->getFirstMessage();
  49. NetCommandRef *next = NULL;
  50. while (msg != NULL) {
  51. next = msg->getNext();
  52. NetCommandRef *temp = addMessage(msg->getCommand());
  53. if (temp != NULL) {
  54. temp->setRelay(msg->getRelay());
  55. }
  56. msg = next;
  57. }
  58. }
  59. /**
  60. * Return the first message in this list.
  61. */
  62. NetCommandRef *NetCommandList::getFirstMessage() {
  63. return m_first;
  64. }
  65. /**
  66. * Remove the given message from this list.
  67. */
  68. void NetCommandList::removeMessage(NetCommandRef *msg) {
  69. if (m_lastMessageInserted == msg) {
  70. m_lastMessageInserted = msg->getNext();
  71. }
  72. if (msg->getPrev() != NULL) {
  73. msg->getPrev()->setNext(msg->getNext());
  74. }
  75. if (msg->getNext() != NULL) {
  76. msg->getNext()->setPrev(msg->getPrev());
  77. }
  78. if (msg == m_first) {
  79. m_first = msg->getNext();
  80. }
  81. if (msg == m_last) {
  82. m_last = msg->getPrev();
  83. }
  84. msg->setNext(NULL);
  85. msg->setPrev(NULL);
  86. }
  87. /**
  88. * Initialize the list.
  89. */
  90. void NetCommandList::init() {
  91. reset();
  92. }
  93. /**
  94. * Reset the contents of this list.
  95. */
  96. void NetCommandList::reset() {
  97. NetCommandRef *temp = m_first;
  98. while (m_first != NULL) {
  99. temp = m_first->getNext();
  100. m_first->setNext(NULL);
  101. m_first->setPrev(NULL);
  102. m_first->deleteInstance();
  103. m_first = temp;
  104. }
  105. m_last = NULL;
  106. m_lastMessageInserted = NULL;
  107. }
  108. /**
  109. * Insert sorts msg. Assumes that all the previous message inserts were done using this function.
  110. * The message is sorted in based first on command type, then player id, and then command id.
  111. */
  112. NetCommandRef * NetCommandList::addMessage(NetCommandMsg *cmdMsg) {
  113. if (cmdMsg == NULL) {
  114. DEBUG_ASSERTCRASH(cmdMsg != NULL, ("NetCommandList::addMessage - command message was NULL"));
  115. return NULL;
  116. }
  117. // UnsignedInt id = cmdMsg->getID();
  118. NetCommandRef *msg = NEW_NETCOMMANDREF(cmdMsg);
  119. if (m_first == NULL) {
  120. // this is the first node, so we don't have to worry about ordering it.
  121. m_first = msg;
  122. m_last = msg;
  123. m_lastMessageInserted = msg;
  124. return msg;
  125. }
  126. if (m_lastMessageInserted != NULL) {
  127. // Messages that are inserted in order should just be put in one right after the other.
  128. // So saving the placement of the last message inserted can give us a huge boost in
  129. // efficiency.
  130. NetCommandRef *theNext = m_lastMessageInserted->getNext();
  131. if ((m_lastMessageInserted->getCommand()->getNetCommandType() == msg->getCommand()->getNetCommandType()) &&
  132. (m_lastMessageInserted->getCommand()->getPlayerID() == msg->getCommand()->getPlayerID()) &&
  133. (m_lastMessageInserted->getCommand()->getID() < msg->getCommand()->getID()) &&
  134. ((theNext == NULL) || ((theNext->getCommand()->getNetCommandType() > msg->getCommand()->getNetCommandType()) ||
  135. (theNext->getCommand()->getPlayerID() > msg->getCommand()->getPlayerID()) ||
  136. (theNext->getCommand()->getID() > msg->getCommand()->getID())))) {
  137. // Make sure this command isn't already in the list.
  138. if (isEqualCommandMsg(m_lastMessageInserted->getCommand(), msg->getCommand())) {
  139. // This command is already in the list, don't duplicate it.
  140. msg->deleteInstance();
  141. msg = NULL;
  142. return NULL;
  143. }
  144. if (theNext == NULL) {
  145. // this means that m_lastMessageInserted == m_last, so m_last should point to the msg that is being inserted.
  146. msg->setNext(m_lastMessageInserted->getNext());
  147. msg->setPrev(m_lastMessageInserted);
  148. m_lastMessageInserted->setNext(msg);
  149. m_lastMessageInserted = msg;
  150. m_last = msg;
  151. } else {
  152. msg->setNext(m_lastMessageInserted->getNext());
  153. msg->setPrev(m_lastMessageInserted);
  154. m_lastMessageInserted->setNext(msg);
  155. msg->getNext()->setPrev(msg);
  156. m_lastMessageInserted = msg;
  157. }
  158. return msg;
  159. }
  160. }
  161. if (msg->getCommand()->getNetCommandType() > m_last->getCommand()->getNetCommandType()) {
  162. // easy optimization for a command that goes at the end of the list
  163. // since they are likely to be added in order.
  164. // Make sure this command isn't already in the list.
  165. if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
  166. // This command is already in the list, don't duplicate it.
  167. msg->deleteInstance();
  168. msg = NULL;
  169. return NULL;
  170. }
  171. msg->setPrev(m_last);
  172. msg->setNext(NULL);
  173. m_last->setNext(msg);
  174. m_last = msg;
  175. m_lastMessageInserted = msg;
  176. return msg;
  177. }
  178. if (msg->getCommand()->getNetCommandType() < m_first->getCommand()->getNetCommandType()) {
  179. // Make sure this command isn't already in the list.
  180. if (isEqualCommandMsg(m_first->getCommand(), msg->getCommand())) {
  181. // This command is already in the list, don't duplicate it.
  182. msg->deleteInstance();
  183. msg = NULL;
  184. return NULL;
  185. }
  186. // The command goes at the head of the list.
  187. msg->setNext(m_first);
  188. msg->setPrev(NULL);
  189. m_first->setPrev(msg);
  190. m_first = msg;
  191. m_lastMessageInserted = msg;
  192. return msg;
  193. }
  194. // Find the start of the command type we're looking for.
  195. NetCommandRef *tempmsg = m_first;
  196. while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() > tempmsg->getCommand()->getNetCommandType())) {
  197. tempmsg = tempmsg->getNext();
  198. }
  199. if (tempmsg == NULL) {
  200. // Make sure this command isn't already in the list.
  201. if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
  202. // This command is already in the list, don't duplicate it.
  203. msg->deleteInstance();
  204. msg = NULL;
  205. return NULL;
  206. }
  207. // message goes at the end of the list.
  208. msg->setPrev(m_last);
  209. msg->setNext(NULL);
  210. m_last->setNext(msg);
  211. m_last = msg;
  212. m_lastMessageInserted = msg;
  213. return msg;
  214. }
  215. // Now find the player position. munkee.
  216. while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() == tempmsg->getCommand()->getNetCommandType()) && (msg->getCommand()->getPlayerID() > tempmsg->getCommand()->getPlayerID())) {
  217. tempmsg = tempmsg->getNext();
  218. }
  219. if (tempmsg == NULL) {
  220. // Make sure this command isn't already in the list.
  221. if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
  222. // This command is already in the list, don't duplicate it.
  223. msg->deleteInstance();
  224. msg = NULL;
  225. return NULL;
  226. }
  227. // message goes at the end of the list.
  228. msg->setPrev(m_last);
  229. msg->setNext(NULL);
  230. m_last->setNext(msg);
  231. m_last = msg;
  232. m_lastMessageInserted = msg;
  233. return msg;
  234. }
  235. // Find the position within the player's section based on the command ID.
  236. // If the command type doesn't require a command ID, sort by whatever it should be sorted by.
  237. while ((tempmsg != NULL) && (msg->getCommand()->getNetCommandType() == tempmsg->getCommand()->getNetCommandType()) && (msg->getCommand()->getPlayerID() == tempmsg->getCommand()->getPlayerID()) && (msg->getCommand()->getSortNumber() > tempmsg->getCommand()->getSortNumber())) {
  238. tempmsg = tempmsg->getNext();
  239. }
  240. if (tempmsg == NULL) {
  241. // Make sure this command isn't already in the list.
  242. if (isEqualCommandMsg(m_last->getCommand(), msg->getCommand())) {
  243. // This command is already in the list, don't duplicate it.
  244. msg->deleteInstance();
  245. msg = NULL;
  246. return NULL;
  247. }
  248. // This message goes at the end of the list.
  249. msg->setPrev(m_last);
  250. msg->setNext(NULL);
  251. m_last->setNext(msg);
  252. m_last = msg;
  253. m_lastMessageInserted = msg;
  254. return msg;
  255. }
  256. if (tempmsg == m_first) {
  257. // Make sure this command isn't already in the list.
  258. if (isEqualCommandMsg(m_first->getCommand(), msg->getCommand())) {
  259. // This command is already in the list, don't duplicate it.
  260. msg->deleteInstance();
  261. return NULL;
  262. }
  263. // This message goes at the head of the list.
  264. msg->setNext(m_first);
  265. msg->setPrev(NULL);
  266. m_first->setPrev(msg);
  267. m_first = msg;
  268. m_lastMessageInserted = msg;
  269. return msg;
  270. }
  271. // Make sure this command isn't already in the list.
  272. if (isEqualCommandMsg(tempmsg->getCommand(), msg->getCommand())) {
  273. // This command is already in the list, don't duplicate it.
  274. msg->deleteInstance();
  275. msg = NULL;
  276. return NULL;
  277. }
  278. // Insert message before tempmsg.
  279. msg->setNext(tempmsg);
  280. msg->setPrev(tempmsg->getPrev());
  281. msg->getPrev()->setNext(msg);
  282. tempmsg->setPrev(msg);
  283. m_lastMessageInserted = msg;
  284. return msg;
  285. }
  286. Int NetCommandList::length() {
  287. Int retval = 0;
  288. NetCommandRef *temp = m_first;
  289. while (temp != NULL) {
  290. ++retval;
  291. temp = temp->getNext();
  292. }
  293. return retval;
  294. }
  295. /**
  296. * This is really inefficient, but we can probably get away with it because
  297. * there shouldn't be too many messages for any given frame.
  298. */
  299. NetCommandRef * NetCommandList::findMessage(NetCommandMsg *msg) {
  300. NetCommandRef *retval = m_first;
  301. while ((retval != NULL) && (isEqualCommandMsg(retval->getCommand(), msg) == FALSE)) {
  302. retval = retval->getNext();
  303. }
  304. return retval;
  305. }
  306. NetCommandRef * NetCommandList::findMessage(UnsignedShort commandID, UnsignedByte playerID) {
  307. NetCommandRef *retval = m_first;
  308. while (retval != NULL) {
  309. if (DoesCommandRequireACommandID(retval->getCommand()->getNetCommandType())) {
  310. if ((retval->getCommand()->getID() == commandID) && (retval->getCommand()->getPlayerID() == playerID)) {
  311. return retval;
  312. }
  313. }
  314. retval = retval->getNext();
  315. }
  316. return retval;
  317. }
  318. Bool NetCommandList::isEqualCommandMsg(NetCommandMsg *msg1, NetCommandMsg *msg2) {
  319. if (DoesCommandRequireACommandID(msg1->getNetCommandType()) != DoesCommandRequireACommandID(msg2->getNetCommandType())) {
  320. return FALSE;
  321. }
  322. // At this point we know that the commands both do or do not require a command id.
  323. // Do or do not, there is no try.
  324. if (DoesCommandRequireACommandID(msg1->getNetCommandType())) {
  325. // Are the commands from the same player?
  326. if (msg1->getPlayerID() != msg2->getPlayerID()) {
  327. return FALSE;
  328. }
  329. // Do they have the same command ID?
  330. if (msg1->getID() != msg2->getID()) {
  331. return FALSE;
  332. }
  333. return TRUE;
  334. }
  335. // If we've gotten this far, we know that the commands do not require a command id.
  336. // So now our equality checking becomes type-specific.
  337. // Are they the same type?
  338. if (msg1->getNetCommandType() != msg2->getNetCommandType()) {
  339. return FALSE;
  340. }
  341. // Are they from the same player?
  342. if (msg1->getPlayerID() != msg2->getPlayerID()) {
  343. return FALSE;
  344. }
  345. // They are the same type and from the same player.
  346. // Time for the type specific stuff.
  347. if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE1) {
  348. NetAckStage1CommandMsg *ack1 = (NetAckStage1CommandMsg *)msg1;
  349. NetAckStage1CommandMsg *ack2 = (NetAckStage1CommandMsg *)msg2;
  350. if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
  351. return FALSE;
  352. }
  353. if (ack1->getCommandID() != ack2->getCommandID()) {
  354. return FALSE;
  355. }
  356. return TRUE;
  357. }
  358. // They are the same type and from the same player.
  359. // Time for the type specific stuff.
  360. if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKSTAGE2) {
  361. NetAckStage2CommandMsg *ack1 = (NetAckStage2CommandMsg *)msg1;
  362. NetAckStage2CommandMsg *ack2 = (NetAckStage2CommandMsg *)msg2;
  363. if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
  364. return FALSE;
  365. }
  366. if (ack1->getCommandID() != ack2->getCommandID()) {
  367. return FALSE;
  368. }
  369. return TRUE;
  370. }
  371. // They are the same type and from the same player.
  372. // Time for the type specific stuff.
  373. if (msg1->getNetCommandType() == NETCOMMANDTYPE_ACKBOTH) {
  374. NetAckBothCommandMsg *ack1 = (NetAckBothCommandMsg *)msg1;
  375. NetAckBothCommandMsg *ack2 = (NetAckBothCommandMsg *)msg2;
  376. if (ack1->getOriginalPlayerID() != ack2->getOriginalPlayerID()) {
  377. return FALSE;
  378. }
  379. if (ack1->getCommandID() != ack2->getCommandID()) {
  380. return FALSE;
  381. }
  382. return TRUE;
  383. }
  384. return FALSE;
  385. }