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