diff options
| author | Travis Bradshaw <travis.bradshaw@idsoftware.com> | 2012-01-31 14:32:09 -0600 |
|---|---|---|
| committer | Travis Bradshaw <travis.bradshaw@idsoftware.com> | 2012-01-31 14:32:09 -0600 |
| commit | 0023db327bc1db00068284b70e1db45857aeee35 (patch) | |
| tree | 01dd8366d20bd674741cfe8ceb069d82a2e33973 /QW/client/net_chan.c | |
| download | quake-pum-0023db327bc1db00068284b70e1db45857aeee35.tar.gz | |
The Quake sources as originally release under the GPL license on December 21, 1999
Diffstat (limited to 'QW/client/net_chan.c')
| -rw-r--r-- | QW/client/net_chan.c | 452 |
1 files changed, 452 insertions, 0 deletions
diff --git a/QW/client/net_chan.c b/QW/client/net_chan.c new file mode 100644 index 0000000..bd325a1 --- /dev/null +++ b/QW/client/net_chan.c @@ -0,0 +1,452 @@ +/* +Copyright (C) 1996-1997 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 "quakedef.h" + +#ifdef _WIN32 +#include "winquake.h" +#endif + +#define PACKET_HEADER 8 + +/* + +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 there. + +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 allways 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. + + +*/ + +int net_drop; +cvar_t showpackets = {"showpackets", "0"}; +cvar_t showdrop = {"showdrop", "0"}; +cvar_t qport = {"qport", "0"}; + +/* +=============== +Netchan_Init + +=============== +*/ +void Netchan_Init (void) +{ + int port; + + // pick a port value that should be nice and random +#ifdef _WIN32 + port = ((int)(timeGetTime()*1000) * time(NULL)) & 0xffff; +#else + port = ((int)(getpid()+getuid()*1000) * time(NULL)) & 0xffff; +#endif + + Cvar_RegisterVariable (&showpackets); + Cvar_RegisterVariable (&showdrop); + Cvar_RegisterVariable (&qport); + Cvar_SetValue("qport", port); +} + +/* +=============== +Netchan_OutOfBand + +Sends an out-of-band datagram +================ +*/ +void Netchan_OutOfBand (netadr_t adr, int length, byte *data) +{ + sizebuf_t send; + byte send_buf[MAX_MSGLEN + PACKET_HEADER]; + +// write the packet header + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 0; + + MSG_WriteLong (&send, -1); // -1 sequence means out of band + SZ_Write (&send, data, length); + +// send the datagram + //zoid, no input in demo playback mode +#ifndef SERVERONLY + if (!cls.demoplayback) +#endif + NET_SendPacket (send.cursize, send.data, adr); +} + +/* +=============== +Netchan_OutOfBandPrint + +Sends a text message in an out-of-band datagram +================ +*/ +void Netchan_OutOfBandPrint (netadr_t adr, char *format, ...) +{ + va_list argptr; + static char string[8192]; // ??? why static? + + va_start (argptr, format); + vsprintf (string, format,argptr); + va_end (argptr); + + + Netchan_OutOfBand (adr, strlen(string), (byte *)string); +} + + +/* +============== +Netchan_Setup + +called to open a channel to a remote system +============== +*/ +void Netchan_Setup (netchan_t *chan, netadr_t adr, int qport) +{ + memset (chan, 0, sizeof(*chan)); + + chan->remote_address = adr; + chan->last_received = realtime; + + chan->message.data = chan->message_buf; + chan->message.allowoverflow = true; + chan->message.maxsize = sizeof(chan->message_buf); + + chan->qport = qport; + + chan->rate = 1.0/2500; +} + + +/* +=============== +Netchan_CanPacket + +Returns true if the bandwidth choke isn't active +================ +*/ +#define MAX_BACKUP 200 +qboolean Netchan_CanPacket (netchan_t *chan) +{ + if (chan->cleartime < realtime + MAX_BACKUP*chan->rate) + return true; + return false; +} + + +/* +=============== +Netchan_CanReliable + +Returns true if the bandwidth choke isn't +================ +*/ +qboolean Netchan_CanReliable (netchan_t *chan) +{ + if (chan->reliable_length) + return false; // waiting for ack + return Netchan_CanPacket (chan); +} + +#ifdef SERVERONLY +qboolean ServerPaused(void); +#endif + +/* +=============== +Netchan_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. +================ +*/ +void Netchan_Transmit (netchan_t *chan, int length, byte *data) +{ + sizebuf_t send; + byte send_buf[MAX_MSGLEN + PACKET_HEADER]; + qboolean send_reliable; + unsigned w1, w2; + int i; + +// check for message overflow + if (chan->message.overflowed) + { + chan->fatal_error = true; + Con_Printf ("%s:Outgoing message overflow\n" + , NET_AdrToString (chan->remote_address)); + return; + } + +// if the remote side dropped the last reliable message, resend it + send_reliable = false; + + if (chan->incoming_acknowledged > chan->last_reliable_sequence + && chan->incoming_reliable_acknowledged != chan->reliable_sequence) + send_reliable = true; + +// if the reliable transmit buffer is empty, copy the current message out + if (!chan->reliable_length && chan->message.cursize) + { + memcpy (chan->reliable_buf, chan->message_buf, chan->message.cursize); + chan->reliable_length = chan->message.cursize; + chan->message.cursize = 0; + chan->reliable_sequence ^= 1; + send_reliable = true; + } + +// write the packet header + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 0; + + w1 = chan->outgoing_sequence | (send_reliable<<31); + w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence<<31); + + chan->outgoing_sequence++; + + MSG_WriteLong (&send, w1); + MSG_WriteLong (&send, w2); + + // send the qport if we are a client +#ifndef SERVERONLY + MSG_WriteShort (&send, cls.qport); +#endif + +// copy the reliable message to the packet first + if (send_reliable) + { + SZ_Write (&send, chan->reliable_buf, chan->reliable_length); + chan->last_reliable_sequence = chan->outgoing_sequence; + } + +// add the unreliable part if space is available + if (send.maxsize - send.cursize >= length) + SZ_Write (&send, data, length); + +// send the datagram + i = chan->outgoing_sequence & (MAX_LATENT-1); + chan->outgoing_size[i] = send.cursize; + chan->outgoing_time[i] = realtime; + + //zoid, no input in demo playback mode +#ifndef SERVERONLY + if (!cls.demoplayback) +#endif + NET_SendPacket (send.cursize, send.data, chan->remote_address); + + if (chan->cleartime < realtime) + chan->cleartime = realtime + send.cursize*chan->rate; + else + chan->cleartime += send.cursize*chan->rate; +#ifdef SERVERONLY + if (ServerPaused()) + chan->cleartime = realtime; +#endif + + if (showpackets.value) + Con_Printf ("--> s=%i(%i) a=%i(%i) %i\n" + , chan->outgoing_sequence + , send_reliable + , chan->incoming_sequence + , chan->incoming_reliable_sequence + , send.cursize); + +} + +/* +================= +Netchan_Process + +called when the current net_message is from remote_address +modifies net_message so that it points to the packet payload +================= +*/ +qboolean Netchan_Process (netchan_t *chan) +{ + unsigned sequence, sequence_ack; + unsigned reliable_ack, reliable_message; +#ifdef SERVERONLY + int qport; +#endif + int i; + + if ( +#ifndef SERVERONLY + !cls.demoplayback && +#endif + !NET_CompareAdr (net_from, chan->remote_address)) + return false; + +// get sequence numbers + MSG_BeginReading (); + sequence = MSG_ReadLong (); + sequence_ack = MSG_ReadLong (); + + // read the qport if we are a server +#ifdef SERVERONLY + qport = MSG_ReadShort (); +#endif + + reliable_message = sequence >> 31; + reliable_ack = sequence_ack >> 31; + + sequence &= ~(1<<31); + sequence_ack &= ~(1<<31); + + if (showpackets.value) + Con_Printf ("<-- s=%i(%i) a=%i(%i) %i\n" + , sequence + , reliable_message + , sequence_ack + , reliable_ack + , net_message.cursize); + +// get a rate estimation +#if 0 + if (chan->outgoing_sequence - sequence_ack < MAX_LATENT) + { + int i; + double time, rate; + + i = sequence_ack & (MAX_LATENT - 1); + time = realtime - chan->outgoing_time[i]; + time -= 0.1; // subtract 100 ms + if (time <= 0) + { // gotta be a digital link for <100 ms ping + if (chan->rate > 1.0/5000) + chan->rate = 1.0/5000; + } + else + { + if (chan->outgoing_size[i] < 512) + { // only deal with small messages + rate = chan->outgoing_size[i]/time; + if (rate > 5000) + rate = 5000; + rate = 1.0/rate; + if (chan->rate > rate) + chan->rate = rate; + } + } + } +#endif + +// +// discard stale or duplicated packets +// + if (sequence <= (unsigned)chan->incoming_sequence) + { + if (showdrop.value) + Con_Printf ("%s:Out of order packet %i at %i\n" + , NET_AdrToString (chan->remote_address) + , sequence + , chan->incoming_sequence); + return false; + } + +// +// dropped packets don't keep the message from being used +// + net_drop = sequence - (chan->incoming_sequence+1); + if (net_drop > 0) + { + chan->drop_count += 1; + + if (showdrop.value) + Con_Printf ("%s:Dropped %i packets at %i\n" + , NET_AdrToString (chan->remote_address) + , sequence-(chan->incoming_sequence+1) + , sequence); + } + +// +// if the current outgoing reliable message has been acknowledged +// clear the buffer to make way for the next +// + if (reliable_ack == (unsigned)chan->reliable_sequence) + chan->reliable_length = 0; // it has been received + +// +// if this message contains a reliable message, bump incoming_reliable_sequence +// + chan->incoming_sequence = sequence; + chan->incoming_acknowledged = sequence_ack; + chan->incoming_reliable_acknowledged = reliable_ack; + if (reliable_message) + chan->incoming_reliable_sequence ^= 1; + +// +// the message can now be read from the current message pointer +// update statistics counters +// + chan->frame_latency = chan->frame_latency*OLD_AVG + + (chan->outgoing_sequence-sequence_ack)*(1.0-OLD_AVG); + chan->frame_rate = chan->frame_rate*OLD_AVG + + (realtime-chan->last_received)*(1.0-OLD_AVG); + chan->good_count += 1; + + chan->last_received = realtime; + + return true; +} + |
