/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : Command & Conquer * * * * $Archive:: /Commando/Code/wwnet/packetmgr.cpp $* * * * $Author:: Steve_t $* * * * $Modtime:: 8/08/02 3:26p $* * * * $Revision:: 36 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "packetmgr.h" #include #include //#include #include "systimer.h" #include #include "netutil.h" #include "wwmemlog.h" #include "crc.h" #include "wwprofile.h" #include "connect.h" /* ** Single instance of PacketManagerClass. */ PacketManagerClass PacketManager; /*********************************************************************************************** * PacketManagerClass::Add_Bit -- Add a bit to a delta compressed packet stream * * * * * * * * INPUT: Bit to add as a bool - true or false * * Ptr to bitstream * * Bit position at pointer * * * * OUTPUT: New bit position * * * * WARNINGS: None * * * * HISTORY: * * 9/24/2001 1:34PM ST : Created * *=============================================================================================*/ inline int PacketManagerClass::Add_Bit(bool bit, unsigned char * &bitstream, int &position) { pm_assert(position < 8); unsigned char whole = bit ? 1 : 0; whole <<= position; *bitstream |= whole; position++; if (position == 8) { bitstream++; *bitstream = 0; position = 0; } return(position); } /*********************************************************************************************** * PacketManagerClass::Get_Bit -- Get a bit from a delta compressed bitstream * * * * * * * * INPUT: Ptr to bitstream * * Bit position * * * * OUTPUT: 0 or 1 * * * * WARNINGS: None * * * * HISTORY: * * 9/24/2001 1:35PM ST : Created * *=============================================================================================*/ inline unsigned char PacketManagerClass::Get_Bit(unsigned char * &bitstream, int &position) { pm_assert(position < 8); unsigned char whole = *bitstream; whole >>= position; whole &= 1; position++; if (position == 8) { bitstream++; position = 0; } return(whole); } /*********************************************************************************************** * PacketManagerClass::PacketManagerClass -- Class constructor. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/24/2001 1:36PM ST : Created * *=============================================================================================*/ PacketManagerClass::PacketManagerClass(void) { BandwidthList.Set_Growth_Step(128); //memset(PacketLengths, 0, sizeof(PacketLengths)); NextPacket = 0; NumPackets = 0; NumReceivePackets = 0; CurrentPacket = 0; LastSendTime = 0; FlushFrequency = 1000 / 10; // Default = 10 times per second. AllowDeltas = true; AllowCombos = true; StatsFrequency = 10 * 1000; LastStatsUpdate = TIMEGETTIME(); ErrorState = STATE_OK; ResetStatsIn = true; ResetStatsOut = true; NumSendBuffers = PACKET_MANAGER_BUFFERS; SendBuffers = new SendBufferClass[NumSendBuffers]; NumReceiveBuffers = PACKET_MANAGER_RECEIVE_BUFFERS; ReceiveBuffers = new ReceiveBufferClass[NumReceiveBuffers]; } /*********************************************************************************************** * PacketManagerClass::~PacketManagerClass -- Class destructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/15/2001 11:24AM ST : Created * *=============================================================================================*/ PacketManagerClass::~PacketManagerClass(void) { if (SendBuffers) { delete [] SendBuffers; SendBuffers = NULL; } if (ReceiveBuffers) { delete [] ReceiveBuffers; ReceiveBuffers = NULL; } } /*********************************************************************************************** * PacketManagerClass::Set_Is_Server -- Set whether to operate in server mode or not. * * * * * * * * INPUT: is_server - true if we are to be a server * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/15/2001 11:29AM ST : Created * *=============================================================================================*/ void PacketManagerClass::Set_Is_Server(bool is_server) { CriticalSectionClass::LockClass lock(CriticalSection); WWMEMLOG(MEM_NETWORK); bool reset = false; if (is_server && NumSendBuffers != PACKET_MANAGER_BUFFERS_WHEN_SERVER) { NumSendBuffers = PACKET_MANAGER_BUFFERS_WHEN_SERVER; NumReceiveBuffers = PACKET_MANAGER_RECEIVE_BUFFERS_AS_SERVER; reset = true; } else { if (!is_server && NumSendBuffers != PACKET_MANAGER_BUFFERS) { NumSendBuffers = PACKET_MANAGER_BUFFERS; NumReceiveBuffers = PACKET_MANAGER_RECEIVE_BUFFERS; reset = true; } } if (reset) { NextPacket = 0; NumPackets = 0; NumReceivePackets = 0; CurrentPacket = 0; if (SendBuffers) { delete [] SendBuffers; } SendBuffers = new SendBufferClass[NumSendBuffers]; if (ReceiveBuffers) { delete [] ReceiveBuffers; } ReceiveBuffers = new ReceiveBufferClass[NumReceiveBuffers]; Reset_Stats(); } } /*********************************************************************************************** * PacketManagerClass::Build_Delta_Packet_Patch -- Calc a delta between two packets * * * * * * * * INPUT: Ptr to base packet * * Ptr to packet to compare with base packet * * Ptr to delta patch that describes differences between base and add packets * * Size of base packet (packet sizes must match) * * Size of add packet (must match base packets size) * * * * OUTPUT: Size of delta patch * * * * WARNINGS: None * * * * HISTORY: * * 9/24/2001 1:36PM ST : Created * *=============================================================================================*/ int PacketManagerClass::Build_Delta_Packet_Patch(unsigned char *base_packet, unsigned char *add_packet, unsigned char *delta_packet, int base_packet_size, int add_packet_size) { /* ** Here's how this works. ** ** The source and delta packets are compared 8 bytes at a time and am equality bit is set in the output stream ** for each block of 8. Then, for each non-matching block, a per byte bit is set in the output stream to indicate ** whether a byte can be used from the base packet or if a patch byte is needed. Finally, patch bytes are added to ** the stream at the end starting at a byte boundry. ** */ /* ** Locals. */ int write_bit_pos = 0; int read_bit_pos = 0; int num_diff_bytes = 0; unsigned char diff_bytes[1024]; /* ** Parameter asserts. */ pm_assert(base_packet != NULL); pm_assert(add_packet != NULL); pm_assert(delta_packet != NULL); pm_assert(base_packet_size == add_packet_size); pm_assert(base_packet_size < sizeof(diff_bytes)); /* ** dereference pointers to the packet header and the packet build pointer. */ PacketDeltaHeaderStruct *header = (PacketDeltaHeaderStruct*) delta_packet; unsigned char *build_delta_ptr = delta_packet + sizeof(PacketDeltaHeaderStruct); unsigned char *chunk_ptr = build_delta_ptr; pm_assert(sizeof(PacketDeltaHeaderStruct) == 1); *build_delta_ptr = 0; header->BytePack = 0; if (base_packet_size == add_packet_size) { /* ** Break it up into 8 byte chunks and see whether any of the chunks are the same. */ bool chunks = false; bool this_match = false; for (int i=0 ; iChunkPack = chunks ? 1 : 0; /* ** Lost the useless chunk info if none of the chunks matched anyway. */ if (!chunks) { build_delta_ptr = chunk_ptr; *build_delta_ptr = 0; write_bit_pos = 0; } /* ** Try a byte for byte check in the chunks that don't match. For any bytes that are different, save the changed value ** so we can add it to the end of the delta packet later. */ read_bit_pos = 0; num_diff_bytes = 0; for (i=0 ; iBytePack = 1; this_match = (base_packet[j] == add_packet[j]) ? true : false; write_bit_pos = Add_Bit(this_match, build_delta_ptr, write_bit_pos); if (!this_match) { diff_bytes[num_diff_bytes++] = add_packet[j]; } } } /* ** OK, now we have bitfields that mark the differences. A 1 in the bitfield means that the corresponding chunk or byte ** in the add packet is the same as in the base packet. ** Throw the non-matching bytes onto the end of the bitfield and we have a complete delta patch. */ unsigned char *delta_bytes_ptr = build_delta_ptr; if (write_bit_pos != 0) { delta_bytes_ptr++; } memcpy(delta_bytes_ptr, diff_bytes, num_diff_bytes); /* ** Calculate the size of the whole delta patch. */ int patch_size = (delta_bytes_ptr - delta_packet) + num_diff_bytes; return(patch_size); } return(-1); } /*********************************************************************************************** * PacketManagerClass::Reconstruct_From_Delta -- Rebuild a packet from a delta stream * * * * * * * * INPUT: Base packet to copy same bytes from * * Buffer to store reconstructed packet into * * Ptr to delta packet stream. * * Size of base packet * * (out) bytes extracted from delta stream * * * * OUTPUT: Size of restored packet (should match size of base packet) * * * * WARNINGS: None * * * * HISTORY: * * 9/26/2001 2:14PM ST : Created * *=============================================================================================*/ int PacketManagerClass::Reconstruct_From_Delta(unsigned char *base_packet, unsigned char *reconstructed_packet, unsigned char *delta_packet, int base_packet_size, int &delta_size) { if (base_packet == NULL) { WWDEBUG_SAY(("*** WARNING: MALFORMED PACKET - PacketManagerClass::Reconstruct_From_Delta -- Bad base packet\n")); return(0); } if (reconstructed_packet == NULL) { WWDEBUG_SAY(("*** WARNING: MALFORMED PACKET - PacketManagerClass::Reconstruct_From_Delta -- Bad reconstructed packet\n")); return(0); } if (delta_packet == NULL) { WWDEBUG_SAY(("*** WARNING: MALFORMED PACKET - PacketManagerClass::Reconstruct_From_Delta -- Bad delta packet\n")); return(0); } if (base_packet_size > 500) { WWDEBUG_SAY(("*** WARNING: MALFORMED PACKET - PacketManagerClass::Reconstruct_From_Delta -- Bad base packet size\n")); return(0); } pm_assert(base_packet != NULL); pm_assert(reconstructed_packet != NULL); pm_assert(delta_packet != NULL); int patch_list[1024]; int num_patches = 0; int read_bit_pos = 0; int read_chunk_pos = 0; int restored_bytes = 0; PacketDeltaHeaderStruct *header = (PacketDeltaHeaderStruct*) delta_packet; unsigned char *read_delta_ptr = delta_packet + sizeof(PacketDeltaHeaderStruct); pm_assert(sizeof(PacketDeltaHeaderStruct) == 1); unsigned char *chunk_ptr = read_delta_ptr; /* ** If there is chunk info then extract that first. Use it to copy the approprate parts of the base packet into the add packet. */ if (header->ChunkPack) { for (int i=0 ; iChunkPack) { if (Get_Bit(chunk_ptr, read_chunk_pos)) { continue; } } /* ** If this chunk didn't match then go through byte by byte and note differences. */ for (int j=i ; j= NumSendBuffers) { NextPacket = 0; } if (SendBuffers[NextPacket].PacketLength == 0) { return_index = NextPacket; break; } } return(return_index); } /*********************************************************************************************** * PacketManagerClass::Take_Packet -- Intercept a packet before it's sent * * * * * * * * INPUT: Ptr to packet * * Length of packet * * IP of packet recipient * * Port of packet recipient * * * * OUTPUT: True if packet was accepted * * * * WARNINGS: None * * * * HISTORY: * * 9/18/2001 4:24PM ST : Created * *=============================================================================================*/ bool PacketManagerClass::Take_Packet(unsigned char *packet, int packet_len, unsigned char *dest_ip, unsigned short dest_port, SOCKET source_socket) { CriticalSectionClass::LockClass lock(CriticalSection); if (NumPackets < NumSendBuffers) { if (packet_len > 0 && packet_len < (PACKET_MANAGER_MTU - sizeof(PacketPackHeaderStruct))) { int index = Get_Next_Free_Buffer_Index(); if (index != -1) { memcpy(SendBuffers[index].PacketBuffer, packet, packet_len); SendBuffers[index].PacketLength = packet_len; SendBuffers[index].Port = dest_port; memcpy(&SendBuffers[index].IPAddress[0], dest_ip, 4); SendBuffers[index].PacketSendSocket = source_socket; NumPackets++; //WWDEBUG_SAY(("NumPackets = %d (added packet at index %d)\n", NumPackets, index)); if (NumPackets > NumSendBuffers - 4) { WWDEBUG_SAY(("***WARNING*** Outgoing packet buffer full - NumPackets = %d\n", NumPackets, index)); Flush(true); WWDEBUG_SAY(("NumPackets = %d after flush\n", NumPackets, index)); } Register_Packet_Out(dest_ip, dest_port, 0, packet_len + UDP_HEADER_SIZE); return(true); } } } return(false); } /*********************************************************************************************** * PacketManagerClass::Flush -- Coalesce and send any pending packets * * * * * * * * INPUT: Forced - set to true if packets should be sent even if it's not time * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/26/2001 2:19PM ST : Created * *=============================================================================================*/ void PacketManagerClass::Flush(bool forced) { { WWPROFILE("PMgr Flush"); CriticalSectionClass::LockClass lock(CriticalSection); int base_index = -1; int length = 0; unsigned char *base_packet = NULL; int new_length = 0; PacketPackHeaderStruct *header = (PacketPackHeaderStruct*) BuildPacket; unsigned char *next_packet_pos = NULL; int index = 0; int i; SOCKET socket = INVALID_SOCKET; /* ** If it's not time to send packets yet then just return. */ unsigned long time = TIMEGETTIME(); if (!forced && FlushFrequency != 0) { if (time - LastSendTime < FlushFrequency) { return; } } LastSendTime = time; //WWDEBUG_SAY(("NumPackets = %d\n", NumPackets)); /* ** Clear the array that indicates whether a packet is ready to be sent. */ for (i=0 ; i= NumSendBuffers) { index = 0; } base_index = -1; for (i=0 ; iNumPackets = 1; header->PacketSize = length; header->MorePackets = 0; pm_assert(header->NumPackets > 0 && header->NumPackets <= PACKET_MANAGER_MAX_PACKETS); next_packet_pos = BuildPacket + sizeof(*header); memcpy(next_packet_pos, base_packet, length); next_packet_pos += length; new_length = length + sizeof(*header); //WWDEBUG_SAY(("Added base packet %d to metapacket - %d (+2) bytes\n", index, length)); index++; if (index >= NumSendBuffers) { index = 0; } break; } index++; if (index >= NumSendBuffers) { index = 0; } } /* ** If we found a packet, see if there are more packets of the same size that we can package up with it. */ if (base_index != -1 && NumPackets > 1) { if ((new_length + length + sizeof(PacketDeltaHeaderStruct)) < PACKET_MANAGER_MTU) { for (int j=i ; jNumPackets == PACKET_MANAGER_MAX_PACKETS) { break; } /* ** Is this packet the same length? If so, it might be suitable for coagulation. */ if (SendBuffers[index].PacketLength == length && SendBuffers[index].PacketSendSocket == socket) { pm_assert(index != base_index); /* ** Is this packet for the same recipient? */ if (SendBuffers[base_index].Port == SendBuffers[index].Port && (*(unsigned long*)(&SendBuffers[index].IPAddress[0])) == (*(unsigned long*)(&SendBuffers[base_index].IPAddress[0]))) { //WWDEBUG_SAY(("Found secondary packet %d\n", index)); /* ** See if using a delta of the two packets would be smaller than including the whole packet. */ int bytes = Build_Delta_Packet_Patch(base_packet, SendBuffers[index].PacketBuffer->Buffer, DeltaPacket, length, length); if (bytes < length && AllowDeltas) { /* ** Yes, the delta is smaller so use that. */ memcpy(next_packet_pos, DeltaPacket, bytes); next_packet_pos += bytes; new_length += bytes; //WWDEBUG_SAY(("Added delta packet %d to metapacket - %d bytes\n", index, bytes)); header->NumPackets++; pm_assert(header->NumPackets > 0 && header->NumPackets <= PACKET_MANAGER_MAX_PACKETS); } else { /* ** The delta was no better than the original. Stick a 0 byte in to say it's not a delta and then ** Copy the whole thing. */ PacketDeltaHeaderStruct *delta_header = (PacketDeltaHeaderStruct*) next_packet_pos; delta_header->ChunkPack = 0; delta_header->BytePack = 0; next_packet_pos += sizeof(PacketDeltaHeaderStruct); new_length += sizeof(PacketDeltaHeaderStruct); memcpy(next_packet_pos, SendBuffers[index].PacketBuffer, length); next_packet_pos += length; new_length += length; //WWDEBUG_SAY(("Added secondary packet %d to metapacket - %d (+1) bytes\n", index, length)); header->NumPackets++; pm_assert(header->NumPackets > 0 && header->NumPackets <= PACKET_MANAGER_MAX_PACKETS); } /* ** Discard the packet buffer now that we have added this packet to the current staging buffer area. */ SendBuffers[index].PacketLength = 0; NumPackets--; //WWDEBUG_SAY(("NumPackets = %d (discarding secondary packet index %d)\n", NumPackets, index)); pm_assert(header->NumPackets > 0 && header->NumPackets <= PACKET_MANAGER_MAX_PACKETS); /* ** If there's no room for any more of these packets then bail out. */ if ((new_length + length + sizeof(PacketDeltaHeaderStruct)) >= PACKET_MANAGER_MTU) { break; } } } index++; if (index >= NumSendBuffers) { index = 0; } } } } pm_assert(header->NumPackets > 0 && header->NumPackets <= PACKET_MANAGER_MAX_PACKETS); /* ** Allright, it's time to send this sucker. */ if (base_index != -1) { /* ** Just copy it back into the base index for now and mark it as ready to go. */ memcpy(SendBuffers[base_index].PacketBuffer, BuildPacket, new_length); SendBuffers[base_index].PacketReady = true; SendBuffers[base_index].PacketSendLength = new_length; SendBuffers[base_index].PacketLength = 0; NumPackets--; //WWDEBUG_SAY(("NumPackets = %d (discarding base packet %d)\n", NumPackets, base_index)); pm_assert(NumPackets >= 0); } pm_assert(header->NumPackets > 0 && header->NumPackets <= PACKET_MANAGER_MAX_PACKETS); } /* ** Merge ready buffers where possible. This is the step that adds runs of different sizeed packets into the same packet. */ if (AllowCombos) { for (i=0 ; iBuffer[current_len]; memcpy(dest_ptr, SendBuffers[j].PacketBuffer, SendBuffers[j].PacketSendLength); current_header->MorePackets = 1; current_header = (PacketPackHeaderStruct*) dest_ptr; current_len += SendBuffers[j].PacketSendLength; SendBuffers[i].PacketSendLength = current_len; SendBuffers[j].PacketReady = false; #ifdef WWDEBUG num_sub_packets++; #endif //WWDEBUG //WWDEBUG_SAY(("Added packet %d with %d sub packets to packet %d\n", j, ((PacketPackHeaderStruct*)&PacketBuffers[j][0])->NumPackets, i)); //WWDEBUG_SAY(("Packet %d has %d packets total\n", i, num_sub_packets)); } } } } } } } /* ** Send any packets marked as ready. */ for (i=0 ; iNumPackets); int debug_packet_size = (int)(((PacketPackHeaderStruct*)SendBuffers[i].PacketBuffer)->PacketSize); pm_assert(debug_num_packets > 0); pm_assert(debug_packet_size < PACKET_MANAGER_MTU); pm_assert(SendBuffers[i].PacketSendLength < PACKET_MANAGER_MTU); //WWDEBUG_SAY(("Sending packet %d (%d bytes) to %s. Packet has %d packets of %d bytes each\n", i, PacketSendLength[i], Addr_As_String(&addr), debug_num_packets, debug_packet_size)); #endif //WWDEBUG #ifdef WRAPPER_CRC unsigned long crc = CRC::Memory((unsigned char*)SendBuffers[i].PacketBuffer, SendBuffers[i].PacketSendLength); #if (1) /* ** Reverse byte order to prevent the demo from having the same CRC as the game. */ _asm { push eax; mov eax,crc; bswap eax; mov crc,eax; pop eax; }; #endif //(0) char *crc_and_buffer = (char*)_alloca(SendBuffers[i].PacketSendLength + sizeof(crc)); *((unsigned long*) crc_and_buffer) = crc; memcpy(crc_and_buffer + sizeof(crc), (const char*)SendBuffers[i].PacketBuffer, SendBuffers[i].PacketSendLength); Register_Packet_Out(&SendBuffers[i].IPAddress[0], SendBuffers[i].Port, SendBuffers[i].PacketSendLength + UDP_HEADER_SIZE + sizeof(crc), 0); int result = sendto(socket, crc_and_buffer, SendBuffers[i].PacketSendLength + sizeof(crc), 0, (LPSOCKADDR) &addr, sizeof(SOCKADDR_IN)); #else //WRAPPER_CRC Register_Packet_Out(&SendBuffers[i].IPAddress[0], SendBuffers[i].Port, SendBuffers[i].PacketSendLength + UDP_HEADER_SIZE, 0); int result = sendto(socket, (const char*)SendBuffers[i].PacketBuffer, SendBuffers[i].PacketSendLength, 0, (LPSOCKADDR) &addr, sizeof(SOCKADDR_IN)); #endif //WRAPPER_CRC if (result == SOCKET_ERROR){ if (WSAGetLastError() != WSAEWOULDBLOCK) { int error_code = 0; error_code = WSAGetLastError();// avoid release build compiler warning WWDEBUG_SAY(("PacketManagerClass - sendto returned error code %d - %s\n", error_code, cNetUtil::Winsock_Error_Text(error_code))); Clear_Socket_Error(socket); } else { /* ** No more room for outgoing packets. Unfortunately, this means we lose the lot. */ WWDEBUG_SAY(("PacketManagerClass - sendto returned WSAEWOULDBLOCK\n")); Sleep(0); ErrorState = STATE_WS_BUFFERS_FULL; } } /* ** Send some random garbage to see if we crash. */ //char garbage[640]; //for (int i=0 ; i<540 ; i++) { // garbage[i] = rand(); //} //sendto(socket, (const char*)garbage, 540, 0, (LPSOCKADDR) &addr, sizeof(SOCKADDR_IN)); } } Update_Stats(); } } /*********************************************************************************************** * PacketManagerClass::Disable_Optimizations -- Disable all low level optimizations * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/26/2001 2:24PM TSS : Created * *=============================================================================================*/ void PacketManagerClass::Disable_Optimizations(void) { FlushFrequency = 0; AllowDeltas = false; AllowCombos = false; } /*********************************************************************************************** * PacketManagerClass::Break_Packet -- Break up in incoming packet into it's original packets * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/26/2001 2:25PM ST : Created * *=============================================================================================*/ bool PacketManagerClass::Break_Packet(unsigned char *packet, int original_packet_size, unsigned char *ip_address, unsigned short port) { /* ** Dereference a pointer to the packet header. */ PacketPackHeaderStruct *header = (PacketPackHeaderStruct*) packet; /* ** Pull out the number of packets and the size of each. */ int num_packets = header->NumPackets; int packet_size = header->PacketSize; bool more_packets = header->MorePackets; if (num_packets < 1 || packet_size > PACKET_MANAGER_MTU) { WWDEBUG_SAY(("PacketManager - Packet decode error. num_packets = %d\n, packet_size = %d\n", num_packets, packet_size)); return(false); } pm_assert(num_packets >= 1); pm_assert(packet_size <= PACKET_MANAGER_MTU); /* ** Get the first packet. This is needed as a reference for other delta packets. */ unsigned char *packet_ptr = packet + sizeof(*header); memcpy(&ReceiveBuffers[NumReceivePackets].ReceiveHoldingBuffer[0], packet_ptr, packet_size); ReceiveBuffers[NumReceivePackets].ReceivePacketLength = packet_size; Register_Packet_In(ip_address, port, 0, packet_size + UDP_HEADER_SIZE); int delta_base_index = NumReceivePackets; memcpy(ReceiveIPAddress, ip_address, 4); ReceivePort = port; NumReceivePackets++; //WWDEBUG_SAY(("Extracted base packet from metapacket - %d bytes\n", packet_size)); packet_ptr += packet_size; /* ** Get the rest of the packets. */ for (int i=0 ; iChunkPack || delta_header->BytePack) { int delta_size = 0; //#ifdef WWDEBUG int bytes = //#endif //WWDEBUG Reconstruct_From_Delta(&ReceiveBuffers[delta_base_index].ReceiveHoldingBuffer[0], &ReceiveBuffers[NumReceivePackets].ReceiveHoldingBuffer[0], (unsigned char *)delta_header, packet_size, delta_size); if (bytes != packet_size) { WWDEBUG_SAY(("*** WARNING: MALFORMED PACKET - PacketManagerClass::Break_Packet -- bytes != packet_size\n")); return(false); } if (delta_size <= 0) { WWDEBUG_SAY(("*** WARNING: MALFORMED PACKET - PacketManagerClass::Break_Packet -- delta_size <= 0\n")); return(false); } pm_assert(bytes == packet_size); pm_assert(delta_size > 0); ReceiveBuffers[NumReceivePackets].ReceivePacketLength = packet_size; NumReceivePackets++; packet_ptr += delta_size; Register_Packet_In(ip_address, port, 0, packet_size + UDP_HEADER_SIZE); //WWDEBUG_SAY(("Extracted delta packet from metapacket - %d bytes (delta size = %d)\n", bytes, delta_size)); } else { /* ** Not a delta, just copy the whole thing. */ packet_ptr += sizeof(*delta_header); memcpy(&ReceiveBuffers[NumReceivePackets].ReceiveHoldingBuffer[0], packet_ptr, packet_size); ReceiveBuffers[NumReceivePackets].ReceivePacketLength = packet_size; packet_ptr += packet_size; NumReceivePackets++; Register_Packet_In(ip_address, port, 0, packet_size + UDP_HEADER_SIZE); //WWDEBUG_SAY(("Extracted secondary packet from metapacket - %d (+1) bytes\n", PacketLength)); } if (NumReceivePackets >= PACKET_MANAGER_RECEIVE_BUFFERS) { break; } } int bytes_pulled_from_packet = packet_ptr - packet; /* ** More packets in this buffer? */ if (more_packets && NumReceivePackets < PACKET_MANAGER_RECEIVE_BUFFERS) { if (!Break_Packet(packet_ptr, original_packet_size - bytes_pulled_from_packet, ip_address, port)) { return(false); } } //if (NumReceivePackets > 20) { // WWDEBUG_SAY(("Sub packet broken into %d packets\n", NumReceivePackets)); //} return(true); } /*********************************************************************************************** * PacketManagerClass::Clear_Socket_Error -- Clear an error condition on a socket * * * * * * * * INPUT: Socket * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/17/2001 12:19PM ST : Created * *=============================================================================================*/ void PacketManagerClass::Clear_Socket_Error(SOCKET socket) { unsigned long error_code; int length = 4; assert(socket != INVALID_SOCKET); if (socket != INVALID_SOCKET) { getsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, &length); WWDEBUG_SAY(("Per socket error is %d - %s\n", error_code, cNetUtil::Winsock_Error_Text(error_code))); } } /*********************************************************************************************** * PacketManagerClass::Get_Packet -- Return the next incoming packet to the app * * * * * * * * INPUT: Socket to use * * Ptr to packet buffer * * Size of packet buffer * * (out) Ptr to ip_address * * (out) Port number * * * * OUTPUT: Size of packet (0 = no packet, -1 = fatal socket error) * * * * WARNINGS: None * * * * HISTORY: * * 9/26/2001 2:28PM ST : Created * *=============================================================================================*/ int PacketManagerClass::Get_Packet(SOCKET socket, unsigned char *packet_buffer, int packet_buffer_size, unsigned char *ip_address, unsigned short &port) { { WWPROFILE("Pmgr Get"); CriticalSectionClass::LockClass lock(CriticalSection); if (NumReceivePackets == 0) { int address_size = sizeof(sockaddr_in); sockaddr_in addr; memset(&addr, 0, sizeof(addr)); pm_assert(packet_buffer_size >= PACKET_MANAGER_MTU); int bytes; int result = ioctlsocket(socket, FIONREAD, (unsigned long *)&bytes); if (result == 0 && bytes != 0) { bytes = recvfrom(socket, (char*)packet_buffer, packet_buffer_size, 0, (LPSOCKADDR) &addr, &address_size); if (bytes > 0) { #ifndef WRAPPER_CRC Register_Packet_In((unsigned char*) &addr.sin_addr.s_addr, addr.sin_port, bytes + UDP_HEADER_SIZE, 0); #endif //WRAPPER_CRC #ifdef WRAPPER_CRC unsigned long crc = CRC::Memory((unsigned char*)packet_buffer + 4, bytes - sizeof(crc)); #if (1) /* ** Reverse byte order to prevent the demo from having the same CRC as the game. */ _asm { push eax; mov eax,crc; bswap eax; mov crc,eax; pop eax; }; #endif //(0) if (crc != *((unsigned long*)packet_buffer)) { WWDEBUG_SAY(("PMC::Get_Packet: Socket %d, received packet %d bytes long from %s\n", socket, bytes, Addr_As_String(&addr))); WWDEBUG_SAY(("PMC::Get_Packet: *** PACKET WRAPPER CRC ERROR ***")); NumReceivePackets = 0; } else { Register_Packet_In((unsigned char*) &addr.sin_addr.s_addr, addr.sin_port, bytes + UDP_HEADER_SIZE, 0); bytes -= sizeof(crc); memmove(packet_buffer, packet_buffer + sizeof(crc), bytes); #endif //WRAPPER_CRC //WWDEBUG_SAY(("PMC::Get_Packet: Socket %d, received packet %d bytes long from %s\n", socket, bytes, Addr_As_String(&addr))); ReceiveSocket = socket; //WWDEBUG_SAY(("Breaking packet %d bytes long from %s\n", bytes, Addr_As_String(&addr))); bool broken = Break_Packet(packet_buffer, bytes, (unsigned char*) &addr.sin_addr.s_addr, addr.sin_port); if (!broken) { WWDEBUG_SAY(("Failed to break packet %d bytes long from %s\n", bytes, Addr_As_String(&addr))); WWDEBUG_SAY(("Discarding %d suspect packets due to decode failure\n", NumReceivePackets)); NumReceivePackets = 0; } else { //WWDEBUG_SAY(("PMC::Get_Packet: Packet broken into %d packets\n", NumReceivePackets)); } CurrentPacket = 0; #ifdef WRAPPER_CRC } #endif //WRAPPER_CRC } else { if (bytes == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) { int error_code = 0; error_code = WSAGetLastError();// avoid release build compiler warning WWDEBUG_SAY(("PacketManagerClass - recvfrom failed with error %d - %s\n", error_code, cNetUtil::Winsock_Error_Text(error_code))); Clear_Socket_Error(socket); if (error_code == WSAECONNRESET) { WWDEBUG_SAY(("PacketManagerClass - WSAECONNRESET from address %s\n", Addr_As_String(&addr))); memcpy(ip_address, &addr.sin_addr.s_addr, 4); port = addr.sin_port; return(-1); } } else { WWDEBUG_SAY(("PacketManagerClass - recvfrom failed with error WSAEWOULDBLOCK\n", WSAGetLastError())); } } } } if (CurrentPacket < NumReceivePackets && socket == ReceiveSocket) { /* ** Copy the current packet into the return buffer. We have to zero out the rest of the buffer or the CRC won't come out ** right. Lordy. FIxed in CRC code now ST - 9/24/2001 3:36PM */ int size = (int)ReceiveBuffers[CurrentPacket].ReceivePacketLength; memcpy(packet_buffer, &ReceiveBuffers[CurrentPacket].ReceiveHoldingBuffer[0], min(size, packet_buffer_size)); //if (size < packet_buffer_size) { // memset(packet_buffer + size, 0, packet_buffer_size - size); //} memcpy(ip_address, ReceiveIPAddress, 4); port = ReceivePort; CurrentPacket++; if (CurrentPacket >= NumReceivePackets) { CurrentPacket = 0; NumReceivePackets = 0; } return(size); } return(0); } } /*********************************************************************************************** * PacketManager::Reset_Stats -- Reset bandwidth stats * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 8:54AM ST : Created * *=============================================================================================*/ void PacketManagerClass::Reset_Stats(void) { CriticalSectionClass::LockClass lock(CriticalSection); WWDEBUG_SAY(("PacketManagerClass Resetting stats\n")); BandwidthList.Delete_All(); LastStatsUpdate = TIMEGETTIME(); ResetStatsIn = true; ResetStatsOut = true; } /*********************************************************************************************** * PacketManager::Get_Stats_Index -- Get stats struct index for ip/port pair * * * * * * * * INPUT: IP * * Port * * Can create - true if allowed to create an entry if one doesn't exist * * * * OUTPUT: Index of stats struct for this ip/port, -1 for not found * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 8:54AM ST : Created * *=============================================================================================*/ int PacketManagerClass::Get_Stats_Index(unsigned long ip_address, unsigned short port, bool can_create) { /* ** Find the stats struct entry for this ip/port. */ for (int i=0 ; i StatsFrequency || forced) { LastStatsUpdate = time; /* ** Reset the bandwidth totals. */ TotalCompressedBandwidthIn = 0; TotalCompressedBandwidthOut = 0; TotalUncompressedBandwidthIn = 0; TotalUncompressedBandwidthOut = 0; /* ** Go through the entry for each ip/port and work out the averages. */ for (int i=0 ; iCompressedBytesOut) { //stats->CompressedBandwidthOut = (stats->CompressedBytesOut * 8) / StatsFrequency; stats->CompressedBandwidthOut = (int)((1000 * stats->CompressedBytesOut * 8) / (float) StatsFrequency); stats->CompressedBytesOut = 0; TotalCompressedBandwidthOut += stats->CompressedBandwidthOut; } if (stats->CompressedBytesIn) { //stats->CompressedBandwidthIn = (stats->CompressedBytesIn * 8) / StatsFrequency; stats->CompressedBandwidthIn = (int)((1000 * stats->CompressedBytesIn * 8) / (float) StatsFrequency); stats->CompressedBytesIn = 0; TotalCompressedBandwidthIn += stats->CompressedBandwidthIn; } if (stats->UncompressedBytesOut) { //stats->UncompressedBandwidthOut = (stats->UncompressedBytesOut * 8) / StatsFrequency; stats->UncompressedBandwidthOut = (int)((1000 * stats->UncompressedBytesOut * 8) / (float) StatsFrequency); stats->UncompressedBytesOut = 0; TotalUncompressedBandwidthOut += stats->UncompressedBandwidthOut; } if (stats->UncompressedBytesIn) { //stats->UncompressedBandwidthIn = (stats->UncompressedBytesIn * 8) / StatsFrequency; stats->UncompressedBandwidthIn = (int)((1000 * stats->UncompressedBytesIn * 8) / (float) StatsFrequency); stats->UncompressedBytesIn = 0; TotalUncompressedBandwidthIn += stats->UncompressedBandwidthIn; } } /* ** Just debug output. */ //WWDEBUG_SAY(("TotalUncompressedBandwidthOut = %d bits per second\n", TotalUncompressedBandwidthOut)); //WWDEBUG_SAY(("TotalCompressedBandwidthOut = %d bits per second\n", TotalCompressedBandwidthOut)); //WWDEBUG_SAY(("TotalUncompressedBandwidthIn = %d bits per second\n", TotalUncompressedBandwidthIn)); //WWDEBUG_SAY(("TotalCompressedBandwidthIn = %d bits per second\n", TotalCompressedBandwidthIn)); //unsigned long comp_out = 100 - ((100 * TotalCompressedBandwidthOut) / TotalUncompressedBandwidthOut); //unsigned long comp_in = 100 - ((100 * TotalCompressedBandwidthIn) / TotalUncompressedBandwidthIn); //WWDEBUG_SAY(("Compression out = %d percent\n", comp_out)); //WWDEBUG_SAY(("Compression in = %d percent\n", comp_in)); } } /*********************************************************************************************** * PacketManager::Get_Total_Raw_Bandwidth_In -- Get total uncompressed bandwidth in * * * * * * * * INPUT: Nothing * * * * OUTPUT: Total bandwidth before compression, in bits per second * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 8:59AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Total_Raw_Bandwidth_In(void) { return(TotalUncompressedBandwidthIn); } /*********************************************************************************************** * PacketManager::Get_Total_Raw_Bandwidth_Out -- Get total uncompressed bandwidth out * * * * * * * * INPUT: Nothing * * * * OUTPUT: Total bandwidth before compression, in bits per second * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 8:59AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Total_Raw_Bandwidth_Out(void) { return(TotalUncompressedBandwidthOut); } /*********************************************************************************************** * PacketManager::Get_Total_Compressed_Bandwidth_In -- Get total compressed bandwidth in * * * * * * * * INPUT: Nothing * * * * OUTPUT: Total bandwidth after compression, in bits per second * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 8:59AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Total_Compressed_Bandwidth_In(void) { return(TotalCompressedBandwidthIn); } /*********************************************************************************************** * PacketManager::Get_Total_Compressed_Bandwidth_Out -- Get total compressed bandwidth out * * * * * * * * INPUT: Nothing * * * * OUTPUT: Total bandwidth after compression, in bits per second * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 8:59AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Total_Compressed_Bandwidth_Out(void) { return(TotalCompressedBandwidthOut); } /*********************************************************************************************** * PacketManager::Get_Raw_Bandwidth_In -- Get uncompressed bandwidth in from given address * * * * * * * * INPUT: Address * * * * OUTPUT: Uncompressed bandwidth from address, in bits per second * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 9:01AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Raw_Bandwidth_In(SOCKADDR_IN *address) { CriticalSectionClass::LockClass lock(CriticalSection); unsigned long ip = *((unsigned long*)&address->sin_addr.s_addr); unsigned short port = address->sin_port; int stats = Get_Stats_Index(ip, port, false); unsigned long bw = 0; if (stats != -1) { bw = BandwidthList[stats].UncompressedBandwidthIn; } return(bw); } /*********************************************************************************************** * PacketManager::Get_Raw_Bandwidth_Out -- Get uncompressed bandwidth to given address * * * * * * * * INPUT: Address * * * * OUTPUT: Uncompressed bandwidth to address, in bits per second * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 9:01AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Raw_Bandwidth_Out(SOCKADDR_IN *address) { CriticalSectionClass::LockClass lock(CriticalSection); unsigned long ip = *((unsigned long*)&address->sin_addr.s_addr); unsigned short port = address->sin_port; int stats = Get_Stats_Index(ip, port, false); unsigned long bw = 0; if (stats != -1) { bw = BandwidthList[stats].UncompressedBandwidthOut; } return(bw); } /*********************************************************************************************** * PacketManager::Get_Raw_Bytes_Out -- Get uncompressed bytes to given address * * * * * * * * INPUT: Address * * * * OUTPUT: Uncompressed bytes sent to address. * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 9:01AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Raw_Bytes_Out(SOCKADDR_IN *address) { CriticalSectionClass::LockClass lock(CriticalSection); unsigned long ip = *((unsigned long*)&address->sin_addr.s_addr); unsigned short port = address->sin_port; int stats = Get_Stats_Index(ip, port, false); unsigned long bytes = 0; if (stats != -1) { bytes = BandwidthList[stats].UncompressedBytesOut; } return(bytes); } /*********************************************************************************************** * PacketManager::Get_Compressed_Bandwidth_In -- Get compressed bandwidth in from address * * * * * * * * INPUT: Address * * * * OUTPUT: Compressed bandwidth in from address, in bits per second * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 9:01AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Compressed_Bandwidth_In(SOCKADDR_IN *address) { CriticalSectionClass::LockClass lock(CriticalSection); unsigned long ip = *((unsigned long*)&address->sin_addr.s_addr); unsigned short port = address->sin_port; int stats = Get_Stats_Index(ip, port, false); unsigned long bw = 0; if (stats != -1) { bw = BandwidthList[stats].CompressedBandwidthIn; } return(bw); } /*********************************************************************************************** * PacketManager::Get_Compressed_Bandwidth_Out -- Get compressed bandwidth to address * * * * * * * * INPUT: Address * * * * OUTPUT: Compressed bandwidth to address, in bits per second * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 9:01AM ST : Created * *=============================================================================================*/ unsigned long PacketManagerClass::Get_Compressed_Bandwidth_Out(SOCKADDR_IN *address) { CriticalSectionClass::LockClass lock(CriticalSection); unsigned long ip = *((unsigned long*)&address->sin_addr.s_addr); unsigned short port = address->sin_port; int stats = Get_Stats_Index(ip, port, false); unsigned long bw = 0; if (stats != -1) { bw = BandwidthList[stats].CompressedBandwidthOut; } return(bw); } /*********************************************************************************************** * PacketManagerClass::Set_Stats_Sampling_Frequency -- Set sample freq for stats * * * * * * * * INPUT: New frequency (delay in ms) * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 10/9/2001 9:58AM ST : Created * *=============================================================================================*/ void PacketManagerClass::Set_Stats_Sampling_Frequency_Delay(unsigned long time_ms) { assert(time_ms > 0); StatsFrequency = time_ms; Reset_Stats(); } /*********************************************************************************************** * PacketManagerClass::Get_Error_State -- Report and clear winsock error state * * * * * * * * INPUT: Nothing * * * * OUTPUT: Class error state * * * * WARNINGS: None * * * * HISTORY: * * 10/24/2001 1:53PM ST : Created * *=============================================================================================*/ PacketManagerClass::ErrorStateEnum PacketManagerClass::Get_Error_State(void) { ErrorStateEnum state = ErrorState; ErrorState = STATE_OK; return(state); } /* ** Operators required to allow us to add BandwidthStateStruct to a dynamic vector. */ bool PacketManagerClass::BandwidthStatsStruct::operator == (BandwidthStatsStruct const &stats) { return((memcmp(this, &stats, sizeof(*this)) == 0) ? true : false); } bool PacketManagerClass::BandwidthStatsStruct::operator != (BandwidthStatsStruct const &stats) { return((memcmp(this, &stats, sizeof(*this)) == 0) ? false : true); }