mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-26 14:20:59 +00:00
12c84046f3
This is an extremely extensive patch as it hits every cvar, and every usage of the cvars. Cvars no longer store the value they control, instead, they use a cexpr value object to reference the value and specify the value's type (currently, a null type is used for strings). Non-string cvars are passed through cexpr, allowing expressions in the cvars' settings. Also, cvars have returned to an enhanced version of the original (id quake) registration scheme. As a minor benefit, relevant code having direct access to the cvar-controlled variables is probably a slight optimization as it removed a pointer dereference, and the variables can be located for data locality. The static cvar descriptors are made private as an additional safety layer, though there's nothing stopping external modification via Cvar_FindVar (which is needed for adding listeners). While not used yet (partly due to working out the design), cvars can have a validation function. Registering a cvar allows a primary listener (and its data) to be specified: it will always be called first when the cvar is modified. The combination of proper listeners and direct access to the controlled variable greatly simplifies the more complex cvar interactions as much less null checking is required, and there's no need for one cvar's callback to call another's. nq-x11 is known to work at least well enough for the demos. More testing will come.
931 lines
19 KiB
C
931 lines
19 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/server.h"
|
|
|
|
int net_is_dedicated = 0;
|
|
|
|
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;
|
|
unsigned net_activeconnections = 0;
|
|
|
|
int messagesSent = 0;
|
|
int messagesReceived = 0;
|
|
int unreliableMessagesSent = 0;
|
|
int unreliableMessagesReceived = 0;
|
|
|
|
float net_messagetimeout;
|
|
static cvar_t net_messagetimeout_cvar = {
|
|
.name = "net_messagetimeout",
|
|
.description =
|
|
"None",
|
|
.default_value = "300",
|
|
.flags = CVAR_NONE,
|
|
.value = { .type = &cexpr_float, .value = &net_messagetimeout },
|
|
};
|
|
char *hostname;
|
|
static cvar_t hostname_cvar = {
|
|
.name = "hostname",
|
|
.description =
|
|
"None",
|
|
.default_value = "UNNAMED",
|
|
.flags = CVAR_NONE,
|
|
.value = { .type = 0, .value = &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;
|
|
|
|
static int hostCacheCount = 0;
|
|
static hostcache_t hostcache[HOSTCACHESIZE];
|
|
|
|
static cbuf_t *net_cbuf;
|
|
|
|
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)
|
|
{
|
|
unsigned 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 (net_cbuf, "listen 0\n");
|
|
|
|
if ((n > 1) && (!listening))
|
|
Cbuf_AddText (net_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 (net_cbuf, "listen 0\n");
|
|
Cbuf_AddText (net_cbuf, "listen 1\n");
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
PrintSlistHeader (void)
|
|
{
|
|
Sys_Printf ("Server Map Users\n");
|
|
Sys_Printf ("--------------- --------------- -----\n");
|
|
slistLastShown = 0;
|
|
}
|
|
|
|
void
|
|
NET_AddCachedHost (const char *name, const char *map, const char *cname,
|
|
int users, int maxusers, int driver, int ldriver,
|
|
const netadr_t *addr)
|
|
{
|
|
if (hostCacheCount == HOSTCACHESIZE) {
|
|
return;
|
|
}
|
|
for (int i = 0; i < hostCacheCount; i++) {
|
|
// addr will be 0 for loopback, and there can be only one loopback
|
|
// server.
|
|
if (!addr || !memcmp (addr, &hostcache[i].addr, sizeof (netadr_t))) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const int namesize = sizeof (hostcache[0].name) - 1;
|
|
const int mapsize = sizeof (hostcache[0].map) - 1;
|
|
const int cnamesize = sizeof (hostcache[0].cname) - 1;
|
|
|
|
hostcache_t *host = &hostcache[hostCacheCount++];
|
|
strncpy (host->name, name, namesize);
|
|
strncpy (host->map, map, mapsize);
|
|
strncpy (host->cname, cname, cnamesize);
|
|
host->name[namesize] = 0;
|
|
host->map[mapsize] = 0;
|
|
host->cname[cnamesize] = 0;
|
|
|
|
host->users = users;
|
|
host->maxusers = maxusers;
|
|
host->driver = driver;
|
|
host->ldriver = ldriver;
|
|
if (addr) {
|
|
host->addr = *addr;
|
|
} else {
|
|
memset (&host->addr, 0, sizeof (host->addr));
|
|
}
|
|
|
|
// check for and resolve name conflicts
|
|
for (int i = 0; i < hostCacheCount - 1; i++) {
|
|
if (strcasecmp (host->name, hostcache[i].name) == 0) {
|
|
int len = strlen (host->name);
|
|
if (len < namesize && host->name[len - 1] > '8') {
|
|
host->name[len] = '0';
|
|
host->name[len + 1] = 0;
|
|
} else {
|
|
host->name[len - 1]++;
|
|
}
|
|
i = -1; // restart loop
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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) {
|
|
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;
|
|
unsigned 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 (cbuf_t *cbuf)
|
|
{
|
|
int i;
|
|
int controlSocket;
|
|
qsocket_t *s;
|
|
|
|
net_cbuf = cbuf;
|
|
|
|
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") || net_is_dedicated)
|
|
listening = true;
|
|
net_numsockets = svs.maxclientslimit;
|
|
if (!net_is_dedicated)
|
|
net_numsockets++;
|
|
|
|
SetNetTime ();
|
|
|
|
for (i = 0; i < net_numsockets; i++) {
|
|
s = (qsocket_t *) Hunk_AllocName (0, 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);
|
|
|
|
Cvar_Register (&net_messagetimeout_cvar, 0, 0);
|
|
Cvar_Register (&hostname_cvar, 0, 0);
|
|
|
|
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;
|
|
}
|