/*
 * 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.
 *
 */
// net_main.c

#include "quakedef.h"
#include "net_vcr.h"

qsocket_t    *net_activeSockets = NULL;
qsocket_t    *net_freeSockets = NULL;
int           net_numsockets = 0;

qboolean      serialAvailable = false;
qboolean      ipxAvailable = false;
qboolean      tcpipAvailable = false;

int           net_hostport;
int           DEFAULTnet_hostport = 26000;

char          my_ipx_address[NET_NAMELEN];
char          my_tcpip_address[NET_NAMELEN];

void          (*GetComPortConfig) (int portNumber, int *port, int *irq, int *baud, qboolean * useModem);
void          (*SetComPortConfig) (int portNumber, int port, int irq, int baud, qboolean useModem);
void          (*GetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);
void          (*SetModemConfig) (int portNumber, char *dialType, char *clear, char *init, char *hangup);

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 };

sizebuf_t     net_message;
int           net_activeconnections = 0;

int           messagesSent = 0;
int           messagesReceived = 0;
int           unreliableMessagesSent = 0;
int           unreliableMessagesReceived = 0;

cvar_t        net_messagetimeout = { "net_messagetimeout", "300" };
cvar_t        net_connecttimeout = { "net_connecttimeout", "10" };      // joe:
                                                                        // qkick/qflood
                                                                        // protection
                                                                        // from
                                                                        // ProQuake
cvar_t        hostname = { "hostname", "UNNAMED" };
cvar_t        cl_password = { "cl_password", "" };      // joe: password
                                                        // protection from
                                                        // ProQuake
cvar_t        rcon_password = { "rcon_password", "" };  // joe: rcon password
                                                        // from ProQuake
cvar_t        rcon_server = { "rcon_server", "" };      // joe: rcon server
                                                        // from ProQuake
char          server_name[MAX_QPATH];  // joe: use the current server if
                                       // rcon_server is not set - ProQuake
                                       // stuff

// joe: rcon from ProQuake
#define RCON_BUFF_SIZE	8192
byte          rcon_buff[RCON_BUFF_SIZE];        // lxndr: char > byte
sizebuf_t     rcon_message = { false, false, rcon_buff, RCON_BUFF_SIZE, 0 };
qboolean      rcon_active = false;

qboolean      configRestored = false;
cvar_t        config_com_port = { "_config_com_port", "0x3f8", CVAR_ARCHIVE };
cvar_t        config_com_irq = { "_config_com_irq", "4", CVAR_ARCHIVE };
cvar_t        config_com_baud = { "_config_com_baud", "57600", CVAR_ARCHIVE };
cvar_t        config_com_modem = { "_config_com_modem", "1", CVAR_ARCHIVE };
cvar_t        config_modem_dialtype = { "_config_modem_dialtype", "T", CVAR_ARCHIVE };
cvar_t        config_modem_clear = { "_config_modem_clear", "ATZ", CVAR_ARCHIVE };
cvar_t        config_modem_init = { "_config_modem_init", "", CVAR_ARCHIVE };
cvar_t        config_modem_hangup = { "_config_modem_hangup", "AT H", CVAR_ARCHIVE };

#ifdef IDGODS
cvar_t        idgods = { "idgods", "0" };
#endif

FILE         *vcrFile = NULL;
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;

extern char  *argv[MAX_NUM_ARGVS];

double SetNetTime (void) {
    net_time = Sys_DoubleTime ();
    return net_time;
}

// joe, from ProQuake: need this for linux build
#ifndef WIN32
unsigned _lrotl (unsigned x, int s) {
    s &= 31;
    return (x << s) | (x >> (32 - s));
}
unsigned _lrotr (unsigned x, int s) {
    s &= 31;
    return (x >> s) | (x << (32 - s));
}
#endif

/*
 * NET_NewQSocket
 *
 * Called by drivers when a new communications endpoint is required The
 * sequence and buffer fields will be filled in properly
 */
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;
    Q_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\n");
    }

    // 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) {
        Con_Printf ("\"listen\" is \"%u\"\n", listening ? 1 : 0);
        return;
    }

    listening = Q_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) {
        Con_Printf ("\"maxplayers\" is \"%u\"\n", svs.maxclients);
        return;
    }

    if (sv.active) {
        Con_Printf ("maxplayers can not be changed while a server is running.\n");
        return;
    }

    n = Q_atoi (Cmd_Argv (1));
    if (n < 1)
        n = 1;
    if (n > svs.maxclientslimit) {
        n = svs.maxclientslimit;
        Con_Printf ("\"maxplayers\" set to \"%u\"\n", n);
    }

    if ((n == 1) && listening)
        Cbuf_AddText ("listen 0\n");

    if ((n > 1) && (!listening))
        Cbuf_AddText ("listen 1\n");

    svs.maxclients = n;
    if (n == 1)
        Cvar_SetValue (&deathmatch, 0);
    else
        Cvar_SetValue (&deathmatch, 1);
}

static void NET_Port_f (void) {
    int           n;

    if (Cmd_Argc () != 2) {
        Con_Printf ("\"port\" is \"%u\"\n", net_hostport);
        return;
    }

    n = Q_atoi (Cmd_Argv (1));
    if (n < 1 || n > 65534) {
        Con_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 ("listen 0\n");
        Cbuf_AddText ("listen 1\n");
    }
}

static void PrintSlistHeader (void) {
    Con_Printf ("Server          Map             Users\n");
    Con_Printf ("--------------- --------------- -----\n");
    slistLastShown = 0;
}

static void PrintSlist (void) {
    int           n;

    for (n = slistLastShown; n < hostCacheCount; n++) {
        if (hostcache[n].maxusers)
            Con_Printf ("%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users,
                        hostcache[n].maxusers);
        else
            Con_Printf ("%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
    }
    slistLastShown = n;
}

static void PrintSlistTrailer (void) {
    if (hostCacheCount)
        Con_Printf ("== end list ==\n\n");
    else
        Con_Printf ("No Quake servers found.\n\n");
}

void NET_Slist_f (void) {
    if (slistInProgress)
        return;

    if (!slistSilent) {
        Con_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) {
    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) {
    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];

/*
 * NET_Connect
 */
qsocket_t    *NET_Connect (char *host) {
    int           n, numdrivers = net_numdrivers;
    qsocket_t    *ret;

    SetNetTime ();

    if (host && *host == 0)
        host = NULL;

    if (host) {
        if (!Q_strcasecmp (host, "local")) {
            numdrivers = 1;
            goto JustDoIt;
        }

        if (hostCacheCount) {
            for (n = 0; n < hostCacheCount; n++)
                if (!Q_strcasecmp (host, hostcache[n].name)) {
                    host = hostcache[n].cname;
                    break;
                }
            if (n < hostCacheCount)
                goto JustDoIt;
        }
    }

    slistSilent = host ? true : false;
    NET_Slist_f ();

    while (slistInProgress)
        NET_Poll ();

    if (!host) {
        if (hostCacheCount != 1)
            return NULL;
        host = hostcache[0].cname;
        Con_Printf ("Connecting to...\n%s @ %s\n\n", hostcache[0].name, host);
    }

    if (hostCacheCount)
        for (n = 0; n < hostCacheCount; n++)
            if (!Q_strcasecmp (host, hostcache[n].name)) {
                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 (!sv.active && cl_cheatfree)
            Security_SetSeed (_lrotr (net_seed, 17), argv0);
        if (ret)
            return ret;
    }

    /*
     * joe: removed as in ProQuake if (host) { Con_Printf ("\n");
     * PrintSlistHeader (); PrintSlist (); PrintSlistTrailer (); }
     */

    return NULL;
}

struct {
    double        time;
    int           op;
    long          session;
} vcrConnect;

/*
 * NET_CheckNewConnections
 */
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;
        if ((ret = dfunc.CheckNewConnections ())) {
            if (recording) {
                vcrConnect.time = host_time;
                vcrConnect.op = VCR_OP_CONNECT;
                vcrConnect.session = (long) ret;
                fwrite (&vcrConnect, 1, sizeof (vcrConnect), vcrFile);
                fwrite (ret->address, 1, NET_NAMELEN, vcrFile);
            }

            return ret;
        }
    }

    if (recording) {
        vcrConnect.time = host_time;
        vcrConnect.op = VCR_OP_CONNECT;
        vcrConnect.session = 0;
        fwrite (&vcrConnect, 1, sizeof (vcrConnect), vcrFile);
    }

    return NULL;
}

/*
 * NET_Close
 */
void NET_Close (qsocket_t * sock) {
    if (!sock)
        return;

    if (sock->disconnected)
        return;

    SetNetTime ();

    // call the driver_Close function
    sfunc.Close (sock);

    NET_FreeQSocket (sock);
}

struct {
    double        time;
    int           op;
    long          session;
    int           ret;
    int           len;
} vcrGetMessage;

extern void   PrintStats (qsocket_t * s);

/*
 * NET_GetMessage
 *
 * If there is a complete message, return it in net_message
 *
 * returns 0 if no data is waiting returns 1 if a message was received returns
 * -1 if connection is invalid
 */
int NET_GetMessage (qsocket_t * sock) {
    int           ret;

    if (!sock)
        return -1;

    if (sock->disconnected) {
        Con_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) {
            NET_Close (sock);
            return -1;
        }
        // joe: qflood/qkick protection from ProQuake
        if (net_time - sock->lastMessageTime > net_connecttimeout.value && sv.active && host_client
            && sock == host_client->netconnection && !strcmp (host_client->name, "unconnected")) {
            NET_Close (sock);
            return -1;
        }
    }

    if (ret > 0) {
        // joe: cheat free from ProQuake
        if (cl_cheatfree && sock->mod != MOD_QSMACK && (sock->mod_version < 35 || sock->encrypt))
            Security_Decode (net_message.data, net_message.data, net_message.cursize, sock->client_port);

        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 = (long) sock;
            vcrGetMessage.ret = ret;
            vcrGetMessage.len = net_message.cursize;
            fwrite (&vcrGetMessage, 1, 24, vcrFile);
            fwrite (net_message.data, 1, net_message.cursize, vcrFile);
        }
    } else {
        if (recording) {
            vcrGetMessage.time = host_time;
            vcrGetMessage.op = VCR_OP_GETMESSAGE;
            vcrGetMessage.session = (long) sock;
            vcrGetMessage.ret = ret;
            fwrite (&vcrGetMessage, 1, 20, vcrFile);
        }
    }

    return ret;
}

struct {
    double        time;
    int           op;
    long          session;
    int           r;
} vcrSendMessage;

// joe: cheat free from ProQuake
byte          buff[NET_MAXMESSAGE];
sizebuf_t     newdata;

/*
 * NET_SendMessage
 *
 * Try to send a complete length+message unit over the reliable stream. returns
 * 0 if the message cannot be delivered reliably, but the connection is still
 * considered valid returns 1 if the message was sent properly returns -1 if
 * the connection died
 */
int NET_SendMessage (qsocket_t * sock, sizebuf_t * data) {
    int           r;

    if (!sock)
        return -1;

    if (sock->disconnected) {
        Con_Printf ("NET_SendMessage: disconnected socket\n");
        return -1;
    }
    // joe: cheat free from ProQuake
    if (cl_cheatfree && sock->mod != MOD_QSMACK) {
        if (sock->mod_version < 35 || sock->encrypt == 1 || sock->encrypt == 2) {
            Security_Encode (data->data, buff, data->cursize, sock->client_port);
            newdata.data = buff;
            newdata.cursize = data->cursize;
            data = &newdata;
        }
        if (sock->encrypt == 1)
            sock->encrypt = 0;
        else if (sock->encrypt == 3)
            sock->encrypt = 2;
    }

    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 = (long) sock;
        vcrSendMessage.r = r;
        fwrite (&vcrSendMessage, 1, 20, vcrFile);
    }

    return r;
}

int NET_SendUnreliableMessage (qsocket_t * sock, sizebuf_t * data) {
    int           r;

    if (!sock)
        return -1;

    if (sock->disconnected) {
        Con_Printf ("NET_SendMessage: disconnected socket\n");
        return -1;
    }
    // joe: cheat free from ProQuake
    if (cl_cheatfree && sock->mod != MOD_QSMACK) {
        if (sock->mod_version < 35 || sock->encrypt == 1 || sock->encrypt == 2) {
            Security_Encode (data->data, buff, data->cursize, sock->client_port);
            newdata.data = buff;
            newdata.cursize = data->cursize;
            data = &newdata;
        }
        if (sock->encrypt == 1)
            sock->encrypt = 0;
        else if (sock->encrypt == 3)
            sock->encrypt = 2;
    }

    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 = (long) sock;
        vcrSendMessage.r = r;
        fwrite (&vcrSendMessage, 1, 20, vcrFile);
    }

    return r;
}

/*
 * NET_CanSendMessage
 *
 * Returns true or false if the given qsocket can currently accept a message to
 * be transmitted.
 */
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 = (long) sock;
        vcrSendMessage.r = r;
        fwrite (&vcrSendMessage, 1, 20, vcrFile);
    }

    return r;
}

int NET_SendToAll (sizebuf_t * data, int blocktime) {
    int           i, count = 0;
    double        start;
    qboolean      state1[MAX_SCOREBOARD], state2[MAX_SCOREBOARD];

    for (i = 0, host_client = svs.clients; i < svs.maxclients; i++, host_client++) {
        if (!host_client->netconnection)
            continue;
        if (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;
}

// 

/*
 * NET_Init
 */
void NET_Init (void) {
    int           i, controlSocket;
    qsocket_t    *s;

    if (COM_CheckParm ("-playback")) {
        net_numdrivers = 1;
        net_drivers[0].Init = VCR_Init;
    }

    if (COM_CheckParm ("-record"))
        recording = true;

    if ((i = COM_CheckParm ("-port")) || (i = COM_CheckParm ("-udpport"))
        || (i = COM_CheckParm ("-ipxport"))) {
        if (i < com_argc - 1)
            DEFAULTnet_hostport = Q_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, NET_MAXMESSAGE);

    Cvar_Register (&net_messagetimeout);
    Cvar_Register (&net_connecttimeout);        // joe: qkick/qflood protection
    // from ProQuake
    Cvar_Register (&hostname);
    Cvar_Register (&cl_password);      // joe: password protection from
    // ProQuake
    Cvar_Register (&rcon_password);    // joe: rcon password from ProQuake
    Cvar_Register (&rcon_server);      // joe: rcon server from ProQuake
    Cvar_Register (&config_com_port);
    Cvar_Register (&config_com_irq);
    Cvar_Register (&config_com_baud);
    Cvar_Register (&config_com_modem);
    Cvar_Register (&config_modem_dialtype);
    Cvar_Register (&config_modem_clear);
    Cvar_Register (&config_modem_init);
    Cvar_Register (&config_modem_hangup);
#ifdef IDGODS
    Cvar_Register (&idgods);
#endif

    Cmd_AddCommand ("slist", NET_Slist_f);
    Cmd_AddCommand ("listen", NET_Listen_f);
    Cmd_AddCommand ("maxplayers", MaxPlayers_f);
    Cmd_AddCommand ("port", NET_Port_f);

    // 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_ipx_address)
        Con_DPrintf ("IPX address %s\n", my_ipx_address);

    if (*my_tcpip_address)
        Con_DPrintf ("TCP/IP address %s\n", my_tcpip_address);

    // joe: cheat free from ProQuake
    if (security_loaded) {
        net_seed = rand () ^ (rand () << 10) ^ (rand () << 20);
        net_seed &= 0x7fffffff;
        if (net_seed == 0x7fffffff)
            net_seed = 0;
        net_seed |= 1;
        if (net_seed <= 1)
            net_seed = 0x34719;
        Security_SetSeed (net_seed, argv0);
    }
}

/*
 * NET_Shutdown
 */
void NET_Shutdown (void) {
    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) {
        Con_Printf ("Closing vcrfile.\n");
        fclose (vcrFile);
    }
}

static PollProcedure *pollProcedureList = NULL;

void NET_Poll (void) {
    PollProcedure *pp;
    qboolean      useModem;

    if (!configRestored) {
        if (serialAvailable) {
            if (config_com_modem.value == 1.0)
                useModem = true;
            else
                useModem = false;
            SetComPortConfig (0, (int) config_com_port.value, (int) config_com_irq.value, (int) config_com_baud.value,
                              useModem);
            SetModemConfig (0, config_modem_dialtype.string, config_modem_clear.string, config_modem_init.string,
                            config_modem_hangup.string);
        }
        configRestored = true;
    }

    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;
}

#ifdef IDGODS
#  define IDNET	0xc0f62800

qboolean IsID (struct qsockaddr * addr) {
    if (idgods.value == 0.0)
        return false;

    if (addr->sa_family != 2)
        return false;

    if ((BigLong (*(int *) &addr->sa_data[2]) & 0xffffff00) == IDNET)
        return true;
    return false;
}
#endif
