quakeforge/libs/net/net_main.c
Bill Currie 5f93c115ff [util] Make developer flag names easier to manage
They're now an enum, and the flag part of the name is all lowercase, but
now the flag definitions and names list will never get out of sync.
2021-03-29 22:38:47 +09:00

861 lines
17 KiB
C

/*
net_main.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
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/cbuf.h"
#include "QF/cmd.h"
#include "QF/cvar.h"
#include "QF/msg.h"
#include "QF/qargs.h"
#include "QF/quakeio.h"
#include "QF/sizebuf.h"
#include "QF/sys.h"
#include "netmain.h"
#include "net_vcr.h"
#include "../nq/include/host.h"
#include "../nq/include/server.h"
qsocket_t *net_activeSockets = NULL;
qsocket_t *net_freeSockets = NULL;
int net_numsockets = 0;
qboolean tcpipAvailable = false;
int net_hostport;
int DEFAULTnet_hostport = 26000;
char my_tcpip_address[NET_NAMELEN];
static qboolean listening = false;
qboolean slistInProgress = false;
qboolean slistSilent = false;
qboolean slistLocal = true;
static double slistStartTime;
static int slistLastShown;
static void Slist_Send (void *);
static void Slist_Poll (void *);
PollProcedure slistSendProcedure = { NULL, 0.0, Slist_Send };
PollProcedure slistPollProcedure = { NULL, 0.0, Slist_Poll };
static sizebuf_t _net_message_message;
static qmsg_t _net_message = { 0, 0, &_net_message_message };
qmsg_t *net_message = &_net_message;
int net_activeconnections = 0;
int messagesSent = 0;
int messagesReceived = 0;
int unreliableMessagesSent = 0;
int unreliableMessagesReceived = 0;
cvar_t *net_messagetimeout;
cvar_t *hostname;
QFile *vcrFile;
qboolean recording = false;
// these two macros are to make the code more readable
#define sfunc net_drivers[sock->driver]
#define dfunc net_drivers[net_driverlevel]
int net_driverlevel;
double net_time;
double
SetNetTime (void)
{
net_time = Sys_DoubleTime ();
return net_time;
}
qsocket_t *
NET_NewQSocket (void)
{
qsocket_t *sock;
if (net_freeSockets == NULL)
return NULL;
if (net_activeconnections >= svs.maxclients)
return NULL;
// get one from free list
sock = net_freeSockets;
net_freeSockets = sock->next;
// add it to active list
sock->next = net_activeSockets;
net_activeSockets = sock;
sock->disconnected = false;
sock->connecttime = net_time;
strcpy (sock->address, "UNSET ADDRESS");
sock->driver = net_driverlevel;
sock->socket = 0;
sock->driverdata = NULL;
sock->canSend = true;
sock->sendNext = false;
sock->lastMessageTime = net_time;
sock->ackSequence = 0;
sock->sendSequence = 0;
sock->unreliableSendSequence = 0;
sock->sendMessageLength = 0;
sock->receiveSequence = 0;
sock->unreliableReceiveSequence = 0;
sock->receiveMessageLength = 0;
return sock;
}
void
NET_FreeQSocket (qsocket_t *sock)
{
qsocket_t *s;
// remove it from active list
if (sock == net_activeSockets)
net_activeSockets = net_activeSockets->next;
else {
for (s = net_activeSockets; s; s = s->next)
if (s->next == sock) {
s->next = sock->next;
break;
}
if (!s)
Sys_Error ("NET_FreeQSocket: not active");
}
// add it to free list
sock->next = net_freeSockets;
net_freeSockets = sock;
sock->disconnected = true;
}
static void
NET_Listen_f (void)
{
if (Cmd_Argc () != 2) {
Sys_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0);
return;
}
listening = atoi (Cmd_Argv (1)) ? true : false;
for (net_driverlevel = 0; net_driverlevel < net_numdrivers;
net_driverlevel++) {
if (net_drivers[net_driverlevel].initialized == false)
continue;
dfunc.Listen (listening);
}
}
static void
MaxPlayers_f (void)
{
int n;
if (Cmd_Argc () != 2) {
Sys_Printf ("\"maxplayers\" is \"%u\"\n", svs.maxclients);
return;
}
if (sv.active) {
Sys_Printf
("maxplayers can not be changed while a server is running.\n");
return;
}
n = atoi (Cmd_Argv (1));
if (n < 1)
n = 1;
if (n > svs.maxclientslimit) {
n = svs.maxclientslimit;
Sys_Printf ("\"maxplayers\" set to \"%u\"\n", n);
}
if ((n == 1) && listening)
Cbuf_AddText (host_cbuf, "listen 0\n");
if ((n > 1) && (!listening))
Cbuf_AddText (host_cbuf, "listen 1\n");
svs.maxclients = n;
if (n == 1)
Cvar_Set (deathmatch, "0");
else
Cvar_Set (deathmatch, "1");
}
static void
NET_Port_f (void)
{
int n;
if (Cmd_Argc () != 2) {
Sys_Printf ("\"port\" is \"%u\"\n", net_hostport);
return;
}
n = atoi (Cmd_Argv (1));
if (n < 1 || n > 65534) {
Sys_Printf ("Bad value, must be between 1 and 65534\n");
return;
}
DEFAULTnet_hostport = n;
net_hostport = n;
if (listening) {
// force a change to the new port
Cbuf_AddText (host_cbuf, "listen 0\n");
Cbuf_AddText (host_cbuf, "listen 1\n");
}
}
static void
PrintSlistHeader (void)
{
Sys_Printf ("Server Map Users\n");
Sys_Printf ("--------------- --------------- -----\n");
slistLastShown = 0;
}
static void
PrintSlist (void)
{
int n;
for (n = slistLastShown; n < hostCacheCount; n++) {
if (hostcache[n].maxusers)
Sys_Printf ("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name,
hostcache[n].map, hostcache[n].users,
hostcache[n].maxusers);
else
Sys_Printf ("%-15.15s %-15.15s\n", hostcache[n].name,
hostcache[n].map);
}
slistLastShown = n;
}
static void
PrintSlistTrailer (void)
{
if (hostCacheCount)
Sys_Printf ("== end list ==\n\n");
else
Sys_Printf ("No Quake servers found.\n\n");
}
static void
NET_Slist_f (void)
{
if (slistInProgress)
return;
if (!slistSilent) {
Sys_Printf ("Looking for Quake servers...\n");
PrintSlistHeader ();
}
slistInProgress = true;
slistStartTime = Sys_DoubleTime ();
SchedulePollProcedure (&slistSendProcedure, 0.0);
SchedulePollProcedure (&slistPollProcedure, 0.1);
hostCacheCount = 0;
}
static void
Slist_Send (void *unused)
{
for (net_driverlevel = 0; net_driverlevel < net_numdrivers;
net_driverlevel++) {
if (!slistLocal && net_driverlevel == 0)
continue;
if (net_drivers[net_driverlevel].initialized == false)
continue;
dfunc.SearchForHosts (true);
}
if ((Sys_DoubleTime () - slistStartTime) < 0.5)
SchedulePollProcedure (&slistSendProcedure, 0.75);
}
static void
Slist_Poll (void *unused)
{
for (net_driverlevel = 0; net_driverlevel < net_numdrivers;
net_driverlevel++) {
if (!slistLocal && net_driverlevel == 0)
continue;
if (net_drivers[net_driverlevel].initialized == false)
continue;
dfunc.SearchForHosts (false);
}
if (!slistSilent)
PrintSlist ();
if ((Sys_DoubleTime () - slistStartTime) < 1.5) {
SchedulePollProcedure (&slistPollProcedure, 0.1);
return;
}
if (!slistSilent)
PrintSlistTrailer ();
slistInProgress = false;
slistSilent = false;
slistLocal = true;
}
int hostCacheCount = 0;
hostcache_t hostcache[HOSTCACHESIZE];
qsocket_t *
NET_Connect (const char *host)
{
qsocket_t *ret;
int n;
int numdrivers = net_numdrivers;
SetNetTime ();
if (host && *host == 0)
host = NULL;
if (host) {
if (strcasecmp (host, "local") == 0) {
numdrivers = 1;
goto JustDoIt;
}
if (hostCacheCount) {
for (n = 0; n < hostCacheCount; n++)
if (strcasecmp (host, hostcache[n].name) == 0) {
host = hostcache[n].cname;
break;
}
if (n < hostCacheCount)
goto JustDoIt;
}
}
slistSilent = host ? true : false;
NET_Slist_f ();
while (slistInProgress)
NET_Poll ();
if (host == NULL) {
if (hostCacheCount != 1)
return NULL;
host = hostcache[0].cname;
Sys_Printf ("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
}
if (hostCacheCount)
for (n = 0; n < hostCacheCount; n++)
if (strcasecmp (host, hostcache[n].name) == 0) {
host = hostcache[n].cname;
break;
}
JustDoIt:
for (net_driverlevel = 0; net_driverlevel < numdrivers; net_driverlevel++) {
if (net_drivers[net_driverlevel].initialized == false)
continue;
ret = dfunc.Connect (host);
if (ret)
return ret;
}
if (host) {
Sys_Printf ("\n");
PrintSlistHeader ();
PrintSlist ();
PrintSlistTrailer ();
}
return NULL;
}
struct {
double time;
int op;
intptr_t session;
} vcrConnect;
qsocket_t *
NET_CheckNewConnections (void)
{
qsocket_t *ret;
SetNetTime ();
for (net_driverlevel = 0; net_driverlevel < net_numdrivers;
net_driverlevel++) {
if (net_drivers[net_driverlevel].initialized == false)
continue;
if (net_driverlevel && listening == false)
continue;
ret = dfunc.CheckNewConnections ();
if (ret) {
if (recording) {
vcrConnect.time = host_time;
vcrConnect.op = VCR_OP_CONNECT;
vcrConnect.session = (intptr_t) ret;
Qwrite (vcrFile, &vcrConnect, sizeof (vcrConnect));
Qwrite (vcrFile, ret->address, NET_NAMELEN);
}
return ret;
}
}
if (recording) {
vcrConnect.time = host_time;
vcrConnect.op = VCR_OP_CONNECT;
vcrConnect.session = 0;
Qwrite (vcrFile, &vcrConnect, sizeof (vcrConnect));
}
return NULL;
}
void
NET_Close (qsocket_t *sock)
{
if (!sock)
return;
if (sock->disconnected)
return;
SetNetTime ();
// call the driver_Close function
sfunc.Close (sock);
Sys_MaskPrintf (SYS_net, "closing socket\n");
NET_FreeQSocket (sock);
}
struct {
double time;
int op;
intptr_t session;
int ret;
int len;
} vcrGetMessage;
int
NET_GetMessage (qsocket_t *sock)
{
int ret;
if (!sock)
return -1;
if (sock->disconnected) {
Sys_Printf ("NET_GetMessage: disconnected socket\n");
return -1;
}
SetNetTime ();
ret = sfunc.QGetMessage (sock);
// see if this connection has timed out
if (ret == 0 && sock->driver) {
if (net_time - sock->lastMessageTime > net_messagetimeout->value) {
Sys_MaskPrintf (SYS_net, "socket timed out\n");
NET_Close (sock);
return -1;
}
}
if (ret > 0) {
if (sock->driver) {
sock->lastMessageTime = net_time;
if (ret == 1)
messagesReceived++;
else if (ret == 2)
unreliableMessagesReceived++;
}
if (recording) {
vcrGetMessage.time = host_time;
vcrGetMessage.op = VCR_OP_GETMESSAGE;
vcrGetMessage.session = (intptr_t) sock;
vcrGetMessage.ret = ret;
vcrGetMessage.len = _net_message_message.cursize;
Qwrite (vcrFile, &vcrGetMessage, 24);
Qwrite (vcrFile, _net_message_message.data,
_net_message_message.cursize);
}
} else {
if (recording) {
vcrGetMessage.time = host_time;
vcrGetMessage.op = VCR_OP_GETMESSAGE;
vcrGetMessage.session = (intptr_t) sock;
vcrGetMessage.ret = ret;
Qwrite (vcrFile, &vcrGetMessage, 20);
}
}
return ret;
}
struct {
double time;
int op;
intptr_t session;
int r;
} vcrSendMessage;
int
NET_SendMessage (qsocket_t *sock, sizebuf_t *data)
{
int r;
if (!sock)
return -1;
if (sock->disconnected) {
Sys_Printf ("NET_SendMessage: disconnected socket\n");
return -1;
}
SetNetTime ();
r = sfunc.QSendMessage (sock, data);
if (r == 1 && sock->driver)
messagesSent++;
if (recording) {
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_SENDMESSAGE;
vcrSendMessage.session = (intptr_t) sock;
vcrSendMessage.r = r;
Qwrite (vcrFile, &vcrSendMessage, 20);
}
return r;
}
int
NET_SendUnreliableMessage (qsocket_t *sock, sizebuf_t *data)
{
int r;
if (!sock)
return -1;
if (sock->disconnected) {
Sys_Printf ("NET_SendMessage: disconnected socket\n");
return -1;
}
SetNetTime ();
r = sfunc.SendUnreliableMessage (sock, data);
if (r == 1 && sock->driver)
unreliableMessagesSent++;
if (recording) {
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_SENDMESSAGE;
vcrSendMessage.session = (intptr_t) sock;
vcrSendMessage.r = r;
Qwrite (vcrFile, &vcrSendMessage, 20);
}
return r;
}
qboolean
NET_CanSendMessage (qsocket_t *sock)
{
int r;
if (!sock)
return false;
if (sock->disconnected)
return false;
SetNetTime ();
r = sfunc.CanSendMessage (sock);
if (recording) {
vcrSendMessage.time = host_time;
vcrSendMessage.op = VCR_OP_CANSENDMESSAGE;
vcrSendMessage.session = (intptr_t) sock;
vcrSendMessage.r = r;
Qwrite (vcrFile, &vcrSendMessage, 20);
}
return r;
}
int
NET_SendToAll (sizebuf_t *data, double blocktime)
{
double start;
int i;
int count = 0;
qboolean state1[MAX_SCOREBOARD]; /* can we send */
qboolean state2[MAX_SCOREBOARD]; /* did we send */
for (i = 0, host_client = svs.clients; i < svs.maxclients;
i++, host_client++) {
if (host_client->netconnection && host_client->active) {
if (host_client->netconnection->driver == 0) {
NET_SendMessage (host_client->netconnection, data);
state1[i] = true;
state2[i] = true;
continue;
}
count++;
state1[i] = false;
state2[i] = false;
} else {
state1[i] = true;
state2[i] = true;
}
}
start = Sys_DoubleTime ();
while (count) {
count = 0;
for (i = 0, host_client = svs.clients; i < svs.maxclients;
i++, host_client++) {
if (!state1[i]) {
if (NET_CanSendMessage (host_client->netconnection)) {
state1[i] = true;
NET_SendMessage (host_client->netconnection, data);
} else {
NET_GetMessage (host_client->netconnection);
}
count++;
continue;
}
if (!state2[i]) {
if (NET_CanSendMessage (host_client->netconnection)) {
state2[i] = true;
} else {
NET_GetMessage (host_client->netconnection);
}
count++;
continue;
}
}
if ((Sys_DoubleTime () - start) > blocktime)
break;
}
return count;
}
//=============================================================================
static void
NET_shutdown (void *data)
{
qsocket_t *sock;
SetNetTime ();
for (sock = net_activeSockets; sock; sock = sock->next)
NET_Close (sock);
//
// shutdown the drivers
//
for (net_driverlevel = 0; net_driverlevel < net_numdrivers;
net_driverlevel++) {
if (net_drivers[net_driverlevel].initialized == true) {
net_drivers[net_driverlevel].Shutdown ();
net_drivers[net_driverlevel].initialized = false;
}
}
if (vcrFile) {
Sys_Printf ("Closing vcrfile.\n");
Qclose (vcrFile);
}
}
void
NET_Init (void)
{
int i;
int controlSocket;
qsocket_t *s;
Sys_RegisterShutdown (NET_shutdown, 0);
if (COM_CheckParm ("-playback")) {
net_numdrivers = 1;
net_drivers[0].Init = VCR_Init;
}
if (COM_CheckParm ("-record"))
recording = true;
i = COM_CheckParm ("-port");
if (!i)
i = COM_CheckParm ("-udpport");
if (!i)
i = COM_CheckParm ("-ipxport");
if (i) {
if (i < com_argc - 1)
DEFAULTnet_hostport = atoi (com_argv[i + 1]);
else
Sys_Error ("NET_Init: you must specify a number after -port");
}
net_hostport = DEFAULTnet_hostport;
if (COM_CheckParm ("-listen") || cls.state == ca_dedicated)
listening = true;
net_numsockets = svs.maxclientslimit;
if (cls.state != ca_dedicated)
net_numsockets++;
SetNetTime ();
for (i = 0; i < net_numsockets; i++) {
s = (qsocket_t *) Hunk_AllocName (sizeof (qsocket_t), "qsocket");
s->next = net_freeSockets;
net_freeSockets = s;
s->disconnected = true;
}
// allocate space for network message buffer
SZ_Alloc (&_net_message_message, NET_MAXMESSAGE);
net_messagetimeout =
Cvar_Get ("net_messagetimeout", "300", CVAR_NONE, NULL, "None");
hostname = Cvar_Get ("hostname", "UNNAMED", CVAR_NONE, NULL, "None");
Cmd_AddCommand ("slist", NET_Slist_f, "No Description");
Cmd_AddCommand ("listen", NET_Listen_f, "No Description");
Cmd_AddCommand ("maxplayers", MaxPlayers_f, "No Description");
Cmd_AddCommand ("port", NET_Port_f, "No Description");
// initialize all the drivers
for (net_driverlevel = 0; net_driverlevel < net_numdrivers;
net_driverlevel++) {
controlSocket = net_drivers[net_driverlevel].Init ();
if (controlSocket == -1)
continue;
net_drivers[net_driverlevel].initialized = true;
net_drivers[net_driverlevel].controlSock = controlSocket;
if (listening)
net_drivers[net_driverlevel].Listen (true);
}
if (*my_tcpip_address)
Sys_MaskPrintf (SYS_net, "TCP/IP address %s\n", my_tcpip_address);
}
static PollProcedure *pollProcedureList = NULL;
void
NET_Poll (void)
{
PollProcedure *pp;
SetNetTime ();
for (pp = pollProcedureList; pp; pp = pp->next) {
if (pp->nextTime > net_time)
break;
pollProcedureList = pp->next;
pp->procedure (pp->arg);
}
}
void
SchedulePollProcedure (PollProcedure *proc, double timeOffset)
{
PollProcedure *pp, *prev;
proc->nextTime = Sys_DoubleTime () + timeOffset;
for (pp = pollProcedureList, prev = NULL; pp; pp = pp->next) {
if (pp->nextTime >= proc->nextTime)
break;
prev = pp;
}
if (prev == NULL) {
proc->next = pollProcedureList;
pollProcedureList = proc;
return;
}
proc->next = pp;
prev->next = proc;
}