aboutsummaryrefslogtreecommitdiff
path: root/src/parsec_server/net_packetdriver.cpp
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
committerFelix Morgner <felix.morgner@gmail.com>2026-08-24 11:16:07 +0200
commitc068f22329d5cc722622a2183bbb22eef2093df7 (patch)
tree12d56c1aede67988a55e241364606bfbb4dba933 /src/parsec_server/net_packetdriver.cpp
downloadopenparsec-c068f22329d5cc722622a2183bbb22eef2093df7.tar.xz
openparsec-c068f22329d5cc722622a2183bbb22eef2093df7.zip
initial importHEADmain
Diffstat (limited to 'src/parsec_server/net_packetdriver.cpp')
-rwxr-xr-xsrc/parsec_server/net_packetdriver.cpp755
1 files changed, 755 insertions, 0 deletions
diff --git a/src/parsec_server/net_packetdriver.cpp b/src/parsec_server/net_packetdriver.cpp
new file mode 100755
index 0000000..4c6edf7
--- /dev/null
+++ b/src/parsec_server/net_packetdriver.cpp
@@ -0,0 +1,755 @@
+/*
+ * PARSEC - Packet driver
+ *
+ * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:48 $
+ *
+ * Orginally written by:
+ * Copyright (c) Clemens Beer <cbx@parsec.org> 2002
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// C library
+#include <stdlib.h>
+#include <stdio.h>
+
+// compilation flags/debug support
+#include "config.h"
+#include "debug.h"
+
+// general definitions
+#include "general.h"
+#include "objstruc.h"
+
+// global externals
+#include "globals.h"
+
+// subsystem headers
+#include "net_defs.h"
+
+// UNP header
+#include "net_wrap.h"
+
+// mathematics header
+#include "utl_math.h"
+
+// server defs
+#include "e_defs.h"
+
+// network code config
+#include "net_conf.h"
+
+// local module header
+#include "net_packetdriver.h"
+
+// proprietary module headers
+#include "con_aux_sv.h"
+#include "net_game_sv.h"
+#include "net_udpdriver.h"
+#include "net_stream.h"
+#include "e_gameserver.h"
+#include "e_packethandler.h"
+#include "e_simulator.h"
+#include "sys_util_sv.h"
+
+
+// flags ----------------------------------------------------------------------
+//
+
+// number of listen buffers in listen buffers array ---------------------------
+//
+#define NUM_LISTEN_BUFFERS ( MAX_NET_ALLOC_SLOTS * 2 )
+
+// history buffer for received message ids to filter out duplicates -----------
+//
+#define MSGID_HISTORY_SIZE 64
+
+// packet loss meter ----------------------------------------------------------
+//
+//FIXME: we need a packet loss meter for every client
+#define PACKET_LOSS_METER_LENGTH 100 // horizontal size
+
+
+// string constants -----------------------------------------------------------
+//
+static const char pchain_alloc_error[]= "Error allocating packet chain.\n";
+
+// ----------------------------------------------------------------------------
+// public methods
+// ----------------------------------------------------------------------------
+
+// default ctor ---------------------------------------------------------------
+//
+NET_PacketDriver::NET_PacketDriver()
+{
+ // init all to NULL for Reset to not delete the data
+ ReceivedPacketsChain = NULL;
+ SendNetPacketExternal = NULL;
+ RecvNetPacketExternal = NULL;
+ m_Streams = NULL;
+
+ Reset();
+}
+
+
+// reset all data members -----------------------------------------------------
+//
+void NET_PacketDriver::Reset()
+{
+ // free when necessary
+ if ( SendNetPacketExternal != NULL ) FREEMEM( SendNetPacketExternal );
+ if ( RecvNetPacketExternal != NULL ) FREEMEM( RecvNetPacketExternal );
+ if ( ReceivedPacketsChain != NULL ) _FreePacketsChain();
+ int bid;
+ for ( bid = 0; bid < NUM_LISTEN_BUFFERS; bid++ ) {
+ if ( NetPacketsInternal[ bid ] != NULL ) FREEMEM( NetPacketsInternal[ bid ] );
+ }
+
+ // allocate the external packets
+ SendNetPacketExternal = (NetPacketExternal_GMSV *) ALLOCMEM( NET_UDP_DATA_LENGTH );
+ RecvNetPacketExternal = (NetPacketExternal_GMSV *) ALLOCMEM( NET_UDP_DATA_LENGTH );
+ memset( SendNetPacketExternal, 0, NET_UDP_DATA_LENGTH );
+ memset( RecvNetPacketExternal, 0, NET_UDP_DATA_LENGTH );
+
+ // allocate the internal packets
+ for ( bid = 0; bid < NUM_LISTEN_BUFFERS; bid++ ) {
+ NetPacketsInternal[ bid ] = (NetPacket_GMSV *) ALLOCMEM( NET_MAX_NETPACKET_INTERNAL_LEN );
+ memset( NetPacketsInternal[ bid ], 0, NET_MAX_NETPACKET_INTERNAL_LEN );
+ memset( &ListenAddress[ bid ], 0, sizeof( sockaddr_in ) );
+ }
+
+ // init the streams
+ if ( m_Streams != NULL ) {
+ delete []m_Streams;
+ m_Streams = NULL;
+ }
+ m_Streams = new NET_Stream[ MAX_NUM_CLIENTS ];
+
+ // set the client IDs the stream is connected to
+ for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) {
+ m_Streams[ nClientID ].SetPeerID( nClientID );
+ m_Streams[ nClientID ].SetSenderID( PLAYERID_SERVER );
+ }
+
+ // allocate the packet chain
+ _AllocPacketsChain();
+}
+
+
+// get the stream message status ----------------------------------------------
+//
+NET_Stream* NET_PacketDriver::GetStream( int nClientID )
+{
+ ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) );
+ return &m_Streams[ nClientID ];
+}
+
+// reset the stream of a specific client --------------------------------------
+//
+void NET_PacketDriver::ResetStream( int nClientID )
+{
+ ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) );
+#ifdef PARSEC_DEBUG
+ MSGOUT( "resetting stream for client %d", nClientID );
+#endif // PARSEC_DEBUG
+ m_Streams[ nClientID ].Reset();
+ m_Streams[ nClientID ].SetPeerID( nClientID );
+ m_Streams[ nClientID ].SetSenderID( PLAYERID_SERVER );
+}
+
+
+// connect the stream of a client ---------------------------------------------
+//
+void NET_PacketDriver::ConnectStream( int nClientID )
+{
+ ResetStream( nClientID );
+ m_Streams[ nClientID ].SetConnected();
+}
+
+
+// flush the reliable backbuffer of a stream ----------------------------------
+//
+void NET_PacketDriver::FlushReliableBuffer( int nClientID )
+{
+ ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) );
+ m_Streams[ nClientID ].FlushReliableBuffer();
+}
+
+// default dtor ---------------------------------------------------------------
+//
+NET_PacketDriver::~NET_PacketDriver()
+{
+ // free the packet chain
+ _FreePacketsChain();
+
+ // free internal/external packets
+ FREEMEM( SendNetPacketExternal );
+ FREEMEM( RecvNetPacketExternal );
+ SendNetPacketExternal = NULL;
+ RecvNetPacketExternal = NULL;
+
+ for ( int bid = 0; bid < NUM_LISTEN_BUFFERS; bid++ ) {
+ FREEMEM( NetPacketsInternal[ bid ] );
+ NetPacketsInternal[ bid ] = NULL;
+ }
+
+ if ( m_Streams != NULL ) {
+ delete []m_Streams;
+ m_Streams = NULL;
+ }
+}
+
+// invoke processing function for all packets currently in chain --------------
+//
+void NET_PacketDriver::NET_ProcessPacketChain()
+{
+ // build chain of received packets
+ _BuildReceivedPacketsChain();
+
+ // invoke processing function for received packets
+ PacketChainBlock *block;
+ while ( ( block = _FetchPacketFromChain() ) != NULL ) {
+
+ // invoke packet processing function
+ ThePacketHandler->HandlePacket( block );
+
+ // release the packet from the packetchain
+ _ReleasePacketFromChain( block );
+ }
+}
+
+
+// retrieve sender's node address from listen header --------------------------
+//
+node_t* NET_PacketDriver::GetPktSender( int bufid )
+{
+ ASSERT( bufid >= 0 );
+ ASSERT( bufid < NUM_LISTEN_BUFFERS );
+
+ node_t *node;
+
+ //NOTE:
+ // instead of returning the address directly from ListenAdress[]
+ // we make a copy here, since the port number will be appended and
+ // we don't want to assume there is enough memory in the structure
+ // after the sin_addr field. (usually there are eight irrelevant bytes)
+
+ node = &ListenSenderStorage[ bufid ];
+
+ memcpy( node, &ListenAddress[ bufid ].sin_addr, IP_ADR_LENGTH );
+ NODE_StorePort( node, ntohs( ListenAddress[ bufid ].sin_port ) );
+
+ return node;
+}
+
+// return the message id used in the next packet that is sent -----------------
+//
+int NET_PacketDriver::GetNextOutMessageId( int nClientID )
+{
+ ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) );
+ return m_Streams[ nClientID ].GetNextOutMessageId();
+}
+
+
+// fill standard fields in gamepacket header ----------------------------------
+//
+void NET_PacketDriver::FillStdGameHeader( byte command, NetPacket_GMSV* gamepacket )
+{
+ ASSERT( gamepacket != NULL );
+
+ // clear header and remote event list area
+ memset( gamepacket, 0, NET_MAX_NETPACKET_INTERNAL_LEN );
+
+ gamepacket->Command = command;
+ gamepacket->SendPlayerId = PLAYERID_SERVER;
+
+ // RE list size always includes the RE terminator
+ ((RE_Header* )&gamepacket->RE_List)->RE_Type = RE_EMPTY;
+ gamepacket->RE_ListSize = sizeof( dword );
+}
+
+
+// determine if packet should be artificially dropped -------------------------
+//
+int NET_PacketDriver::_CheckForcePacketDrop()
+{
+
+#ifdef ENABLE_PACKETDROP_TESTING
+
+ //NOTE:
+ // SVAUX_PACKETDROP_TESTING sets the probability
+ // of packet-drop (well, approximately).
+
+ if ( SVAUX_PACKETDROP_TESTING ) {
+
+ int r = RAND() % 100;
+ if ( r < SVAUX_PACKETDROP_TESTING ) {
+ return TRUE;
+ }
+ }
+
+#endif // ENABLE_PACKETDROP_TESTING
+
+ return FALSE;
+}
+
+
+// send a datagram ( unreliable, unnumbered packet ) to a node ----------------
+//
+int NET_PacketDriver::SendDatagram( NetPacket_GMSV* gamepacket, node_t* node, int nClientID, E_REList* pUnreliable )
+{
+ ASSERT( gamepacket != NULL );
+ ASSERT( node != NULL );
+ ASSERT( pUnreliable != NULL );
+
+ RE_Header* pFillPosition = (RE_Header*)(&gamepacket->RE_List);
+ size_t maxfillsize = RE_LIST_MAXAVAIL + sizeof( dword );
+
+ // copy unreliable RE ( account maxsize for dword in NetPacket_GMSV )
+ size_t unreliable_size = pUnreliable->WriteTo( pFillPosition, maxfillsize, TRUE );
+
+ // unreliable overflow ?
+ if ( unreliable_size == 0 ) {
+ DBGTXT( MSGOUT( "NET_PacketDriver::SendDatagram(): WARNING: unreliable overflow" ); );
+ return FALSE;
+ }
+
+ // fill message header/#s for datagram
+ gamepacket->MessageId = MSGID_DATAGRAM;
+ gamepacket->ReliableMessageId = 0;
+ gamepacket->AckMessageId = 0;
+ gamepacket->AckReliableMessageId = 0;
+
+ // pack from internal packet to external packet
+ size_t pktsize = NETs_HandleOutPacket( (NetPacket*)gamepacket, SendNetPacketExternal );
+
+ sockaddr_in SendAddress;
+ bzero( &SendAddress, sizeof( SendAddress ) );
+ SendAddress.sin_family = AF_INET;
+ SendAddress.sin_port = htons( NODE_GetPort( node ) );
+ memcpy ( &SendAddress.sin_addr, node, IP_ADR_LENGTH );
+
+ int rc = TheUDPDriver->SendPacket( (char *) SendNetPacketExternal, pktsize, (SA*) &SendAddress );
+
+ return pktsize;
+}
+
+
+// send a packet to a node ----------------------------------------------------
+//
+int NET_PacketDriver::SendPacket( NetPacket_GMSV* gamepacket, node_t* node, int nClientID, E_REList* pReliable, E_REList* pUnreliable )
+{
+ ASSERT( gamepacket != NULL );
+ ASSERT( node != NULL );
+ ASSERT( nClientID != PLAYERID_MASTERSERVER );
+ ASSERT( nClientID != PLAYERID_ANONYMOUS );
+ ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) );
+
+ NET_Stream* pStream = &m_Streams[ nClientID ];
+ ASSERT( pStream != NULL );
+
+ // check for artificial packet drop
+ if ( _CheckForcePacketDrop() ) {
+ pStream->LogPacketLossStats( 1, FALSE, FALSE );
+ return -1;
+ }
+
+ //FIXME_MASV: eventually move all E_REList dependent code to util function
+
+ // append reliable data to queue in stream and check for reliable choke
+ if ( ( pReliable != NULL ) && ( pReliable->GetSize() > 0 ) ) {
+ if ( pStream->AppendToReliableFIFO( pReliable ) == FALSE ) {
+ ASSERT( nClientID != PLAYERID_MASTERSERVER );
+
+ //FIXME: what do we do with the RELIABLE RE_COMMANDINFO containing the DISCONNECT
+
+ // disconnect the client if we have a reliable choke
+ MSGOUT("Choking");
+ TheConnManager->DisconnectClient( nClientID );
+ return -1;
+ }
+ }
+
+ // retrieve the first reliable RE list
+ pReliable = pStream->GetNextReliableToSend();
+
+ RE_Header* pFillPosition = (RE_Header*)(&gamepacket->RE_List);
+ size_t maxfillsize = RE_LIST_MAXAVAIL + sizeof( dword );
+ size_t reliable_size = 0;
+ size_t unreliable_size = 0;
+
+ if ( ( pReliable != NULL ) && ( pReliable->GetSize() > 0 ) ) {
+ ASSERT( nClientID != PLAYERID_MASTERSERVER );
+
+ // copy reliable RE and check for reliable overflow
+ reliable_size = pReliable->WriteTo( pFillPosition, maxfillsize, FALSE );
+ if ( reliable_size == 0 ) {
+ DBGTXT( MSGOUT( "NET_PacketDriver::SendPacket(): ERROR: reliable overflow" ); );
+ return -1;
+ }
+ // determine next fill position
+ pFillPosition = (RE_Header*) ( ((char*)(&gamepacket->RE_List)) + reliable_size );
+ maxfillsize -= ( reliable_size + 1 );
+ }
+
+ // handle unreliable part
+ if ( ( pUnreliable != NULL ) && ( pUnreliable->GetSize() > 0 ) ) {
+
+ // copy unreliable RE ( account maxsize for dword in NetPacket_GMSV and size of already inserted reliable )
+ unreliable_size = pUnreliable->WriteTo( pFillPosition, maxfillsize, TRUE );
+
+ // unreliable overflow ?
+ if ( unreliable_size == 0 ) {
+ DBGTXT( MSGOUT( "NET_PacketDriver::SendPacket(): WARNING: unreliable overflow" ); );
+ }
+ }
+
+ // we do not send empty ( header only ) packets
+ if ( ( reliable_size + unreliable_size ) == 0 ) {
+ DBGTXT( MSGOUT( "NET_PacketDriver::SendPacket(): WARNING: skip sending of empty packet ( header only ) - possibly due to reliable buffering." ); );
+ return 0;
+ }
+
+ // fill in message-IDs and ACKs
+ pStream->OutPacket( gamepacket, ( pReliable != NULL ) );
+
+ // pack from internal packet to external packet
+ size_t pktsize = NETs_HandleOutPacket( (NetPacket*)gamepacket, SendNetPacketExternal );
+
+ // write local packet to stream - for testing purposes only
+#ifdef SAVE_PACKETSTREAM
+ SaveLocalPacket( SendNetPacketExternal );
+#endif // SAVE_PACKETSTREAM
+
+ sockaddr_in SendAddress;
+ bzero( &SendAddress, sizeof( SendAddress ) );
+ SendAddress.sin_family = AF_INET;
+ SendAddress.sin_port = htons( NODE_GetPort( node ) );
+ memcpy ( &SendAddress.sin_addr, node, IP_ADR_LENGTH );
+
+ int rc = TheUDPDriver->SendPacket( (char *) SendNetPacketExternal, pktsize, (SA*) &SendAddress );
+
+ // log packet loss
+ pStream->LogPacketLossStats( 1, ( rc >= 0 ), FALSE );
+
+ return pktsize;
+}
+
+
+// ----------------------------------------------------------------------------
+// protected methods
+// ----------------------------------------------------------------------------
+
+// retrieve next packet from received packets chain ---------------------------
+//
+PacketChainBlock* NET_PacketDriver::_FetchPacketFromChain()
+{
+ ASSERT( ReceivedPacketsChain != NULL );
+
+ PacketChainBlock *temp = ReceivedPacketsChain->nextblock;
+ if ( temp != NULL ) {
+ ASSERT( ReceivedPacketsChain->chainlength > 0 );
+ ReceivedPacketsChain->nextblock = temp->nextblock;
+ temp->nextblock = NULL;
+ ReceivedPacketsChain->chainlength--;
+ }
+
+ return temp;
+}
+
+
+// allocate received packets chain (head node) --------------------------------
+//
+int NET_PacketDriver::_AllocPacketsChain()
+{
+ ASSERT( ReceivedPacketsChain == NULL );
+ ReceivedPacketsChain = (PacketChainHead *) ALLOCMEM( sizeof( PacketChainHead ) );
+ if ( ReceivedPacketsChain == NULL ) {
+ fprintf( stderr, pchain_alloc_error );
+ return FALSE;
+ }
+ ReceivedPacketsChain->nextblock = NULL;
+ ReceivedPacketsChain->chainlength = 0;
+
+ return TRUE;
+}
+
+
+// free all blocks in received packets chain ----------------------------------
+//
+void NET_PacketDriver::_FreePacketsChain()
+{
+ ASSERT( ReceivedPacketsChain != NULL );
+ while ( ReceivedPacketsChain != NULL ) {
+ PacketChainBlock *temp = ReceivedPacketsChain->nextblock;
+ FREEMEM( ReceivedPacketsChain );
+ ReceivedPacketsChain = (PacketChainHead *) temp;
+ }
+}
+
+
+// release block containing pointer and info of packet ------------------------
+//
+void NET_PacketDriver::_ReleasePacketFromChain( PacketChainBlock *block )
+{
+ CHECKHEAPBASEREF( block );
+ FREEMEM( block );
+}
+
+
+// pre-process received packets -----------------------------------------------
+//
+void NET_PacketDriver::_BuildReceivedPacketsChain()
+{
+ //NOTE:
+ // this function checks all packets received since it was last called.
+ // packets are decrypted, CRC checked and converted from external to internal
+ // packet format in _UDP_FetchPacket.
+ // the packets have to be sequenced according to their id, as they may be
+ // in any order, and then inserted into the chain (this is done by ChainPacket() ).
+
+ ASSERT( ReceivedPacketsChain != NULL );
+
+#ifdef ASSERT_PACKET_CHAIN_EMPTY
+ ASSERT( ReceivedPacketsChain->nextblock == NULL );
+ ASSERT( ReceivedPacketsChain->chainlength == 0 );
+#endif
+
+ // scan listen buffers
+ for ( int bufid = 0; bufid < NUM_LISTEN_BUFFERS; bufid++ ) {
+
+ // try to retrieve packet
+ if ( !_UDP_FetchPacket( bufid ) )
+ break;
+
+ // process received packet
+ if ( !ListenStatus[ bufid ] ) {
+
+ // insert packet into chain
+ _ChainPacket( NetPacketsInternal[ bufid ], bufid );
+ }
+ }
+}
+
+
+// insert received packet into chain ------------------------------------------
+//
+int NET_PacketDriver::_ChainPacket( NetPacket_GMSV* gamepacket, int bufid )
+{
+ ASSERT( gamepacket != NULL );
+ ASSERT( ReceivedPacketsChain != NULL );
+
+ PacketChainBlock* scan = ReceivedPacketsChain;
+ int nClientID = gamepacket->SendPlayerId;
+ int messageid = gamepacket->MessageId;
+
+ // exclude non-regular packets (negative sender ids) from
+ // duplicate checking: PLAYERID_SERVER, PLAYERID_ANONYMOUS
+ switch( nClientID ) {
+ case PLAYERID_SERVER:
+
+ if(!TheServer->GetServerIsMaster()){
+ DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): filtering packet %d from %d", messageid, nClientID ); );
+ return FALSE;
+ }
+ break;
+ case PLAYERID_ANONYMOUS:
+ MSGOUT("Ignoring Anonymous Packet");
+ break;
+
+ case PLAYERID_MASTERSERVER:
+ {
+ //FIXME: _IsLegitSender() is also called in E_PacketHandler::HandlePacket(), duplicate work ?
+
+ // check src node with master server node
+ node_t* node = GetPktSender( bufid );
+ if ( !TheServer->IsMasterServerNode( node ) ) {
+ //DBGTXT(
+ MSGOUT( "NET_PacketDriver::_ChainPacket(): MasterServer NODE MISMATCH %s", NODE_Print( node ) );// );
+ return FALSE;
+ }
+ }
+ break;
+
+ default:
+ if( ( nClientID < 0 ) || ( nClientID >= MAX_NUM_CLIENTS ) ) {
+ DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): filtering packet %d from %d", messageid, nClientID ); );
+ return FALSE;
+ } else {
+
+ if(!TheServer->GetServerIsMaster()){
+ // check src node with stored node for already connected client
+ node_t* clientnode = GetPktSender( bufid );
+ if ( !TheConnManager->CheckNodesMatch( nClientID, clientnode ) ) {
+ DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): NODE MISMATCH %d: %s", nClientID, NODE_Print( clientnode ) ); );
+ return FALSE;
+ }
+
+ // check regular player packets for duplicates
+ if ( _FilterPacketDuplicate( nClientID, messageid ) ) {
+ DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): filtering duplicate packet %d from %d", messageid, nClientID ); );
+ return FALSE;
+ }
+ }
+ }
+ break;
+ }
+
+ // no duplicate checking/stream handling for datagrams
+ if ( (dword)messageid != MSGID_DATAGRAM ) {
+
+ // normal stream packets must come from identified clients
+ ASSERT( nClientID != PLAYERID_ANONYMOUS );
+
+ // search for packet of same sender already in chain
+ for ( ; scan->nextblock; scan = scan->nextblock ) {
+ if ( NODE_Compare( GetPktSender( scan->nextblock->bufferno ), GetPktSender( bufid ) ) == NODECMP_EQUAL ) {
+ break;
+ }
+ }
+
+ // search to right insert position (according to message id)
+ for ( ; scan->nextblock; scan = scan->nextblock ) {
+ if ( NODE_Compare( GetPktSender( scan->nextblock->bufferno ), GetPktSender( bufid ) ) != NODECMP_EQUAL ) {
+ break;
+ }
+ if ( messageid <= scan->nextblock->messageid ) {
+ if ( messageid == scan->nextblock->messageid )
+ DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): received same message twice: %d.", messageid ); );
+ break;
+ }
+ }
+
+ // maintain stream IDs/ACKs
+ NET_Stream* pStream = GetStream( nClientID );
+ if ( !pStream->InPacket( gamepacket ) ) {
+ MSGOUT("Return in maintain stream IDs/ACKs");
+ return FALSE;
+ }
+ }
+
+ // record packet
+ if ( bufid != VIRTUAL_BUFFER_ID ) {
+ //MSGOUT("Recording Packet");
+ _RecordPacket( bufid );
+ }
+
+ // alloc block in chain
+ PacketChainBlock *temp = (PacketChainBlock *) ALLOCMEM( sizeof( PacketChainBlock ) );
+ if ( temp == NULL ) {
+ ASSERT( 0 );
+ return FALSE;
+ }
+
+ // store critical info
+ temp->bufferno = bufid;
+ temp->gamepacket= gamepacket;
+ temp->messageid = messageid;
+
+ // link packet into chain
+ temp->nextblock = scan->nextblock;
+ scan->nextblock = temp;
+ ReceivedPacketsChain->chainlength++;
+
+ return TRUE;
+}
+
+
+// record packet before inserting it into packet chain ------------------------
+//
+void NET_PacketDriver::_RecordPacket( int bufid )
+{
+/*#ifdef ENABLE_PACKET_RECORDING
+
+ ASSERT( bufid != VIRTUAL_BUFFER_ID );
+
+ // check whether we are currently recording packets
+ if ( REC_IsRecordingPackets() ) {
+
+ // translate NetPacketsInternal to corresponding DEMO NetPacketExternal
+ NetPacketExternal* ext_gamepacket = (NetPacketExternal*)ALLOCMEM( RECORD_PACKET_SIZE );
+ NETs_HandleOutPacket_DEMO( NetPacketsInternal[ bufid ], ext_gamepacket );
+
+
+ //NOTE:
+ // packets are recorded in network byte-order, that is, unswapped.
+ // This is exactly the format that is held in a NetPackeExternal
+ // ( along with encryption, compression etc ).
+
+ // record the external DEMO packet
+ REC_RecordRemotePacket( ext_gamepacket );
+ }
+
+#endif // ENABLE_PACKET_RECORDING
+*/
+}
+
+
+// check regular player packets for duplicates --------------------------------
+//
+int NET_PacketDriver::_FilterPacketDuplicate( int senderid, int messageid )
+{
+ ASSERT( ( senderid >= 0 ) && ( senderid < MAX_NUM_CLIENTS ) );
+ return m_Streams[ senderid ].FilterPacketDuplicate( messageid );
+}
+
+
+// retrieve received udp packet and store into listen buffer ------------------
+//
+int NET_PacketDriver::_UDP_FetchPacket( int bufid )
+{
+ // assume buffer is busy (nothing received)
+ ListenStatus[ bufid ] = TRUE;
+
+ // fetch packet
+ sockaddr_in from_adress;
+ //ListenDatLen[ bufid ] =
+ int rc = TheUDPDriver->FetchPacket( (char *)RecvNetPacketExternal, NET_UDP_DATA_LENGTH, (SA *)&from_adress );
+
+ if ( rc >= 0 ) {
+
+ // copy from adress
+ memcpy( &ListenAddress[ bufid ], &from_adress, sizeof( sockaddr_in ) );
+
+ // filter broadcast packets that are looping back
+ if ( NODE_Compare( TheUDPDriver->GetNode(), GetPktSender( bufid ) ) == NODECMP_EQUAL ) {
+ //DBGTXT(
+ MSGOUT( "NET_PacketDriver::_UDP_FetchPacket(): dropped packet [loop-back]." ); //);
+ // indicate we want to try to receive more packets
+ return TRUE;
+ }
+
+ // convert external packet to internal format
+ if ( !NETs_HandleInPacket( RecvNetPacketExternal, (size_t )rc, (NetPacket*) NetPacketsInternal[ bufid ] ) ) {
+ DBGTXT( MSGOUT( "NET_PacketDriver::_UDP_FetchPacket(): dropped packet [conversion error]." ); );
+ return FALSE;
+ } else {
+
+ UPDTXT2(MSGOUT( "NET_PacketDriver::_UDP_FetchPacket(): received packet msg: %d.", RecvNetPacketExternal->MessageId ));
+
+ // indicate that buffer now contains received packet
+ ListenStatus[ bufid ] = FALSE;
+ return TRUE;
+ }
+
+ } else {
+
+ //FIXME: could check for ERRNO_ECONNRESET (ICMP "Port Unreachable"), to identify dead clients
+ return FALSE;
+ }
+}
+
+
+