summaryrefslogtreecommitdiff
path: root/source/net_chan.c
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2007-08-14 20:18:08 +0000
committerAndrey Nazarov <skuller@skuller.net>2007-08-14 20:18:08 +0000
commitf294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch)
treee8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/net_chan.c
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/net_chan.c')
-rw-r--r--source/net_chan.c878
1 files changed, 878 insertions, 0 deletions
diff --git a/source/net_chan.c b/source/net_chan.c
new file mode 100644
index 0000000..a52ca72
--- /dev/null
+++ b/source/net_chan.c
@@ -0,0 +1,878 @@
+/*
+Copyright (C) 1997-2001 Id Software, 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 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.
+
+*/
+
+#include "com_local.h"
+#include "net_chan.h"
+
+/*
+
+packet header
+-------------
+31 sequence
+1 does this message contain a reliable payload
+31 acknowledge sequence
+1 acknowledge receipt of even/odd message
+16 qport
+
+The remote connection never knows if it missed a reliable message, the
+local side detects that it has been dropped by seeing a sequence acknowledge
+higher thatn the last reliable sequence, but without the correct evon/odd
+bit for the reliable set.
+
+If the sender notices that a reliable message has been dropped, it will be
+retransmitted. It will not be retransmitted again until a message after
+the retransmit has been acknowledged and the reliable still failed to get theref.
+
+if the sequence number is -1, the packet should be handled without a netcon
+
+The reliable message can be added to at any time by doing
+MSG_Write* (&netchan->message, <data>).
+
+If the message buffer is overflowed, either by a single message, or by
+multiple frames worth piling up while the last reliable transmit goes
+unacknowledged, the netchan signals a fatal error.
+
+Reliable messages are always placed first in a packet, then the unreliable
+message is included if there is sufficient room.
+
+To the receiver, there is no distinction between the reliable and unreliable
+parts of the message, they are just processed out as a single larger message.
+
+Illogical packet sequence numbers cause the packet to be dropped, but do
+not kill the connection. This, combined with the tight window of valid
+reliable acknowledgement numbers provides protection against malicious
+address spoofing.
+
+
+The qport field is a workaround for bad address translating routers that
+sometimes remap the client's source port on a packet during gameplay.
+
+If the base part of the net address matches and the qport matches, then the
+channel matches even if the IP port differs. The IP port should be updated
+to the new value before sending out any replies.
+
+
+If there is no information that needs to be transfered on a given frame,
+such as during the connection stage while waiting for the client to load,
+then a packet only needs to be delivered if there is something in the
+unacknowledged reliable
+*/
+
+static cvar_t *showpackets;
+static cvar_t *showdrop;
+
+cvar_t *net_qport;
+cvar_t *net_maxmsglen;
+cvar_t *net_chantype;
+
+netadr_t net_from;
+
+/*
+===============
+Netchan_Init
+
+===============
+*/
+void Netchan_Init( void ) {
+ int port;
+
+ // pick a port value that should be nice and random
+ port = Sys_Milliseconds() & 0xffff;
+
+ showpackets = Cvar_Get( "showpackets", "0", 0 );
+ showdrop = Cvar_Get( "showdrop", "0", 0 );
+ net_qport = Cvar_Get( "qport", va( "%i", port ), 0 );
+ net_maxmsglen = Cvar_Get( "net_maxmsglen", "1390", 0 );
+ net_chantype = Cvar_Get( "net_chantype", "1", 0 );
+}
+
+/*
+===============
+Netchan_OutOfBand
+
+Sends an out-of-band datagram
+================
+*/
+neterr_t Netchan_OutOfBand( netsrc_t sock, const netadr_t *address,
+ uint32 length, const byte *data )
+{
+ sizebuf_t send;
+ byte send_data[MAX_PACKETLEN_DEFAULT];
+ neterr_t ret;
+
+ SZ_Init( &send, send_data, sizeof( send_data ) );
+
+// write the packet header
+ SZ_WriteLong( &send, -1 ); // -1 sequence means out of band
+ SZ_Write( &send, data, length );
+
+// send the datagram
+ ret = NET_SendPacket( sock, address, send.cursize, send.data );
+
+ return ret;
+
+}
+
+/*
+===============
+Netchan_OutOfBandPrint
+
+Sends a text message in an out-of-band datagram
+================
+*/
+neterr_t Netchan_OutOfBandPrint( netsrc_t sock, const netadr_t *address,
+ const char *format, ... )
+{
+ va_list argptr;
+ byte send_data[MAX_PACKETLEN_DEFAULT];
+ int length;
+ neterr_t ret;
+
+/* write the packet header */
+ *( uint32 * )send_data = -1; /* -1 sequence means out of band */
+
+ va_start( argptr, format );
+ length = Q_vsnprintf( ( char * )send_data + 4, sizeof( send_data ) - 4,
+ format, argptr );
+ va_end( argptr );
+
+/* send the datagram */
+ ret = NET_SendPacket( sock, address, length + 4, send_data );
+
+ return ret;
+}
+
+// ============================================================================
+
+static int NetchanOld_TransmitNextFragment( netchan_t *netchan ) {
+ Com_Error( ERR_FATAL, "NetchanOld_TransmitNextFragment: not implemented" );
+ return 0;
+}
+
+/*
+===============
+NetchanOld_Transmit
+
+tries to send an unreliable message to a connection, and handles the
+transmition / retransmition of the reliable messages.
+
+A 0 length will still generate a packet and deal with the reliable messages.
+================
+*/
+static int NetchanOld_Transmit( netchan_t *netchan, int length,
+ const byte *data )
+{
+ netchan_old_t *chan = ( netchan_old_t * )netchan;
+ sizebuf_t send;
+ byte send_buf[MAX_PACKETLEN];
+ qboolean send_reliable;
+ uint32 w1, w2;
+ neterr_t ret;
+
+// check for message overflow
+ if( netchan->message.overflowed ) {
+ netchan->fatal_error = qtrue;
+ Com_WPrintf( "%s: outgoing message overflow\n",
+ NET_AdrToString( &netchan->remote_address ) );
+ return -1;
+ }
+
+ send_reliable = qfalse;
+
+ // if the remote side dropped the last reliable message, resend it
+ if( netchan->incoming_acknowledged > chan->last_reliable_sequence &&
+ chan->incoming_reliable_acknowledged != chan->reliable_sequence )
+ {
+ send_reliable = qtrue;
+ }
+
+// if the reliable transmit buffer is empty, copy the current message out
+ if( !netchan->reliable_length && netchan->message.cursize ) {
+ send_reliable = qtrue;
+ memcpy( chan->reliable_buf, chan->message_buf,
+ netchan->message.cursize );
+ netchan->reliable_length = netchan->message.cursize;
+ netchan->message.cursize = 0;
+ chan->reliable_sequence ^= 1;
+ }
+
+// write the packet header
+ w1 = ( netchan->outgoing_sequence & ~( 1 << 31 ) ) |
+ ( send_reliable << 31 );
+ w2 = ( netchan->incoming_sequence & ~( 1 << 31 ) ) |
+ ( chan->incoming_reliable_sequence << 31 );
+
+ netchan->outgoing_sequence++;
+ netchan->reliable_ack_pending = qfalse;
+ netchan->last_sent = com_localTime;
+
+ SZ_Init( &send, send_buf, sizeof( send_buf ) );
+
+ SZ_WriteLong( &send, w1 );
+ SZ_WriteLong( &send, w2 );
+
+ // send the qport if we are a client
+ if( netchan->sock == NS_CLIENT ) {
+ if( netchan->protocol < PROTOCOL_VERSION_R1Q2 ) {
+ SZ_WriteShort( &send, netchan->qport );
+ } else if( netchan->qport ) {
+ SZ_WriteByte( &send, netchan->qport );
+ }
+ }
+
+// copy the reliable message to the packet first
+ if( send_reliable ) {
+ SZ_Write( &send, chan->reliable_buf, netchan->reliable_length );
+ chan->last_reliable_sequence = netchan->outgoing_sequence;
+ }
+
+// add the unreliable part if space is available
+ if( send.maxsize - send.cursize >= length )
+ SZ_Write( &send, data, length );
+ else
+ Com_WPrintf( "%s: dumped unreliable\n",
+ NET_AdrToString( &netchan->remote_address ) );
+
+ if( showpackets->integer ) {
+ Com_Printf( "send %4i : s=%i ack=%i rack=%i",
+ send.cursize,
+ netchan->outgoing_sequence - 1,
+ netchan->incoming_sequence,
+ chan->incoming_reliable_sequence );
+ if( send_reliable ) {
+ Com_Printf( " reliable=%i", chan->reliable_sequence );
+ }
+ Com_Printf( "\n" );
+ }
+
+ // send the datagram
+ ret = NET_SendPacket( netchan->sock, &netchan->remote_address,
+ send.cursize, send.data );
+ if( ret == NET_ERROR ) {
+ return -1;
+ }
+
+ return send.cursize;
+
+}
+
+/*
+=================
+NetchanOld_Process
+
+called when the current net_message is from remote_address
+modifies net_message so that it points to the packet payload
+=================
+*/
+static qboolean NetchanOld_Process( netchan_t *netchan ) {
+ netchan_old_t *chan = ( netchan_old_t * )netchan;
+ uint32 sequence, sequence_ack;
+ uint32 reliable_ack, reliable_message;
+ int qport;
+
+// get sequence numbers
+ MSG_BeginReading();
+ sequence = MSG_ReadLong();
+ sequence_ack = MSG_ReadLong();
+
+ // read the qport if we are a server
+ if( netchan->sock == NS_SERVER ) {
+ if( netchan->protocol < PROTOCOL_VERSION_R1Q2 ) {
+ qport = MSG_ReadShort();
+ } else if( netchan->qport ) {
+ qport = MSG_ReadByte();
+ }
+ }
+
+ reliable_message = sequence >> 31;
+ reliable_ack = sequence_ack >> 31;
+
+ sequence &= ~( 1 << 31 );
+ sequence_ack &= ~( 1 << 31 );
+
+ if( showpackets->integer ) {
+ Com_Printf( "recv %4i : s=%i ack=%i rack=%i",
+ msg_read.cursize,
+ sequence,
+ sequence_ack,
+ reliable_ack );
+ if( reliable_message ) {
+ Com_Printf( " reliable=%i", chan->incoming_reliable_sequence ^ 1 );
+ }
+ Com_Printf( "\n" );
+ }
+
+//
+// discard stale or duplicated packets
+//
+ if( sequence <= netchan->incoming_sequence ) {
+ if( showdrop->integer )
+ Com_Printf( "%s: out of order packet %i at %i\n",
+ NET_AdrToString( &netchan->remote_address ),
+ sequence,
+ netchan->incoming_sequence );
+ return qfalse;
+ }
+
+//
+// dropped packets don't keep the message from being used
+//
+ netchan->dropped = sequence - ( netchan->incoming_sequence + 1 );
+ if( netchan->dropped > 0 ) {
+ if( showdrop->integer )
+ Com_Printf( "%s: dropped %i packets at %i\n",
+ NET_AdrToString( &netchan->remote_address ),
+ netchan->dropped,
+ sequence );
+ }
+
+//
+// if the current outgoing reliable message has been acknowledged
+// clear the buffer to make way for the next
+//
+ chan->incoming_reliable_acknowledged = reliable_ack;
+ if( reliable_ack == chan->reliable_sequence )
+ netchan->reliable_length = 0; // it has been received
+
+//
+// if this message contains a reliable message, bump incoming_reliable_sequence
+//
+ netchan->incoming_sequence = sequence;
+ netchan->incoming_acknowledged = sequence_ack;
+ if( reliable_message ) {
+ netchan->reliable_ack_pending = qtrue;
+ chan->incoming_reliable_sequence ^= 1;
+ }
+
+//
+// the message can now be read from the current message pointer
+//
+ netchan->last_received = com_localTime;
+
+ return qtrue;
+}
+
+/*
+===============
+NetchanOld_ShouldUpdate
+================
+*/
+static qboolean NetchanOld_ShouldUpdate( netchan_t *netchan ) {
+ if( netchan->message.cursize || netchan->reliable_ack_pending ||
+ com_localTime - netchan->last_sent > 1000 )
+ {
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+==============
+NetchanOld_Setup
+
+called to open a channel to a remote system
+==============
+*/
+static netchan_t *NetchanOld_Setup( netsrc_t sock, const netadr_t *adr,
+ int qport, int maxpacketlen )
+{
+ netchan_old_t *chan;
+ netchan_t *netchan;
+
+ Z_Reserve( sizeof( *chan ) + maxpacketlen * 2 );
+
+ chan = Z_ReservedAlloc( sizeof( *chan ) );
+ memset( chan, 0, sizeof( *chan ) );
+ netchan = ( netchan_t * )chan;
+ netchan->sock = sock;
+ netchan->remote_address = *adr;
+ netchan->qport = qport;
+ netchan->maxpacketlen = maxpacketlen;
+ netchan->last_received = com_localTime;
+ netchan->incoming_sequence = 0;
+ netchan->outgoing_sequence = 1;
+
+ netchan->Process = NetchanOld_Process;
+ netchan->Transmit = NetchanOld_Transmit;
+ netchan->TransmitNextFragment = NetchanOld_TransmitNextFragment;
+ netchan->ShouldUpdate = NetchanOld_ShouldUpdate;
+
+ chan->message_buf = Z_ReservedAlloc( maxpacketlen );
+ SZ_Init( &netchan->message, chan->message_buf, maxpacketlen );
+
+ chan->reliable_buf = Z_ReservedAlloc( maxpacketlen );
+
+ return netchan;
+}
+
+// ============================================================================
+
+
+/*
+===============
+NetchanNew_TransmitNextFragment
+================
+*/
+static int NetchanNew_TransmitNextFragment( netchan_t *netchan ) {
+ netchan_new_t *chan = ( netchan_new_t * )netchan;
+ sizebuf_t send;
+ byte send_buf[MAX_PACKETLEN];
+ qboolean send_reliable;
+ uint32 w1, w2;
+ uint16 offset;
+ int fragment_length;
+ qboolean more_fragments;
+ neterr_t ret;
+
+ send_reliable = netchan->reliable_length ? qtrue : qfalse;
+
+ /* write the packet header */
+ w1 = ( netchan->outgoing_sequence & 0x3FFFFFFF ) | ( 1 << 30 ) |
+ ( send_reliable << 31 );
+ w2 = ( netchan->incoming_sequence & 0x3FFFFFFF ) | ( 0 << 30 ) |
+ ( chan->incoming_reliable_sequence << 31 );
+
+ SZ_Init( &send, send_buf, sizeof( send_buf ) );
+ send.allowoverflow = qfalse;
+
+ SZ_WriteLong( &send, w1 );
+ SZ_WriteLong( &send, w2 );
+
+ /* send the qport if we are a client */
+ if( netchan->sock == NS_CLIENT && netchan->qport ) {
+ SZ_WriteByte( &send, netchan->qport );
+ }
+
+ fragment_length = chan->fragment_out.cursize - chan->fragment_out.readcount;
+ if( fragment_length > netchan->maxpacketlen ) {
+ fragment_length = netchan->maxpacketlen;
+ }
+
+ more_fragments = qtrue;
+ if( chan->fragment_out.readcount + fragment_length ==
+ chan->fragment_out.cursize )
+ {
+ more_fragments = qfalse;
+ }
+
+ /* write fragment offset */
+ offset = ( chan->fragment_out.readcount & 0x7FFF ) |
+ ( more_fragments << 15 );
+ SZ_WriteShort( &send, offset );
+
+ /* write fragment contents */
+ SZ_Write( &send, chan->fragment_out.data + chan->fragment_out.readcount,
+ fragment_length );
+
+ if( showpackets->integer ) {
+ Com_Printf( "send %4i : s=%i ack=%i rack=%i "
+ "fragment_offset=%i more_fragments=%i",
+ send.cursize,
+ netchan->outgoing_sequence,
+ netchan->incoming_sequence,
+ chan->incoming_reliable_sequence,
+ chan->fragment_out.readcount,
+ more_fragments );
+ if( send_reliable ) {
+ Com_Printf( " reliable=%i ", chan->reliable_sequence );
+ }
+ Com_Printf( "\n" );
+ }
+
+ chan->fragment_out.readcount += fragment_length;
+ netchan->fragment_pending = more_fragments;
+
+ /* if the message has been sent completely, clear the fragment buffer */
+ if( !netchan->fragment_pending ) {
+ netchan->outgoing_sequence++;
+ netchan->last_sent = com_localTime;
+ SZ_Clear( &chan->fragment_out );
+ }
+
+ // send the datagram
+ ret = NET_SendPacket( netchan->sock, &netchan->remote_address,
+ send.cursize, send.data );
+ if( ret == NET_ERROR ) {
+ return -1;
+ }
+
+ return send.cursize;
+}
+
+/*
+===============
+NetchanNew_Transmit
+================
+*/
+static int NetchanNew_Transmit( netchan_t *netchan, int length,
+ const byte *data )
+{
+ netchan_new_t *chan = ( netchan_new_t * )netchan;
+ sizebuf_t send;
+ byte send_buf[MAX_PACKETLEN];
+ qboolean send_reliable;
+ uint32 w1, w2;
+ neterr_t ret;
+
+// check for message overflow
+ if( netchan->message.overflowed ) {
+ netchan->fatal_error = qtrue;
+ Com_WPrintf( "%s: outgoing message overflow\n",
+ NET_AdrToString( &netchan->remote_address ) );
+ return -1;
+ }
+
+ if( netchan->fragment_pending ) {
+ return NetchanNew_TransmitNextFragment( netchan );
+ }
+
+ send_reliable = qfalse;
+
+// if the remote side dropped the last reliable message, resend it
+ if( netchan->incoming_acknowledged > chan->last_reliable_sequence &&
+ chan->incoming_reliable_acknowledged != chan->reliable_sequence )
+ {
+ send_reliable = qtrue;
+ }
+
+// if the reliable transmit buffer is empty, copy the current message out
+ if( !netchan->reliable_length && netchan->message.cursize ) {
+ send_reliable = qtrue;
+ memcpy( chan->reliable_buf, chan->message_buf,
+ netchan->message.cursize );
+ netchan->reliable_length = netchan->message.cursize;
+ netchan->message.cursize = 0;
+ chan->reliable_sequence ^= 1;
+ }
+
+ if( length > netchan->maxpacketlen || ( send_reliable &&
+ ( netchan->reliable_length + length > netchan->maxpacketlen ) ) )
+ {
+ if( send_reliable ) {
+ chan->last_reliable_sequence = netchan->outgoing_sequence;
+ SZ_Write( &chan->fragment_out, chan->reliable_buf,
+ netchan->reliable_length );
+ }
+ SZ_Write( &chan->fragment_out, data, length );
+ return NetchanNew_TransmitNextFragment( netchan );
+ }
+
+// write the packet header
+ w1 = ( netchan->outgoing_sequence & 0x3FFFFFFF ) | ( send_reliable << 31 );
+ w2 = ( netchan->incoming_sequence & 0x3FFFFFFF ) |
+ ( chan->incoming_reliable_sequence << 31 );
+
+ netchan->outgoing_sequence++;
+ netchan->reliable_ack_pending = qfalse;
+ netchan->last_sent = com_localTime;
+
+ SZ_Init( &send, send_buf, netchan->maxpacketlen );
+ send.allowoverflow = qfalse;
+
+ SZ_WriteLong( &send, w1 );
+ SZ_WriteLong( &send, w2 );
+
+ // send the qport if we are a client
+ if( netchan->sock == NS_CLIENT && netchan->qport ) {
+ SZ_WriteByte( &send, netchan->qport );
+ }
+
+ // copy the reliable message to the packet first
+ if( send_reliable ) {
+ chan->last_reliable_sequence = netchan->outgoing_sequence;
+ SZ_Write( &send, chan->reliable_buf, netchan->reliable_length );
+ }
+
+ // add the unreliable part
+ SZ_Write( &send, data, length );
+
+ if( showpackets->integer ) {
+ Com_Printf( "send %4i : s=%i ack=%i rack=%i",
+ send.cursize,
+ netchan->outgoing_sequence - 1,
+ netchan->incoming_sequence,
+ chan->incoming_reliable_sequence );
+ if( send_reliable ) {
+ Com_Printf( " reliable=%i", chan->reliable_sequence );
+ }
+ Com_Printf( "\n" );
+ }
+
+ // send the datagram
+ ret = NET_SendPacket( netchan->sock, &netchan->remote_address,
+ send.cursize, send.data );
+ if( ret == NET_ERROR ) {
+ return -1;
+ }
+
+ return send.cursize;
+
+}
+
+/*
+=================
+NetchanNew_Process
+=================
+*/
+static qboolean NetchanNew_Process( netchan_t *netchan ) {
+ netchan_new_t *chan = ( netchan_new_t * )netchan;
+ uint32 sequence, sequence_ack, reliable_ack;
+ qboolean reliable_message, fragmented_message, more_fragments;
+ uint16 fragment_offset;
+ int length;
+
+// get sequence numbers
+ MSG_BeginReading();
+ sequence = MSG_ReadLong();
+ sequence_ack = MSG_ReadLong();
+
+ // read the qport if we are a server
+ if( netchan->sock == NS_SERVER && netchan->qport ) {
+ MSG_ReadByte();
+ }
+
+ reliable_message = sequence >> 31;
+ reliable_ack = sequence_ack >> 31;
+ fragmented_message = ( sequence >> 30 ) & 1;
+
+ sequence &= 0x3FFFFFFF;
+ sequence_ack &= 0x3FFFFFFF;
+
+ fragment_offset = 0;
+ more_fragments = qfalse;
+ if( fragmented_message ) {
+ fragment_offset = MSG_ReadShort();
+ more_fragments = fragment_offset >> 15;
+ fragment_offset &= 0x7FFF;
+ }
+
+ if( showpackets->integer ) {
+ Com_Printf( "recv %4i : s=%i ack=%i rack=%i", msg_read.cursize,
+ sequence, sequence_ack, reliable_ack );
+ if( fragmented_message ) {
+ Com_Printf( " fragment_offset=%i more_fragments=%i",
+ fragment_offset, more_fragments );
+ }
+ if( reliable_message ) {
+ Com_Printf( " reliable=%i", chan->incoming_reliable_sequence ^ 1 );
+ }
+ Com_Printf( "\n" );
+ }
+
+//
+// discard stale or duplicated packets
+//
+ if( sequence <= netchan->incoming_sequence ) {
+ if( showdrop->integer || showpackets->integer ) {
+ Com_Printf( "%s: out of order packet %i at %i\n",
+ NET_AdrToString( &netchan->remote_address ),
+ sequence, netchan->incoming_sequence );
+ }
+ return qfalse;
+ }
+
+//
+// dropped packets don't keep the message from being used
+//
+ netchan->dropped = sequence - ( netchan->incoming_sequence + 1 );
+ if( netchan->dropped > 0 ) {
+ if( showdrop->integer || showpackets->integer ) {
+ Com_Printf( "%s: dropped %i packets at %i\n",
+ NET_AdrToString( &netchan->remote_address ),
+ netchan->dropped, sequence );
+ }
+ }
+
+//
+// if the current outgoing reliable message has been acknowledged
+// clear the buffer to make way for the next
+//
+ chan->incoming_reliable_acknowledged = reliable_ack;
+ if( reliable_ack == chan->reliable_sequence ) {
+ netchan->reliable_length = 0; /* it has been received */
+ }
+
+
+//
+// parse fragment header, if any
+//
+ if( fragmented_message ) {
+ if( chan->fragment_sequence != sequence ) {
+ /* start new receive sequence */
+ chan->fragment_sequence = sequence;
+ SZ_Clear( &chan->fragment_in );
+ }
+
+ if( fragment_offset < chan->fragment_in.cursize ) {
+ if( showdrop->integer || showpackets->integer ) {
+ Com_Printf( "%s: out of order fragment at %i\n",
+ NET_AdrToString( &netchan->remote_address ),
+ sequence );
+ }
+ return qfalse;
+ }
+
+ if( fragment_offset > chan->fragment_in.cursize ) {
+ if( showdrop->integer || showpackets->integer ) {
+ Com_Printf( "%s: dropped fragment(s) at %i\n",
+ NET_AdrToString( &netchan->remote_address ),
+ sequence );
+ }
+ return qfalse;
+ }
+
+ length = msg_read.cursize - msg_read.readcount;
+ if( chan->fragment_in.cursize + length > chan->fragment_in.maxsize ) {
+ if( showdrop->integer || showpackets->integer ) {
+ Com_Printf( "%s: oversize fragment at %i\n",
+ NET_AdrToString( &netchan->remote_address ),
+ sequence );
+ }
+ return qfalse;
+ }
+
+ SZ_Write( &chan->fragment_in, msg_read.data + msg_read.readcount,
+ length );
+ if( more_fragments ) {
+ return qfalse;
+ }
+
+ /* message has been sucessfully assembled */
+ SZ_Clear( &msg_read );
+ SZ_Write( &msg_read, chan->fragment_in.data,
+ chan->fragment_in.cursize );
+ SZ_Clear( &chan->fragment_in );
+ }
+
+ netchan->incoming_sequence = sequence;
+ netchan->incoming_acknowledged = sequence_ack;
+
+//
+// if this message contains a reliable message, bump incoming_reliable_sequence
+//
+ if( reliable_message ) {
+ netchan->reliable_ack_pending = qtrue;
+ chan->incoming_reliable_sequence ^= 1;
+ }
+
+//
+// the message can now be read from the current message pointer
+//
+ netchan->last_received = com_localTime;
+
+ return qtrue;
+}
+
+/*
+==============
+NetchanNew_ShouldUpdate
+==============
+*/
+static qboolean NetchanNew_ShouldUpdate( netchan_t *netchan ) {
+ netchan_new_t *chan = ( netchan_new_t * )netchan;
+
+ if( netchan->message.cursize ||
+ netchan->reliable_ack_pending ||
+ chan->fragment_out.cursize ||
+ com_localTime - netchan->last_sent > 1000 )
+ {
+ return qtrue;
+ }
+
+ return qfalse;
+}
+
+/*
+==============
+NetchanNew_Setup
+==============
+*/
+static netchan_t *NetchanNew_Setup( netsrc_t sock, const netadr_t *adr,
+ int qport, int maxpacketlen )
+{
+ netchan_new_t *chan;
+ netchan_t *netchan;
+
+ chan = Z_Mallocz( sizeof( *chan ) );
+ netchan = ( netchan_t * )chan;
+ netchan->sock = sock;
+ netchan->remote_address = *adr;
+ netchan->qport = qport;
+ netchan->maxpacketlen = maxpacketlen;
+ netchan->last_received = com_localTime;
+ netchan->incoming_sequence = 0;
+ netchan->outgoing_sequence = 1;
+
+ netchan->Process = NetchanNew_Process;
+ netchan->Transmit = NetchanNew_Transmit;
+ netchan->TransmitNextFragment = NetchanNew_TransmitNextFragment;
+ netchan->ShouldUpdate = NetchanNew_ShouldUpdate;
+
+ SZ_Init( &netchan->message, chan->message_buf,
+ sizeof( chan->message_buf ) );
+ SZ_Init( &chan->fragment_in, chan->fragment_in_buf,
+ sizeof( chan->fragment_in_buf ) );
+ SZ_Init( &chan->fragment_out, chan->fragment_out_buf,
+ sizeof( chan->fragment_out_buf ) );
+
+ return netchan;
+}
+
+/*
+==============
+Netchan_Setup
+==============
+*/
+netchan_t *Netchan_Setup( netsrc_t sock, netchan_type_t type,
+ const netadr_t *adr, int qport, int maxpacketlen, int protocol )
+{
+ netchan_t *netchan;
+
+ clamp( maxpacketlen, 256, MAX_PACKETLEN_WRITABLE );
+
+ switch( type ) {
+ case NETCHAN_OLD:
+ netchan = NetchanOld_Setup( sock, adr, qport, maxpacketlen );
+ break;
+ case NETCHAN_NEW:
+ netchan = NetchanNew_Setup( sock, adr, qport, maxpacketlen );
+ break;
+ default:
+ Com_Error( ERR_FATAL, "Netchan_Setup: bad type" );
+ netchan = NULL;
+ }
+
+ netchan->protocol = protocol;
+ netchan->type = type;
+
+ return netchan;
+
+}
+
+/*
+==============
+Netchan_Close
+==============
+*/
+void Netchan_Close( netchan_t *netchan ) {
+ Z_Free( netchan );
+}
+