SEQCONN.CPP (24567B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: F:\projects\c&c\vcs\code\seqconn.cpv 1.9 16 Oct 1995 16:51:30 JOE_BOSTIC $ */ 17 /*************************************************************************** 18 ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** 19 *************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : SEQCONN.CPP * 24 * * 25 * Programmer : Bill Randolph * 26 * * 27 * Start Date : December 20, 1994 * 28 * * 29 * Last Update : April 9, 1995 [BRR] * 30 * * 31 *-------------------------------------------------------------------------* 32 * Functions: * 33 * SequencedConnClass::SequencedConnClass -- class constructor * 34 * SequencedConnClass::~SequencedConnClass -- class destructor * 35 * SequencedConnClass::Init -- Initializes connection queue to empty * 36 * SequencedConnClass::Send_Packet -- adds a packet to the send queue * 37 * SequencedConnClass::Receive_Packet -- adds packet to receive queue * 38 * SequencedConnClass::Get_Packet -- gets a packet from receive queue * 39 * SequencedConnClass::Service_Send_Queue -- services the send queue * 40 * SequencedConnClass::Service_Receive_Queue -- services recieve queue * 41 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 42 43 #include "function.h" 44 45 // PG_TO_FIX 46 #if (0) 47 48 /*************************************************************************** 49 * SequencedConnClass::SequencedConnClass -- class constructor * 50 * * 51 * INPUT: * 52 * numsend desired # of entries for the send queue * 53 * numreceive desired # of entries for the recieve queue * 54 * maxlen max length of an application packet * 55 * magicnum the packet "magic number" for this connection * 56 * retry_delta the time to wait between sends * 57 * max_retries the max # of retries allowed for a packet * 58 * (-1 means retry forever, based on this parameter) * 59 * timeout the max amount of time before we give up on a packet * 60 * (-1 means retry forever, based on this parameter) * 61 * * 62 * OUTPUT: * 63 * none. * 64 * * 65 * WARNINGS: * 66 * none. * 67 * * 68 * HISTORY: * 69 * 12/20/1994 BR : Created. * 70 *=========================================================================*/ 71 SequencedConnClass::SequencedConnClass (int numsend, int numreceive, 72 int maxlen, unsigned short magicnum, unsigned long retry_delta, 73 unsigned long max_retries, unsigned long timeout) : 74 ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) 75 { 76 NumRecNoAck = 0; 77 NumRecAck = 0; 78 NumSendNoAck = 0; 79 NumSendAck = 0; 80 81 /*------------------------------------------------------------------------ 82 Allocate the packet Queue. This will store incoming packets (which will 83 be placed there by the Connection Manager), and outgoing packets (which 84 are placed there by this class when it "sends" a packet). 85 ------------------------------------------------------------------------*/ 86 Queue = new CommQueueClass (numsend, numreceive, MaxPacketLen); 87 88 } /* end of SequencedConnClass */ 89 90 91 /*************************************************************************** 92 * SequencedConnClass::~SequencedConnClass -- class destructor * 93 * * 94 * INPUT: * 95 * none. * 96 * * 97 * OUTPUT: * 98 * none. * 99 * * 100 * WARNINGS: * 101 * none. * 102 * * 103 * HISTORY: * 104 * 12/20/1994 BR : Created. * 105 *=========================================================================*/ 106 SequencedConnClass::~SequencedConnClass () 107 { 108 delete Queue; 109 } 110 111 112 /*************************************************************************** 113 * SequencedConnClass::Init -- Initializes connection queue to empty * 114 * * 115 * INPUT: * 116 * none. * 117 * * 118 * OUTPUT: * 119 * none. * 120 * * 121 * WARNINGS: * 122 * none. * 123 * * 124 * HISTORY: * 125 * 12/20/1994 BR : Created. * 126 *=========================================================================*/ 127 void SequencedConnClass::Init (void) 128 { 129 Queue->Init(); 130 131 } /* end of Init */ 132 133 134 /*************************************************************************** 135 * SequencedConnClass::Send_Packet -- adds a packet to the send queue * 136 * * 137 * This routine prefixes the given buffer with a CommHeaderType and * 138 * queues the resulting packet into the Send Queue. (It's actually the * 139 * Service() routine that handles the hardware-dependent Send of the data).* 140 * The packet's MagicNumber, Code, and PacketID are set here. * 141 * * 142 * INPUT: * 143 * buf buffer to send * 144 * buflen length of buffer * 145 * ack_req true = ACK is required for this packet; false = isn't * 146 * * 147 * OUTPUT: * 148 * 1 = packet was queue'd OK, 0 = wasn't * 149 * * 150 * WARNINGS: * 151 * none. * 152 * * 153 * HISTORY: * 154 * 12/20/1994 BR : Created. * 155 *=========================================================================*/ 156 int SequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) 157 { 158 /*........................................................................ 159 Set the magic # for the packet 160 ........................................................................*/ 161 ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; 162 163 /*........................................................................ 164 Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't 165 Set the packet ID to the appropriate counter value. 166 ........................................................................*/ 167 if (ack_req) { 168 ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; 169 ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; 170 } else { 171 ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; 172 ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; 173 } 174 175 /*........................................................................ 176 Now build the packet 177 ........................................................................*/ 178 memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); 179 180 /*........................................................................ 181 Add it to the queue. 182 ........................................................................*/ 183 if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { 184 if (ack_req) { 185 NumSendAck++; 186 } else { 187 NumSendNoAck++; 188 } 189 return(true); 190 191 } else { 192 return(false); 193 } 194 } 195 196 197 /*************************************************************************** 198 * SequencedConnClass::Receive_Packet -- adds packet to the receive queue * 199 * * 200 * INPUT: * 201 * buf buffer to process (already includes CommHeaderType) * 202 * buflen length of buffer to process * 203 * * 204 * OUTPUT: * 205 * 1 = packet was processed OK, 0 = error * 206 * * 207 * WARNINGS: * 208 * none. * 209 * * 210 * HISTORY: * 211 * 12/20/1994 BR : Created. * 212 *=========================================================================*/ 213 int SequencedConnClass::Receive_Packet (void * buf, int buflen) 214 { 215 CommHeaderType *packet; // ptr to this packet 216 SendQueueType *send_entry; // ptr to send entry header 217 ReceiveQueueType *rec_entry; // ptr to receive entry header 218 CommHeaderType *entry_data; // ptr to queue entry data 219 int save_packet = 1; // 0 = this is a resend, or 220 // out-of-order packet 221 222 /* 223 --------------------------- Check the magic # ---------------------------- 224 */ 225 packet = (CommHeaderType *)buf; 226 if (packet->MagicNumber!=MagicNum) 227 return(false); 228 229 /*------------------------------------------------------------------------ 230 Process the packet based on its Code 231 ------------------------------------------------------------------------*/ 232 switch (packet->Code) { 233 /*..................................................................... 234 DATA: If this is a No-Ack packet, always save it. Otherwise, only 235 save it if it's received in the proper sequence. 236 .....................................................................*/ 237 case PACKET_DATA_ACK: 238 case PACKET_DATA_NOACK: 239 if (packet->Code == PACKET_DATA_NOACK) { 240 #ifdef DEBUG_SEQ 241 printf("PACKET_DATA_NOACK received (%d)\n",packet->PacketID); 242 #endif 243 save_packet = 1; 244 } else { 245 #ifdef DEBUG_SEQ 246 printf("PACKET_DATA_ACK received (%d)\n",packet->PacketID); 247 #endif 248 if ((packet->PacketID == NumRecAck)) { 249 save_packet = 1; 250 } else { 251 save_packet = 0; 252 /*............................................................... 253 If this is a resend of our next-available received message, it 254 means the other app didn't get our ACK, so mark it as 255 non-acknowledged to tell Service_Receive_Queue to send an ACK. 256 ...............................................................*/ 257 rec_entry = Queue->Next_Receive(); 258 if (rec_entry) { 259 entry_data = (CommHeaderType *)rec_entry->Buffer; 260 if (entry_data->PacketID==packet->PacketID && 261 entry_data->Code == PACKET_DATA_ACK) { 262 rec_entry->IsACK = 0; 263 #ifdef DEBUG_SEQ 264 printf("(Resend)\n"); 265 #endif 266 } 267 } 268 } 269 } 270 271 /* 272 ...................... queue this packet ........................ 273 */ 274 if (save_packet) { 275 if (!Queue->Queue_Receive(buf, buflen)) { 276 return(false); 277 } 278 if (packet->Code == PACKET_DATA_ACK) { 279 NumRecAck++; 280 } else { 281 NumRecNoAck++; 282 } 283 } 284 break; 285 286 /*..................................................................... 287 ACK: If this ACK is for the latest-sent packet, mark that packet as 288 acknowledged, then throw this packet away. Otherwise, ignore the ACK 289 (if we re-sent before we received the other system's first ACK, this 290 ACK will be a leftover) 291 .....................................................................*/ 292 case PACKET_ACK: 293 #ifdef DEBUG_SEQ 294 printf("ACK received (%d)\n",packet->PacketID); 295 #endif 296 /* 297 ....................... Get queue entry ptr ........................ 298 */ 299 send_entry = Queue->Next_Send(); 300 /* 301 ............... If ptr is valid, get ptr to its data ............... 302 */ 303 if (send_entry!=NULL) { 304 entry_data = (CommHeaderType *)send_entry->Buffer; 305 306 /* 307 .............. If ACK is for this entry, mark it ................ 308 */ 309 if (packet->PacketID==entry_data->PacketID && 310 entry_data->Code == PACKET_DATA_ACK) { 311 send_entry->IsACK = 1; 312 } 313 } 314 break; 315 316 /*..................................................................... 317 Default: ignore the packet 318 .....................................................................*/ 319 default: 320 break; 321 322 } /* end of switch */ 323 324 return(true); 325 } 326 327 328 /*************************************************************************** 329 * SequencedConnClass::Get_Packet -- gets a packet from the receive queue * 330 * * 331 * INPUT: * 332 * buf location to store buffer * 333 * buflen filled in with length of 'buf' * 334 * * 335 * OUTPUT: * 336 * 1 = packet was read, 0 = wasn't * 337 * * 338 * WARNINGS: * 339 * none. * 340 * * 341 * HISTORY: * 342 * 12/20/1994 BR : Created. * 343 *=========================================================================*/ 344 int SequencedConnClass::Get_Packet (void * buf, int *buflen) 345 { 346 ReceiveQueueType *rec_entry; // ptr to receive entry header 347 int packetlen; // size of received packet 348 349 /* 350 ------------------ Get ptr to the next available entry ------------------- 351 */ 352 rec_entry = Queue->Next_Receive(); 353 354 /* 355 ------------------------ Read it if it's un-read ------------------------- 356 */ 357 if (rec_entry!=NULL && rec_entry->IsRead==0) { 358 /* 359 ........................... Mark as read .............................. 360 */ 361 rec_entry->IsRead = 1; 362 363 /* 364 .......................... Copy data packet ........................... 365 */ 366 packetlen = rec_entry->BufLen - sizeof(CommHeaderType); 367 if (packetlen > 0) { 368 memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); 369 } 370 (*buflen) = packetlen; 371 return(true); 372 } 373 374 return(false); 375 } 376 377 378 /*************************************************************************** 379 * SequencedConnClass::Service_Send_Queue -- services the send queue * 380 * * 381 * INPUT: * 382 * none. * 383 * * 384 * OUTPUT: * 385 * 1 = OK, 0 = error * 386 * * 387 * WARNINGS: * 388 * none. * 389 * * 390 * HISTORY: * 391 * 12/20/1994 BR : Created. * 392 *=========================================================================*/ 393 int SequencedConnClass::Service_Send_Queue (void) 394 { 395 SendQueueType *send_entry; // ptr to send queue entry 396 CommHeaderType *packet_hdr; // packet header 397 unsigned long curtime; // current time 398 399 /*------------------------------------------------------------------------ 400 - If the next packet is ACK'd remove it from the queue 401 - If the next packet isn't ACK'd, [re-]send it 402 ------------------------------------------------------------------------*/ 403 /* 404 ......................... Get ptr to data to send ........................ 405 */ 406 send_entry = Queue->Next_Send(); 407 if (send_entry==NULL) 408 return(true); 409 410 /* 411 ------------------ If ACK has been received, unqueue it ------------------ 412 */ 413 if (send_entry->IsACK) { 414 415 /* 416 .................. Update this queue's response time .................. 417 */ 418 packet_hdr = (CommHeaderType *)send_entry->Buffer; 419 if (packet_hdr->Code == PACKET_DATA_ACK) { 420 Queue->Add_Delay(Time() - send_entry->FirstTime); 421 } 422 423 /* 424 ......................... unqueue the packet .......................... 425 */ 426 #ifdef DEBUG_SEQ 427 printf(">>>Unqueueing Receive packet #%d<<<\n",packet_hdr->PacketID); 428 #endif 429 Queue->UnQueue_Send(NULL,NULL); 430 } else { 431 432 /* 433 ----------------- ACK not received yet, [re-]send packet ----------------- 434 */ 435 /*..................................................................... 436 Only send the message if time has elapsed. (The message's Time 437 fields are init'd to 0 when a message is queue'd or unqueue'd, so the 438 first time through, the delta time will appear large.) 439 .....................................................................*/ 440 curtime = Time(); 441 if (curtime - send_entry->LastTime > RetryDelta) { 442 /* 443 ......................... Send the message ......................... 444 */ 445 Send (send_entry->Buffer, send_entry->BufLen); 446 /* 447 ....................... Fill in Time fields ........................ 448 */ 449 send_entry->LastTime = curtime; 450 if (send_entry->SendCount==0) { 451 send_entry->FirstTime = curtime; 452 /*............................................................... 453 If this is the 1st time we're sending this packet, and it doesn't 454 require an ACK, mark it as ACK'd; then, the next time through, 455 it will just be removed from the queue. 456 ...............................................................*/ 457 packet_hdr = (CommHeaderType *)send_entry->Buffer; 458 if (packet_hdr->Code == PACKET_DATA_NOACK) 459 send_entry->IsACK = 1; 460 } 461 462 #ifdef DEBUG_SEQ 463 packet_hdr = (CommHeaderType *)send_entry->Buffer; 464 if (packet_hdr->Code == PACKET_DATA_NOACK) { 465 printf("Sending PACKET_DATA_NOACK (%d)\n",packet_hdr->PacketID); 466 } else { 467 printf("Sending PACKET_DATA_ACK (%d)\n",packet_hdr->PacketID); 468 } 469 #endif 470 /* 471 ......................... Update SendCount ......................... 472 */ 473 send_entry->SendCount++; 474 /*.................................................................. 475 Perform error detection, based on either MaxRetries or Timeout 476 ..................................................................*/ 477 if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) 478 return(false); 479 if (Timeout != -1 && send_entry->LastTime - 480 send_entry->FirstTime > Timeout) 481 return(false); 482 483 } 484 } 485 486 return(true); 487 } 488 489 490 /*************************************************************************** 491 * SequencedConnClass::Service_Receive_Queue -- services the recieve queue * 492 * * 493 * INPUT: * 494 * none. * 495 * * 496 * OUTPUT: * 497 * 1 = OK, 0 = error * 498 * * 499 * WARNINGS: * 500 * none. * 501 * * 502 * HISTORY: * 503 * 12/20/1994 BR : Created. * 504 *=========================================================================*/ 505 int SequencedConnClass::Service_Receive_Queue (void) 506 { 507 CommHeaderType ackpacket; // ACK packet to send 508 ReceiveQueueType *rec_entry; // ptr to receive entry header 509 CommHeaderType *packet_hdr; // packet header 510 511 /*------------------------------------------------------------------------ 512 Get a pointer to the next received entry 513 ------------------------------------------------------------------------*/ 514 rec_entry = Queue->Next_Receive(); 515 if (rec_entry==NULL) 516 return(true); 517 518 /*------------------------------------------------------------------------ 519 If this packet doesn't require an ACK, mark it as ACK'd. 520 ------------------------------------------------------------------------*/ 521 packet_hdr = (CommHeaderType *)(rec_entry->Buffer); 522 if (packet_hdr->Code==PACKET_DATA_NOACK) 523 rec_entry->IsACK = 1; 524 525 /*------------------------------------------------------------------------ 526 If this packet hasn't been ACK'd, send an ACK: 527 - Fill in the MagicNum & the Code 528 - Set the PacketID to the same ID that the sending system used, so the 529 sending system knows which packet the ACK is for 530 ------------------------------------------------------------------------*/ 531 if (rec_entry->IsACK==0) { 532 #ifdef DEBUG_SEQ 533 printf("Sending ACK (%d)\n",packet_hdr->PacketID); 534 #endif 535 ackpacket.MagicNumber = MagicNum; 536 ackpacket.Code = PACKET_ACK; 537 ackpacket.PacketID = packet_hdr->PacketID; 538 539 Send((char *)&ackpacket, sizeof(CommHeaderType)); 540 541 rec_entry->IsACK = 1; 542 } 543 544 /*------------------------------------------------------------------------ 545 If this packet has been read by the application, and has been ACK'd, and 546 there is another packet in the queue behind this one, it means the other 547 system got the ACK we sent for this packet; remove this packet from the 548 queue. 549 ------------------------------------------------------------------------*/ 550 if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && 551 Queue->Num_Receive() > 1) { 552 #ifdef DEBUG_SEQ 553 printf(">>>Unqueueing Send packet #%d<<<\n",packet_hdr->PacketID); 554 #endif 555 Queue->UnQueue_Receive(NULL,NULL); 556 } 557 558 return(true); 559 } 560 561 #endif