File: qcommon\net_chan.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 
   21 #include "qcommon.h"
   22 
   23 /*
   24 
   25 packet header
   26 -------------
   27 31      sequence
   28 1       does this message contain a reliable payload
   29 31      acknowledge sequence
   30 1       acknowledge receipt of even/odd message
   31 16      qport
   32 
   33 The remote connection never knows if it missed a reliable message, the
   34 local side detects that it has been dropped by seeing a sequence acknowledge
   35 higher thatn the last reliable sequence, but without the correct evon/odd
   36 bit for the reliable set.
   37 
   38 If the sender notices that a reliable message has been dropped, it will be
   39 retransmitted.  It will not be retransmitted again until a message after
   40 the retransmit has been acknowledged and the reliable still failed to get there.
   41 
   42 if the sequence number is -1, the packet should be handled without a netcon
   43 
   44 The reliable message can be added to at any time by doing
   45 MSG_Write* (&netchan->message, <data>).
   46 
   47 If the message buffer is overflowed, either by a single message, or by
   48 multiple frames worth piling up while the last reliable transmit goes
   49 unacknowledged, the netchan signals a fatal error.
   50 
   51 Reliable messages are always placed first in a packet, then the unreliable
   52 message is included if there is sufficient room.
   53 
   54 To the receiver, there is no distinction between the reliable and unreliable
   55 parts of the message, they are just processed out as a single larger message.
   56 
   57 Illogical packet sequence numbers cause the packet to be dropped, but do
   58 not kill the connection.  This, combined with the tight window of valid
   59 reliable acknowledgement numbers provides protection against malicious
   60 address spoofing.
   61 
   62 
   63 The qport field is a workaround for bad address translating routers that
   64 sometimes remap the client's source port on a packet during gameplay.
   65 
   66 If the base part of the net address matches and the qport matches, then the
   67 channel matches even if the IP port differs.  The IP port should be updated
   68 to the new value before sending out any replies.
   69 
   70 
   71 If there is no information that needs to be transfered on a given frame,
   72 such as during the connection stage while waiting for the client to load,
   73 then a packet only needs to be delivered if there is something in the
   74 unacknowledged reliable
   75 */
   76 
   77 cvar_t          *showpackets;
   78 cvar_t          *showdrop;
   79 cvar_t          *qport;
   80 
   81 netadr_t        net_from;
   82 sizebuf_t       net_message;
   83 byte            net_message_buffer[MAX_MSGLEN];
   84 
   85 /*
   86 ===============
   87 Netchan_Init
   88 
   89 ===============
   90 */
   91 void Netchan_Init (void)
   92 {
   93         int             port;
   94 
   95         // pick a port value that should be nice and random
   96         port = Sys_Milliseconds() & 0xffff;
   97 
   98         showpackets = Cvar_Get ("showpackets", "0", 0);
   99         showdrop = Cvar_Get ("showdrop", "0", 0);
  100         qport = Cvar_Get ("qport", va("%i", port), CVAR_NOSET);
  101 }
  102 
  103 /*
  104 ===============
  105 Netchan_OutOfBand
  106 
  107 Sends an out-of-band datagram
  108 ================
  109 */
  110 void Netchan_OutOfBand (int net_socket, netadr_t adr, int length, byte *data)
  111 {
  112         sizebuf_t       send;
  113         byte            send_buf[MAX_MSGLEN];
  114 
  115 // write the packet header
  116         SZ_Init (&send, send_buf, sizeof(send_buf));
  117         
  118         MSG_WriteLong (&send, -1);      // -1 sequence means out of band
  119         SZ_Write (&send, data, length);
  120 
  121 // send the datagram
  122         NET_SendPacket (net_socket, send.cursize, send.data, adr);
  123 }
  124 
  125 /*
  126 ===============
  127 Netchan_OutOfBandPrint
  128 
  129 Sends a text message in an out-of-band datagram
  130 ================
  131 */
  132 void Netchan_OutOfBandPrint (int net_socket, netadr_t adr, char *format, ...)
  133 {
  134         va_list         argptr;
  135         static char             string[MAX_MSGLEN - 4];
  136         
  137         va_start (argptr, format);
  138         vsprintf (string, format,argptr);
  139         va_end (argptr);
  140 
  141         Netchan_OutOfBand (net_socket, adr, strlen(string), (byte *)string);
  142 }
  143 
  144 
  145 /*
  146 ==============
  147 Netchan_Setup
  148 
  149 called to open a channel to a remote system
  150 ==============
  151 */
  152 void Netchan_Setup (netsrc_t sock, netchan_t *chan, netadr_t adr, int qport)
  153 {
  154         memset (chan, 0, sizeof(*chan));
  155         
  156         chan->sock = sock;
  157         chan->remote_address = adr;
  158         chan->qport = qport;
  159         chan->last_received = curtime;
  160         chan->incoming_sequence = 0;
  161         chan->outgoing_sequence = 1;
  162 
  163         SZ_Init (&chan->message, chan->message_buf, sizeof(chan->message_buf));
  164         chan->message.allowoverflow = true;
  165 }
  166 
  167 
  168 /*
  169 ===============
  170 Netchan_CanReliable
  171 
  172 Returns true if the last reliable message has acked
  173 ================
  174 */
  175 qboolean Netchan_CanReliable (netchan_t *chan)
  176 {
  177         if (chan->reliable_length)
  178                 return false;                   // waiting for ack
  179         return true;
  180 }
  181 
  182 
  183 qboolean Netchan_NeedReliable (netchan_t *chan)
  184 {
  185         qboolean        send_reliable;
  186 
  187 // if the remote side dropped the last reliable message, resend it
  188         send_reliable = false;
  189 
  190         if (chan->incoming_acknowledged > chan->last_reliable_sequence
  191         && chan->incoming_reliable_acknowledged != chan->reliable_sequence)
  192                 send_reliable = true;
  193 
  194 // if the reliable transmit buffer is empty, copy the current message out
  195         if (!chan->reliable_length && chan->message.cursize)
  196         {
  197                 send_reliable = true;
  198         }
  199 
  200         return send_reliable;
  201 }
  202 
  203 /*
  204 ===============
  205 Netchan_Transmit
  206 
  207 tries to send an unreliable message to a connection, and handles the
  208 transmition / retransmition of the reliable messages.
  209 
  210 A 0 length will still generate a packet and deal with the reliable messages.
  211 ================
  212 */
  213 void Netchan_Transmit (netchan_t *chan, int length, byte *data)
  214 {
  215         sizebuf_t       send;
  216         byte            send_buf[MAX_MSGLEN];
  217         qboolean        send_reliable;
  218         unsigned        w1, w2;
  219 
  220 // check for message overflow
  221         if (chan->message.overflowed)
  222         {
  223                 chan->fatal_error = true;
  224                 Com_Printf ("%s:Outgoing message overflow\n"
  225                         , NET_AdrToString (chan->remote_address));
  226                 return;
  227         }
  228 
  229         send_reliable = Netchan_NeedReliable (chan);
  230 
  231         if (!chan->reliable_length && chan->message.cursize)
  232         {
  233                 memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize);
  234                 chan->reliable_length = chan->message.cursize;
  235                 chan->message.cursize = 0;
  236                 chan->reliable_sequence ^= 1;
  237         }
  238 
  239 
  240 // write the packet header
  241         SZ_Init (&send, send_buf, sizeof(send_buf));
  242 
  243         w1 = ( chan->outgoing_sequence & ~(1<<31) ) | (send_reliable<<31);
  244         w2 = ( chan->incoming_sequence & ~(1<<31) ) | (chan->incoming_reliable_sequence<<31);
  245 
  246         chan->outgoing_sequence++;
  247         chan->last_sent = curtime;
  248 
  249         MSG_WriteLong (&send, w1);
  250         MSG_WriteLong (&send, w2);
  251 
  252         // send the qport if we are a client
  253         if (chan->sock == NS_CLIENT)
  254                 MSG_WriteShort (&send, qport->value);
  255 
  256 // copy the reliable message to the packet first
  257         if (send_reliable)
  258         {
  259                 SZ_Write (&send, chan->reliable_buf, chan->reliable_length);
  260                 chan->last_reliable_sequence = chan->outgoing_sequence;
  261         }
  262         
  263 // add the unreliable part if space is available
  264         if (send.maxsize - send.cursize >= length)
  265                 SZ_Write (&send, data, length);
  266         else
  267                 Com_Printf ("Netchan_Transmit: dumped unreliable\n");
  268 
  269 // send the datagram
  270         NET_SendPacket (chan->sock, send.cursize, send.data, chan->remote_address);
  271 
  272         if (showpackets->value)
  273         {
  274                 if (send_reliable)
  275                         Com_Printf ("send %4i : s=%i reliable=%i ack=%i rack=%i\n"
  276                                 , send.cursize
  277                                 , chan->outgoing_sequence - 1
  278                                 , chan->reliable_sequence
  279                                 , chan->incoming_sequence
  280                                 , chan->incoming_reliable_sequence);
  281                 else
  282                         Com_Printf ("send %4i : s=%i ack=%i rack=%i\n"
  283                                 , send.cursize
  284                                 , chan->outgoing_sequence - 1
  285                                 , chan->incoming_sequence
  286                                 , chan->incoming_reliable_sequence);
  287         }
  288 }
  289 
  290 /*
  291 =================
  292 Netchan_Process
  293 
  294 called when the current net_message is from remote_address
  295 modifies net_message so that it points to the packet payload
  296 =================
  297 */
  298 qboolean Netchan_Process (netchan_t *chan, sizebuf_t *msg)
  299 {
  300         unsigned        sequence, sequence_ack;
  301         unsigned        reliable_ack, reliable_message;
  302         int                     qport;
  303 
  304 // get sequence numbers         
  305         MSG_BeginReading (msg);
  306         sequence = MSG_ReadLong (msg);
  307         sequence_ack = MSG_ReadLong (msg);
  308 
  309         // read the qport if we are a server
  310         if (chan->sock == NS_SERVER)
  311                 qport = MSG_ReadShort (msg);
  312 
  313         reliable_message = sequence >> 31;
  314         reliable_ack = sequence_ack >> 31;
  315 
  316         sequence &= ~(1<<31);
  317         sequence_ack &= ~(1<<31);       
  318 
  319         if (showpackets->value)
  320         {
  321                 if (reliable_message)
  322                         Com_Printf ("recv %4i : s=%i reliable=%i ack=%i rack=%i\n"
  323                                 , msg->cursize
  324                                 , sequence
  325                                 , chan->incoming_reliable_sequence ^ 1
  326                                 , sequence_ack
  327                                 , reliable_ack);
  328                 else
  329                         Com_Printf ("recv %4i : s=%i ack=%i rack=%i\n"
  330                                 , msg->cursize
  331                                 , sequence
  332                                 , sequence_ack
  333                                 , reliable_ack);
  334         }
  335 
  336 //
  337 // discard stale or duplicated packets
  338 //
  339         if (sequence <= chan->incoming_sequence)
  340         {
  341                 if (showdrop->value)
  342                         Com_Printf ("%s:Out of order packet %i at %i\n"
  343                                 , NET_AdrToString (chan->remote_address)
  344                                 ,  sequence
  345                                 , chan->incoming_sequence);
  346                 return false;
  347         }
  348 
  349 //
  350 // dropped packets don't keep the message from being used
  351 //
  352         chan->dropped = sequence - (chan->incoming_sequence+1);
  353         if (chan->dropped > 0)
  354         {
  355                 if (showdrop->value)
  356                         Com_Printf ("%s:Dropped %i packets at %i\n"
  357                         , NET_AdrToString (chan->remote_address)
  358                         , chan->dropped
  359                         , sequence);
  360         }
  361 
  362 //
  363 // if the current outgoing reliable message has been acknowledged
  364 // clear the buffer to make way for the next
  365 //
  366         if (reliable_ack == chan->reliable_sequence)
  367                 chan->reliable_length = 0;      // it has been received
  368         
  369 //
  370 // if this message contains a reliable message, bump incoming_reliable_sequence 
  371 //
  372         chan->incoming_sequence = sequence;
  373         chan->incoming_acknowledged = sequence_ack;
  374         chan->incoming_reliable_acknowledged = reliable_ack;
  375         if (reliable_message)
  376         {
  377                 chan->incoming_reliable_sequence ^= 1;
  378         }
  379 
  380 //
  381 // the message can now be read from the current message pointer
  382 //
  383         chan->last_received = curtime;
  384 
  385         return true;
  386 }
  387 
  388