quakeforge/libs/net/net_chan.c

384 lines
9.7 KiB
C
Raw Normal View History

/*
net_chan.c
(description)
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
2001-10-16 21:40:45 +00:00
#ifdef HAVE_UNISTD_H
2001-08-29 02:12:57 +00:00
# include <unistd.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdarg.h>
2001-08-29 02:12:57 +00:00
#include <time.h>
#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/msg.h"
2001-11-27 04:50:41 +00:00
#include "QF/sys.h"
2001-08-29 02:12:57 +00:00
#include "compat.h"
#include "netchan.h"
#define PACKET_HEADER 8
int net_nochoke;
int net_blocksend;
2004-02-19 23:06:47 +00:00
double *net_realtime;
cvar_t *showpackets;
cvar_t *showdrop;
cvar_t *qport;
void (*net_log_packet) (int length, const void *data, netadr_t to);
2001-08-29 02:12:57 +00:00
void
Netchan_Init (void)
{
int port;
// pick a port value that should be nice and random
2001-11-27 04:50:41 +00:00
port = Sys_TimeID ();
Cvar_SetValue (qport, port);
}
void
Netchan_Init_Cvars (void)
{
showpackets = Cvar_Get ("showpackets", "0", CVAR_NONE, NULL,
2001-08-29 02:12:57 +00:00
"Show all network packets");
showdrop = Cvar_Get ("showdrop", "0", CVAR_NONE, NULL, "Toggle the "
"display of how many packets you are dropping");
2001-10-16 21:40:45 +00:00
qport = Cvar_Get ("qport", "0", CVAR_NONE, NULL, "The internal port "
"number for the game networking code. Useful for "
"clients who use multiple connections through one "
"IP address (NAT/IP-MASQ) because default port is "
"random.");
}
/*
Netchan_OutOfBand
Sends an out-of-band datagram
*/
void
Netchan_OutOfBand (netadr_t adr, unsigned length, byte * data)
{
byte send_buf[MAX_MSGLEN + PACKET_HEADER];
2001-08-29 02:12:57 +00:00
sizebuf_t send;
2001-08-29 02:12:57 +00:00
// 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);
2001-08-29 02:12:57 +00:00
// send the datagram
// zoid, no input in demo playback mode
if (!net_blocksend)
Netchan_SendPacket (send.cursize, send.data, adr);
}
/*
Netchan_OutOfBandPrint
Sends a text message in an out-of-band datagram
*/
void
2001-07-15 07:04:17 +00:00
Netchan_OutOfBandPrint (netadr_t adr, const char *format, ...)
{
static dstring_t *string;
2001-08-29 02:12:57 +00:00
va_list argptr;
if (!string)
string = dstring_new ();
va_start (argptr, format);
dvsprintf (string, format, argptr);
va_end (argptr);
Netchan_OutOfBand (adr, strlen (string->str), (byte *) string->str);
}
/*
Netchan_Setup
called to open a channel to a remote system
*/
void
Netchan_Setup (netchan_t *chan, netadr_t adr, int qport, ncqport_e flags)
{
memset (chan, 0, sizeof (*chan));
chan->remote_address = adr;
2004-02-19 23:06:47 +00:00
chan->last_received = *net_realtime;
chan->incoming_sequence = -1;
chan->message.data = chan->message_buf;
chan->message.allowoverflow = true;
chan->message.maxsize = sizeof (chan->message_buf);
chan->qport = qport;
chan->flags = flags;
chan->rate = 1.0 / 2500.0;
}
2001-08-29 02:12:57 +00:00
#define MAX_BACKUP 200
/*
Netchan_CanPacket
Returns true if the bandwidth choke isn't active
*/
qboolean
Netchan_CanPacket (netchan_t *chan)
{
2004-02-19 23:06:47 +00:00
if (chan->cleartime < *net_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);
}
void
Netchan_Transmit (netchan_t *chan, unsigned length, byte *data)
{
byte send_buf[MAX_MSGLEN + PACKET_HEADER];
int i;
2001-08-29 02:12:57 +00:00
unsigned int w1, w2;
qboolean send_reliable;
sizebuf_t send;
2001-08-29 02:12:57 +00:00
// check for message overflow
if (chan->message.overflowed) {
chan->fatal_error = true;
Sys_Printf ("%s:Outgoing message overflow\n",
NET_AdrToString (chan->remote_address));
return;
}
2001-08-29 02:12:57 +00:00
// 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;
2001-08-29 02:12:57 +00:00
// 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;
}
2001-08-29 02:12:57 +00:00
// 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 appropriate (we are a client).
if (chan->flags & NC_QPORT_SEND)
MSG_WriteShort (&send, chan->qport);
/// First copy the reliable message to the packet.
if (send_reliable) {
SZ_Write (&send, chan->reliable_buf, chan->reliable_length);
chan->last_reliable_sequence = chan->outgoing_sequence;
}
/// Then add the unreliable part if space is available.
if (send.maxsize - send.cursize >= length)
SZ_Write (&send, data, length);
/// Send the datagram if not blocked (in demo playback mode)
i = chan->outgoing_sequence & (MAX_LATENT - 1);
chan->outgoing_size[i] = send.cursize;
2004-02-19 23:06:47 +00:00
chan->outgoing_time[i] = *net_realtime;
// zoid, no input in demo playback mode
if (!net_blocksend)
Netchan_SendPacket (send.cursize, send.data, chan->remote_address);
2004-02-19 23:06:47 +00:00
if (chan->cleartime < *net_realtime)
chan->cleartime = *net_realtime + send.cursize * chan->rate;
else
chan->cleartime += send.cursize * chan->rate;
if (net_nochoke)
2004-02-19 23:06:47 +00:00
chan->cleartime = *net_realtime;
if (showpackets->int_val & 1) {
Sys_Printf ("--> s=%i(%i) a=%i(%i) %-4i %i\n",
chan->outgoing_sequence, send_reliable,
chan->incoming_sequence, chan->incoming_reliable_sequence,
send.cursize,
chan->outgoing_sequence - chan->incoming_sequence);
if (showpackets->int_val & 4) {
SZ_Dump (&send);
}
}
}
qboolean
Netchan_Process (netchan_t *chan)
{
2001-08-29 02:12:57 +00:00
unsigned int reliable_ack, reliable_message, sequence, sequence_ack;
if (!net_blocksend)
if (!NET_CompareAdr (net_from, chan->remote_address))
return false;
/// Get the sequence numbers.
MSG_BeginReading (net_message);
sequence = MSG_ReadLong (net_message);
sequence_ack = MSG_ReadLong (net_message);
/// Read the qport if appropriate (we are a server), but ignore it.
if (chan->flags & NC_QPORT_READ)
MSG_ReadShort (net_message);
reliable_message = sequence >> 31;
reliable_ack = sequence_ack >> 31;
sequence &= ~(1 << 31);
sequence_ack &= ~(1 << 31);
if (showpackets->int_val & 2) {
Sys_Printf ("<-- s=%i(%i) a=%i(%i) %i\n", sequence, reliable_message,
sequence_ack, reliable_ack, net_message->message->cursize);
if (showpackets->int_val & 8) {
SZ_Dump (net_message->message);
}
}
2001-08-29 02:12:57 +00:00
// get a rate estimation
2002-07-07 02:33:00 +00:00
#if 0 // FIXME: Dead code
if (chan->outgoing_sequence - sequence_ack < MAX_LATENT) {
int i;
double time, rate;
i = sequence_ack & (MAX_LATENT - 1);
2004-02-19 23:06:47 +00:00
time = *net_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) { // deal with only 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 int) chan->incoming_sequence + 1) {
if (showdrop->int_val)
Sys_Printf ("%s:Out of order packet %i at %i\n",
NET_AdrToString (chan->remote_address), sequence,
chan->incoming_sequence);
return false;
}
2001-08-29 02:12:57 +00:00
/// Dropped packets don't keep the message from being used.
chan->net_drop = sequence - (chan->incoming_sequence + 1);
if (chan->net_drop > 0) {
chan->drop_count += 1;
if (showdrop->int_val)
Sys_Printf ("%s:Dropped %i packets at %i\n",
NET_AdrToString (chan->remote_address),
2002-07-07 02:33:00 +00:00
sequence - (chan->incoming_sequence + 1), sequence);
}
2001-08-29 02:12:57 +00:00
/// If the current outgoing reliable message has been acknowledged,
/// clear the buffer to make way for the next.
if (reliable_ack == (unsigned int) 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
2004-02-19 23:06:47 +00:00
+ (*net_realtime - chan->last_received) * (1.0 - OLD_AVG);
chan->good_count += 1;
2004-02-19 23:06:47 +00:00
chan->last_received = *net_realtime;
return true;
}
2009-12-19 10:54:23 +00:00
void
Netchan_SendPacket (int length, const void *data, netadr_t to)
{
if (net_log_packet) {
net_log_packet (length, data, to);
}
2009-12-19 10:54:23 +00:00
NET_SendPacket (length, data, to);
}