Annotation of quake2/server/sv_main.c, revision 1.1.1.4

1.1.1.4 ! root        1: /*
        !             2: Copyright (C) 1997-2001 Id Software, Inc.
        !             3: 
        !             4: This program is free software; you can redistribute it and/or
        !             5: modify it under the terms of the GNU General Public License
        !             6: as published by the Free Software Foundation; either version 2
        !             7: of the License, or (at your option) any later version.
        !             8: 
        !             9: This program is distributed in the hope that it will be useful,
        !            10: but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
        !            12: 
        !            13: See the GNU General Public License for more details.
        !            14: 
        !            15: You should have received a copy of the GNU General Public License
        !            16: along with this program; if not, write to the Free Software
        !            17: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
        !            18: 
        !            19: */
1.1       root       20: 
                     21: #include "server.h"
                     22: 
                     23: netadr_t       master_adr[MAX_MASTERS];        // address of group servers
                     24: 
                     25: client_t       *sv_client;                     // current client
                     26: 
                     27: cvar_t *sv_paused;
                     28: cvar_t *sv_timedemo;
                     29: 
                     30: cvar_t *sv_enforcetime;
                     31: 
                     32: cvar_t *timeout;                               // seconds without any message
                     33: cvar_t *zombietime;                    // seconds to sink messages after disconnect
                     34: 
                     35: cvar_t *rcon_password;                 // password for remote server commands
                     36: 
                     37: cvar_t *allow_download;
1.1.1.2   root       38: cvar_t *allow_download_players;
                     39: cvar_t *allow_download_models;
                     40: cvar_t *allow_download_sounds;
                     41: cvar_t *allow_download_maps;
1.1       root       42: 
1.1.1.3   root       43: cvar_t *sv_airaccelerate;
                     44: 
1.1       root       45: cvar_t *sv_noreload;                   // don't reload level state when reentering
                     46: 
                     47: cvar_t *maxclients;                    // FIXME: rename sv_maxclients
                     48: cvar_t *sv_showclamp;
                     49: 
                     50: cvar_t *hostname;
                     51: cvar_t *public_server;                 // should heartbeats be sent
                     52: 
                     53: cvar_t *sv_reconnect_limit;    // minimum seconds between connect messages
                     54: 
                     55: void Master_Shutdown (void);
                     56: 
                     57: 
                     58: //============================================================================
                     59: 
                     60: 
                     61: /*
                     62: =====================
                     63: SV_DropClient
                     64: 
                     65: Called when the player is totally leaving the server, either willingly
                     66: or unwillingly.  This is NOT called if the entire server is quiting
                     67: or crashing.
                     68: =====================
                     69: */
                     70: void SV_DropClient (client_t *drop)
                     71: {
                     72:        // add the disconnect
                     73:        MSG_WriteByte (&drop->netchan.message, svc_disconnect);
                     74: 
                     75:        if (drop->state == cs_spawned)
                     76:        {
                     77:                // call the prog function for removing a client
                     78:                // this will remove the body, among other things
                     79:                ge->ClientDisconnect (drop->edict);
                     80:        }
                     81: 
                     82:        if (drop->download)
                     83:        {
                     84:                FS_FreeFile (drop->download);
                     85:                drop->download = NULL;
                     86:        }
                     87: 
                     88:        drop->state = cs_zombie;                // become free in a few seconds
                     89:        drop->name[0] = 0;
                     90: }
                     91: 
                     92: 
                     93: 
                     94: /*
                     95: ==============================================================================
                     96: 
                     97: CONNECTIONLESS COMMANDS
                     98: 
                     99: ==============================================================================
                    100: */
                    101: 
                    102: /*
                    103: ===============
                    104: SV_StatusString
                    105: 
                    106: Builds the string that is sent as heartbeats and status replies
                    107: ===============
                    108: */
                    109: char   *SV_StatusString (void)
                    110: {
                    111:        char    player[1024];
1.1.1.3   root      112:        static char     status[MAX_MSGLEN - 16];
1.1       root      113:        int             i;
                    114:        client_t        *cl;
                    115:        int             statusLength;
                    116:        int             playerLength;
                    117: 
                    118:        strcpy (status, Cvar_Serverinfo());
                    119:        strcat (status, "\n");
                    120:        statusLength = strlen(status);
                    121: 
                    122:        for (i=0 ; i<maxclients->value ; i++)
                    123:        {
                    124:                cl = &svs.clients[i];
                    125:                if (cl->state == cs_connected || cl->state == cs_spawned )
                    126:                {
                    127:                        Com_sprintf (player, sizeof(player), "%i %i \"%s\"\n", 
                    128:                                cl->edict->client->ps.stats[STAT_FRAGS], cl->ping, cl->name);
                    129:                        playerLength = strlen(player);
                    130:                        if (statusLength + playerLength >= sizeof(status) )
                    131:                                break;          // can't hold any more
                    132:                        strcpy (status + statusLength, player);
                    133:                        statusLength += playerLength;
                    134:                }
                    135:        }
                    136: 
                    137:        return status;
                    138: }
                    139: 
                    140: /*
                    141: ================
                    142: SVC_Status
                    143: 
                    144: Responds with all the info that qplug or qspy can see
                    145: ================
                    146: */
                    147: void SVC_Status (void)
                    148: {
1.1.1.3   root      149:        Netchan_OutOfBandPrint (NS_SERVER, net_from, "print\n%s", SV_StatusString());
                    150: #if 0
1.1       root      151:        Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
                    152:        Com_Printf (SV_StatusString());
                    153:        Com_EndRedirect ();
1.1.1.3   root      154: #endif
1.1       root      155: }
                    156: 
                    157: /*
                    158: ================
                    159: SVC_Ack
                    160: 
                    161: ================
                    162: */
                    163: void SVC_Ack (void)
                    164: {
                    165:        Com_Printf ("Ping acknowledge from %s\n", NET_AdrToString(net_from));
                    166: }
                    167: 
                    168: /*
                    169: ================
                    170: SVC_Info
                    171: 
                    172: Responds with short info for broadcast scans
                    173: The second parameter should be the current protocol version number.
                    174: ================
                    175: */
                    176: void SVC_Info (void)
                    177: {
                    178:        char    string[64];
                    179:        int             i, count;
                    180:        int             version;
                    181: 
                    182:        if (maxclients->value == 1)
                    183:                return;         // ignore in single player
                    184: 
                    185:        version = atoi (Cmd_Argv(1));
                    186: 
                    187:        if (version != PROTOCOL_VERSION)
                    188:                Com_sprintf (string, sizeof(string), "%s: wrong version\n", hostname->string, sizeof(string));
                    189:        else
                    190:        {
                    191:                count = 0;
                    192:                for (i=0 ; i<maxclients->value ; i++)
                    193:                        if (svs.clients[i].state >= cs_connected)
                    194:                                count++;
                    195: 
                    196:                Com_sprintf (string, sizeof(string), "%16s %8s %2i/%2i\n", hostname->string, sv.name, count, (int)maxclients->value);
                    197:        }
                    198: 
                    199:        Netchan_OutOfBandPrint (NS_SERVER, net_from, "info\n%s", string);
                    200: }
                    201: 
                    202: /*
                    203: ================
                    204: SVC_Ping
                    205: 
                    206: Just responds with an acknowledgement
                    207: ================
                    208: */
                    209: void SVC_Ping (void)
                    210: {
                    211:        Netchan_OutOfBandPrint (NS_SERVER, net_from, "ack");
                    212: }
                    213: 
                    214: 
                    215: /*
                    216: =================
                    217: SVC_GetChallenge
                    218: 
                    219: Returns a challenge number that can be used
                    220: in a subsequent client_connect command.
                    221: We do this to prevent denial of service attacks that
                    222: flood the server with invalid connection IPs.  With a
                    223: challenge, they must give a valid IP address.
                    224: =================
                    225: */
                    226: void SVC_GetChallenge (void)
                    227: {
                    228:        int             i;
                    229:        int             oldest;
                    230:        int             oldestTime;
                    231: 
                    232:        oldest = 0;
                    233:        oldestTime = 0x7fffffff;
                    234: 
                    235:        // see if we already have a challenge for this ip
                    236:        for (i = 0 ; i < MAX_CHALLENGES ; i++)
                    237:        {
                    238:                if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
                    239:                        break;
                    240:                if (svs.challenges[i].time < oldestTime)
                    241:                {
                    242:                        oldestTime = svs.challenges[i].time;
                    243:                        oldest = i;
                    244:                }
                    245:        }
                    246: 
                    247:        if (i == MAX_CHALLENGES)
                    248:        {
                    249:                // overwrite the oldest
1.1.1.2   root      250:                svs.challenges[oldest].challenge = rand() & 0x7fff;
1.1       root      251:                svs.challenges[oldest].adr = net_from;
                    252:                svs.challenges[oldest].time = curtime;
                    253:                i = oldest;
                    254:        }
                    255: 
                    256:        // send it back
                    257:        Netchan_OutOfBandPrint (NS_SERVER, net_from, "challenge %i", svs.challenges[i].challenge);
                    258: }
                    259: 
                    260: /*
                    261: ==================
                    262: SVC_DirectConnect
                    263: 
                    264: A connection request that did not come from the master
                    265: ==================
                    266: */
                    267: void SVC_DirectConnect (void)
                    268: {
                    269:        char            userinfo[MAX_INFO_STRING];
                    270:        netadr_t        adr;
                    271:        int                     i;
                    272:        client_t        *cl, *newcl;
                    273:        client_t        temp;
                    274:        edict_t         *ent;
                    275:        int                     edictnum;
                    276:        int                     version;
                    277:        int                     qport;
                    278:        int                     challenge;
                    279: 
                    280:        adr = net_from;
                    281: 
                    282:        Com_DPrintf ("SVC_DirectConnect ()\n");
                    283: 
                    284:        version = atoi(Cmd_Argv(1));
                    285:        if (version != PROTOCOL_VERSION)
                    286:        {
                    287:                Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is version %4.2f.\n", VERSION);
                    288:                Com_DPrintf ("    rejected connect from version %i\n", version);
                    289:                return;
                    290:        }
                    291: 
                    292:        qport = atoi(Cmd_Argv(2));
                    293: 
                    294:        challenge = atoi(Cmd_Argv(3));
                    295: 
                    296:        strncpy (userinfo, Cmd_Argv(4), sizeof(userinfo)-1);
1.1.1.2   root      297:        userinfo[sizeof(userinfo) - 1] = 0;
1.1       root      298: 
                    299:        // force the IP key/value pair so the game can filter based on ip
                    300:        Info_SetValueForKey (userinfo, "ip", NET_AdrToString(net_from));
                    301: 
                    302:        // attractloop servers are ONLY for local clients
                    303:        if (sv.attractloop)
                    304:        {
                    305:                if (!NET_IsLocalAddress (adr))
                    306:                {
                    307:                        Com_Printf ("Remote connect in attract loop.  Ignored.\n");
                    308:                        Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n");
                    309:                        return;
                    310:                }
                    311:        }
                    312: 
                    313:        // see if the challenge is valid
                    314:        if (!NET_IsLocalAddress (adr))
                    315:        {
                    316:                for (i=0 ; i<MAX_CHALLENGES ; i++)
                    317:                {
                    318:                        if (NET_CompareBaseAdr (net_from, svs.challenges[i].adr))
                    319:                        {
                    320:                                if (challenge == svs.challenges[i].challenge)
                    321:                                        break;          // good
                    322:                                Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nBad challenge.\n");
                    323:                                return;
                    324:                        }
                    325:                }
                    326:                if (i == MAX_CHALLENGES)
                    327:                {
                    328:                        Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nNo challenge for address.\n");
                    329:                        return;
                    330:                }
                    331:        }
                    332: 
                    333:        newcl = &temp;
                    334:        memset (newcl, 0, sizeof(client_t));
                    335: 
                    336:        // if there is already a slot for this ip, reuse it
                    337:        for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
                    338:        {
                    339:                if (cl->state == cs_free)
                    340:                        continue;
                    341:                if (NET_CompareBaseAdr (adr, cl->netchan.remote_address)
                    342:                        && ( cl->netchan.qport == qport 
                    343:                        || adr.port == cl->netchan.remote_address.port ) )
                    344:                {
1.1.1.2   root      345:                        if (!NET_IsLocalAddress (adr) && (svs.realtime - cl->lastconnect) < ((int)sv_reconnect_limit->value * 1000))
1.1       root      346:                        {
                    347:                                Com_DPrintf ("%s:reconnect rejected : too soon\n", NET_AdrToString (adr));
                    348:                                return;
                    349:                        }
                    350:                        Com_Printf ("%s:reconnect\n", NET_AdrToString (adr));
                    351:                        newcl = cl;
                    352:                        goto gotnewcl;
                    353:                }
                    354:        }
                    355: 
                    356:        // find a client slot
                    357:        newcl = NULL;
                    358:        for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
                    359:        {
                    360:                if (cl->state == cs_free)
                    361:                {
                    362:                        newcl = cl;
                    363:                        break;
                    364:                }
                    365:        }
                    366:        if (!newcl)
                    367:        {
                    368:                Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nServer is full.\n");
                    369:                Com_DPrintf ("Rejected a connection.\n");
                    370:                return;
                    371:        }
                    372: 
                    373: gotnewcl:      
                    374:        // build a new connection
                    375:        // accept the new client
                    376:        // this is the only place a client_t is ever initialized
                    377:        *newcl = temp;
                    378:        sv_client = newcl;
                    379:        edictnum = (newcl-svs.clients)+1;
                    380:        ent = EDICT_NUM(edictnum);
                    381:        newcl->edict = ent;
1.1.1.2   root      382:        newcl->challenge = challenge; // save challenge for checksumming
1.1       root      383: 
                    384:        // get the game a chance to reject this connection or modify the userinfo
                    385:        if (!(ge->ClientConnect (ent, userinfo)))
                    386:        {
1.1.1.3   root      387:                if (*Info_ValueForKey (userinfo, "rejmsg")) 
                    388:                        Netchan_OutOfBandPrint (NS_SERVER, adr, "print\n%s\nConnection refused.\n",  
                    389:                                Info_ValueForKey (userinfo, "rejmsg"));
                    390:                else
                    391:                        Netchan_OutOfBandPrint (NS_SERVER, adr, "print\nConnection refused.\n" );
1.1       root      392:                Com_DPrintf ("Game rejected a connection.\n");
                    393:                return;
                    394:        }
                    395: 
                    396:        // parse some info from the info strings
                    397:        strncpy (newcl->userinfo, userinfo, sizeof(newcl->userinfo)-1);
                    398:        SV_UserinfoChanged (newcl);
                    399: 
                    400:        // send the connect packet to the client
                    401:        Netchan_OutOfBandPrint (NS_SERVER, adr, "client_connect");
                    402: 
                    403:        Netchan_Setup (NS_SERVER, &newcl->netchan , adr, qport);
                    404: 
                    405:        newcl->state = cs_connected;
                    406:        
                    407:        SZ_Init (&newcl->datagram, newcl->datagram_buf, sizeof(newcl->datagram_buf) );
                    408:        newcl->datagram.allowoverflow = true;
                    409:        newcl->lastmessage = svs.realtime;      // don't timeout
                    410:        newcl->lastconnect = svs.realtime;
                    411: }
                    412: 
                    413: int Rcon_Validate (void)
                    414: {
                    415:        if (!strlen (rcon_password->string))
                    416:                return 0;
                    417: 
                    418:        if (strcmp (Cmd_Argv(1), rcon_password->string) )
                    419:                return 0;
                    420: 
                    421:        return 1;
                    422: }
                    423: 
                    424: /*
                    425: ===============
                    426: SVC_RemoteCommand
                    427: 
                    428: A client issued an rcon command.
                    429: Shift down the remaining args
                    430: Redirect all printfs
                    431: ===============
                    432: */
                    433: void SVC_RemoteCommand (void)
                    434: {
                    435:        int             i;
                    436:        char    remaining[1024];
                    437: 
                    438:        i = Rcon_Validate ();
                    439: 
                    440:        if (i == 0)
                    441:                Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
1.1.1.2   root      442:        else
1.1       root      443:                Com_Printf ("Rcon from %s:\n%s\n", NET_AdrToString (net_from), net_message.data+4);
                    444: 
                    445:        Com_BeginRedirect (RD_PACKET, sv_outputbuf, SV_OUTPUTBUF_LENGTH, SV_FlushRedirect);
                    446: 
                    447:        if (!Rcon_Validate ())
                    448:        {
                    449:                Com_Printf ("Bad rcon_password.\n");
                    450:        }
                    451:        else
                    452:        {
                    453:                remaining[0] = 0;
                    454: 
                    455:                for (i=2 ; i<Cmd_Argc() ; i++)
                    456:                {
                    457:                        strcat (remaining, Cmd_Argv(i) );
                    458:                        strcat (remaining, " ");
                    459:                }
                    460: 
                    461:                Cmd_ExecuteString (remaining);
                    462:        }
                    463: 
                    464:        Com_EndRedirect ();
                    465: }
                    466: 
                    467: /*
                    468: =================
                    469: SV_ConnectionlessPacket
                    470: 
                    471: A connectionless packet has four leading 0xff
                    472: characters to distinguish it from a game channel.
                    473: Clients that are in the game can still send
                    474: connectionless packets.
                    475: =================
                    476: */
                    477: void SV_ConnectionlessPacket (void)
                    478: {
                    479:        char    *s;
                    480:        char    *c;
                    481: 
                    482:        MSG_BeginReading (&net_message);
                    483:        MSG_ReadLong (&net_message);            // skip the -1 marker
                    484: 
                    485:        s = MSG_ReadStringLine (&net_message);
                    486: 
                    487:        Cmd_TokenizeString (s, false);
                    488: 
                    489:        c = Cmd_Argv(0);
                    490:        Com_DPrintf ("Packet %s : %s\n", NET_AdrToString(net_from), c);
                    491: 
                    492:        if (!strcmp(c, "ping"))
                    493:                SVC_Ping ();
                    494:        else if (!strcmp(c, "ack"))
                    495:                SVC_Ack ();
                    496:        else if (!strcmp(c,"status"))
                    497:                SVC_Status ();
                    498:        else if (!strcmp(c,"info"))
                    499:                SVC_Info ();
                    500:        else if (!strcmp(c,"getchallenge"))
                    501:                SVC_GetChallenge ();
                    502:        else if (!strcmp(c,"connect"))
                    503:                SVC_DirectConnect ();
                    504:        else if (!strcmp(c, "rcon"))
                    505:                SVC_RemoteCommand ();
                    506:        else
                    507:                Com_Printf ("bad connectionless packet from %s:\n%s\n"
                    508:                , NET_AdrToString (net_from), s);
                    509: }
                    510: 
                    511: 
                    512: //============================================================================
                    513: 
                    514: /*
                    515: ===================
                    516: SV_CalcPings
                    517: 
                    518: Updates the cl->ping variables
                    519: ===================
                    520: */
                    521: void SV_CalcPings (void)
                    522: {
                    523:        int                     i, j;
                    524:        client_t        *cl;
                    525:        int                     total, count;
                    526: 
                    527:        for (i=0 ; i<maxclients->value ; i++)
                    528:        {
                    529:                cl = &svs.clients[i];
                    530:                if (cl->state != cs_spawned )
                    531:                        continue;
                    532: 
1.1.1.2   root      533: #if 0
1.1       root      534:                if (cl->lastframe > 0)
                    535:                        cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = sv.framenum - cl->lastframe + 1;
                    536:                else
                    537:                        cl->frame_latency[sv.framenum&(LATENCY_COUNTS-1)] = 0;
1.1.1.2   root      538: #endif
1.1       root      539: 
                    540:                total = 0;
                    541:                count = 0;
                    542:                for (j=0 ; j<LATENCY_COUNTS ; j++)
                    543:                {
                    544:                        if (cl->frame_latency[j] > 0)
                    545:                        {
                    546:                                count++;
                    547:                                total += cl->frame_latency[j];
                    548:                        }
                    549:                }
                    550:                if (!count)
                    551:                        cl->ping = 0;
                    552:                else
1.1.1.2   root      553: #if 0
1.1       root      554:                        cl->ping = total*100/count - 100;
1.1.1.2   root      555: #else
                    556:                        cl->ping = total / count;
                    557: #endif
1.1       root      558: 
                    559:                // let the game dll know about the ping
                    560:                cl->edict->client->ping = cl->ping;
                    561:        }
                    562: }
                    563: 
                    564: 
                    565: /*
                    566: ===================
                    567: SV_GiveMsec
                    568: 
                    569: Every few frames, gives all clients an allotment of milliseconds
                    570: for their command moves.  If they exceed it, assume cheating.
                    571: ===================
                    572: */
                    573: void SV_GiveMsec (void)
                    574: {
                    575:        int                     i;
                    576:        client_t        *cl;
                    577: 
                    578:        if (sv.framenum & 15)
                    579:                return;
                    580: 
                    581:        for (i=0 ; i<maxclients->value ; i++)
                    582:        {
                    583:                cl = &svs.clients[i];
                    584:                if (cl->state == cs_free )
                    585:                        continue;
                    586:                
                    587:                cl->commandMsec = 1800;         // 1600 + some slop
                    588:        }
                    589: }
                    590: 
                    591: 
                    592: /*
                    593: =================
                    594: SV_ReadPackets
                    595: =================
                    596: */
                    597: void SV_ReadPackets (void)
                    598: {
                    599:        int                     i;
                    600:        client_t        *cl;
                    601:        int                     qport;
                    602: 
                    603:        while (NET_GetPacket (NS_SERVER, &net_from, &net_message))
                    604:        {
                    605:                // check for connectionless packet (0xffffffff) first
                    606:                if (*(int *)net_message.data == -1)
                    607:                {
                    608:                        SV_ConnectionlessPacket ();
                    609:                        continue;
                    610:                }
                    611: 
                    612:                // read the qport out of the message so we can fix up
                    613:                // stupid address translating routers
                    614:                MSG_BeginReading (&net_message);
                    615:                MSG_ReadLong (&net_message);            // sequence number
                    616:                MSG_ReadLong (&net_message);            // sequence number
                    617:                qport = MSG_ReadShort (&net_message) & 0xffff;
                    618: 
                    619:                // check for packets from connected clients
                    620:                for (i=0, cl=svs.clients ; i<maxclients->value ; i++,cl++)
                    621:                {
                    622:                        if (cl->state == cs_free)
                    623:                                continue;
                    624:                        if (!NET_CompareBaseAdr (net_from, cl->netchan.remote_address))
                    625:                                continue;
                    626:                        if (cl->netchan.qport != qport)
                    627:                                continue;
                    628:                        if (cl->netchan.remote_address.port != net_from.port)
                    629:                        {
                    630:                                Com_Printf ("SV_ReadPackets: fixing up a translated port\n");
                    631:                                cl->netchan.remote_address.port = net_from.port;
                    632:                        }
                    633: 
                    634:                        if (Netchan_Process(&cl->netchan, &net_message))
                    635:                        {       // this is a valid, sequenced packet, so process it
                    636:                                if (cl->state != cs_zombie)
                    637:                                {
                    638:                                        cl->lastmessage = svs.realtime; // don't timeout
                    639:                                        SV_ExecuteClientMessage (cl);
                    640:                                }
                    641:                        }
                    642:                        break;
                    643:                }
                    644:                
                    645:                if (i != maxclients->value)
                    646:                        continue;
                    647:        }
                    648: }
                    649: 
                    650: /*
                    651: ==================
                    652: SV_CheckTimeouts
                    653: 
                    654: If a packet has not been received from a client for timeout->value
                    655: seconds, drop the conneciton.  Server frames are used instead of
                    656: realtime to avoid dropping the local client while debugging.
                    657: 
                    658: When a client is normally dropped, the client_t goes into a zombie state
                    659: for a few seconds to make sure any final reliable message gets resent
                    660: if necessary
                    661: ==================
                    662: */
                    663: void SV_CheckTimeouts (void)
                    664: {
                    665:        int             i;
                    666:        client_t        *cl;
                    667:        int                     droppoint;
                    668:        int                     zombiepoint;
                    669: 
                    670:        droppoint = svs.realtime - 1000*timeout->value;
                    671:        zombiepoint = svs.realtime - 1000*zombietime->value;
                    672: 
                    673:        for (i=0,cl=svs.clients ; i<maxclients->value ; i++,cl++)
                    674:        {
                    675:                // message times may be wrong across a changelevel
                    676:                if (cl->lastmessage > svs.realtime)
                    677:                        cl->lastmessage = svs.realtime;
                    678: 
                    679:                if (cl->state == cs_zombie
                    680:                && cl->lastmessage < zombiepoint)
                    681:                {
                    682:                        cl->state = cs_free;    // can now be reused
                    683:                        continue;
                    684:                }
                    685:                if ( (cl->state == cs_connected || cl->state == cs_spawned) 
                    686:                        && cl->lastmessage < droppoint)
                    687:                {
                    688:                        SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", cl->name);
                    689:                        SV_DropClient (cl); 
                    690:                        cl->state = cs_free;    // don't bother with zombie state
                    691:                }
                    692:        }
                    693: }
                    694: 
                    695: /*
                    696: ================
                    697: SV_PrepWorldFrame
                    698: 
                    699: This has to be done before the world logic, because
                    700: player processing happens outside RunWorldFrame
                    701: ================
                    702: */
                    703: void SV_PrepWorldFrame (void)
                    704: {
                    705:        edict_t *ent;
                    706:        int             i;
                    707: 
                    708:        for (i=0 ; i<ge->num_edicts ; i++, ent++)
                    709:        {
                    710:                ent = EDICT_NUM(i);
                    711:                // events only last for a single message
                    712:                ent->s.event = 0;
                    713:        }
                    714: 
                    715: }
                    716: 
                    717: 
                    718: /*
                    719: =================
                    720: SV_RunGameFrame
                    721: =================
                    722: */
                    723: void SV_RunGameFrame (void)
                    724: {
                    725:        if (host_speeds->value)
                    726:                time_before_game = Sys_Milliseconds ();
                    727: 
                    728:        // we always need to bump framenum, even if we
                    729:        // don't run the world, otherwise the delta
                    730:        // compression can get confused when a client
                    731:        // has the "current" frame
                    732:        sv.framenum++;
                    733:        sv.time = sv.framenum*100;
                    734: 
                    735:        // don't run if paused
                    736:        if (!sv_paused->value || maxclients->value > 1)
                    737:        {
                    738:                ge->RunFrame ();
                    739: 
                    740:                // never get more than one tic behind
                    741:                if (sv.time < svs.realtime)
                    742:                {
                    743:                        if (sv_showclamp->value)
                    744:                                Com_Printf ("sv highclamp\n");
                    745:                        svs.realtime = sv.time;
                    746:                }
                    747:        }
                    748: 
                    749:        if (host_speeds->value)
                    750:                time_after_game = Sys_Milliseconds ();
                    751: 
                    752: }
                    753: 
                    754: /*
                    755: ==================
                    756: SV_Frame
                    757: 
                    758: ==================
                    759: */
                    760: void SV_Frame (int msec)
                    761: {
                    762:        time_before_game = time_after_game = 0;
                    763: 
                    764:        // if server is not active, do nothing
                    765:        if (!svs.initialized)
                    766:                return;
                    767: 
                    768:     svs.realtime += msec;
                    769: 
                    770:        // keep the random time dependent
                    771:        rand ();
                    772: 
                    773:        // check timeouts
                    774:        SV_CheckTimeouts ();
                    775: 
                    776:        // get packets from clients
                    777:        SV_ReadPackets ();
                    778: 
                    779:        // move autonomous things around if enough time has passed
                    780:        if (!sv_timedemo->value && svs.realtime < sv.time)
                    781:        {
                    782:                // never let the time get too far off
                    783:                if (sv.time - svs.realtime > 100)
                    784:                {
                    785:                        if (sv_showclamp->value)
                    786:                                Com_Printf ("sv lowclamp\n");
                    787:                        svs.realtime = sv.time - 100;
                    788:                }
1.1.1.2   root      789:                NET_Sleep(sv.time - svs.realtime);
1.1       root      790:                return;
                    791:        }
                    792: 
                    793:        // update ping based on the last known frame from all clients
                    794:        SV_CalcPings ();
                    795: 
                    796:        // give the clients some timeslices
                    797:        SV_GiveMsec ();
                    798: 
                    799:        // let everything in the world think and move
                    800:        SV_RunGameFrame ();
                    801: 
                    802:        // send messages back to the clients that had packets read this frame
                    803:        SV_SendClientMessages ();
                    804: 
                    805:        // save the entire world state if recording a serverdemo
                    806:        SV_RecordDemoMessage ();
                    807: 
                    808:        // send a heartbeat to the master if needed
                    809:        Master_Heartbeat ();
                    810: 
                    811:        // clear teleport flags, etc for next frame
                    812:        SV_PrepWorldFrame ();
                    813: 
                    814: }
                    815: 
                    816: //============================================================================
                    817: 
                    818: /*
                    819: ================
                    820: Master_Heartbeat
                    821: 
                    822: Send a message to the master every few minutes to
                    823: let it know we are alive, and log information
                    824: ================
                    825: */
                    826: #define        HEARTBEAT_SECONDS       300
                    827: void Master_Heartbeat (void)
                    828: {
                    829:        char            *string;
                    830:        int                     i;
                    831: 
1.1.1.4 ! root      832:        // pgm post3.19 change, cvar pointer not validated before dereferencing
        !           833:        if (!dedicated || !dedicated->value)
1.1       root      834:                return;         // only dedicated servers send heartbeats
                    835: 
1.1.1.4 ! root      836:        // pgm post3.19 change, cvar pointer not validated before dereferencing
        !           837:        if (!public_server || !public_server->value)
1.1       root      838:                return;         // a private dedicated game
                    839: 
                    840:        // check for time wraparound
                    841:        if (svs.last_heartbeat > svs.realtime)
                    842:                svs.last_heartbeat = svs.realtime;
                    843: 
                    844:        if (svs.realtime - svs.last_heartbeat < HEARTBEAT_SECONDS*1000)
                    845:                return;         // not time to send yet
                    846: 
                    847:        svs.last_heartbeat = svs.realtime;
                    848: 
                    849:        // send the same string that we would give for a status OOB command
                    850:        string = SV_StatusString();
                    851: 
                    852:        // send to group master
                    853:        for (i=0 ; i<MAX_MASTERS ; i++)
                    854:                if (master_adr[i].port)
                    855:                {
                    856:                        Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
                    857:                        Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "heartbeat\n%s", string);
                    858:                }
                    859: }
                    860: 
                    861: /*
                    862: =================
                    863: Master_Shutdown
                    864: 
                    865: Informs all masters that this server is going down
                    866: =================
                    867: */
                    868: void Master_Shutdown (void)
                    869: {
                    870:        int                     i;
                    871: 
1.1.1.4 ! root      872:        // pgm post3.19 change, cvar pointer not validated before dereferencing
        !           873:        if (!dedicated || !dedicated->value)
1.1       root      874:                return;         // only dedicated servers send heartbeats
                    875: 
1.1.1.4 ! root      876:        // pgm post3.19 change, cvar pointer not validated before dereferencing
        !           877:        if (!public_server || !public_server->value)
1.1       root      878:                return;         // a private dedicated game
                    879: 
                    880:        // send to group master
                    881:        for (i=0 ; i<MAX_MASTERS ; i++)
                    882:                if (master_adr[i].port)
                    883:                {
                    884:                        if (i > 0)
                    885:                                Com_Printf ("Sending heartbeat to %s\n", NET_AdrToString (master_adr[i]));
                    886:                        Netchan_OutOfBandPrint (NS_SERVER, master_adr[i], "shutdown");
                    887:                }
                    888: }
                    889: 
                    890: //============================================================================
                    891: 
                    892: 
                    893: /*
                    894: =================
                    895: SV_UserinfoChanged
                    896: 
                    897: Pull specific info from a newly changed userinfo string
                    898: into a more C freindly form.
                    899: =================
                    900: */
                    901: void SV_UserinfoChanged (client_t *cl)
                    902: {
                    903:        char    *val;
                    904:        int             i;
                    905: 
                    906:        // call prog code to allow overrides
                    907:        ge->ClientUserinfoChanged (cl->edict, cl->userinfo);
                    908:        
                    909:        // name for C code
                    910:        strncpy (cl->name, Info_ValueForKey (cl->userinfo, "name"), sizeof(cl->name)-1);
                    911:        // mask off high bit
                    912:        for (i=0 ; i<sizeof(cl->name) ; i++)
                    913:                cl->name[i] &= 127;
                    914: 
                    915:        // rate command
                    916:        val = Info_ValueForKey (cl->userinfo, "rate");
                    917:        if (strlen(val))
                    918:        {
                    919:                i = atoi(val);
                    920:                cl->rate = i;
                    921:                if (cl->rate < 100)
                    922:                        cl->rate = 100;
                    923:                if (cl->rate > 15000)
                    924:                        cl->rate = 15000;
                    925:        }
                    926:        else
                    927:                cl->rate = 5000;
                    928: 
                    929:        // msg command
                    930:        val = Info_ValueForKey (cl->userinfo, "msg");
                    931:        if (strlen(val))
                    932:        {
                    933:                cl->messagelevel = atoi(val);
                    934:        }
                    935: 
                    936: }
                    937: 
                    938: 
                    939: //============================================================================
                    940: 
                    941: /*
                    942: ===============
                    943: SV_Init
                    944: 
                    945: Only called at quake2.exe startup, not for each game
                    946: ===============
                    947: */
                    948: void SV_Init (void)
                    949: {
                    950:        SV_InitOperatorCommands ();
                    951: 
                    952:        rcon_password = Cvar_Get ("rcon_password", "", 0);
                    953:        Cvar_Get ("skill", "1", 0);
                    954:        Cvar_Get ("deathmatch", "0", CVAR_LATCH);
                    955:        Cvar_Get ("coop", "0", CVAR_LATCH);
                    956:        Cvar_Get ("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO);
                    957:        Cvar_Get ("fraglimit", "0", CVAR_SERVERINFO);
                    958:        Cvar_Get ("timelimit", "0", CVAR_SERVERINFO);
                    959:        Cvar_Get ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH);
                    960:        Cvar_Get ("protocol", va("%i", PROTOCOL_VERSION), CVAR_SERVERINFO|CVAR_NOSET);;
                    961:        maxclients = Cvar_Get ("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH);
                    962:        hostname = Cvar_Get ("hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE);
                    963:        timeout = Cvar_Get ("timeout", "125", 0);
                    964:        zombietime = Cvar_Get ("zombietime", "2", 0);
                    965:        sv_showclamp = Cvar_Get ("showclamp", "0", 0);
                    966:        sv_paused = Cvar_Get ("paused", "0", 0);
                    967:        sv_timedemo = Cvar_Get ("timedemo", "0", 0);
                    968:        sv_enforcetime = Cvar_Get ("sv_enforcetime", "0", 0);
1.1.1.4 ! root      969:        allow_download = Cvar_Get ("allow_download", "1", CVAR_ARCHIVE);
1.1.1.2   root      970:        allow_download_players  = Cvar_Get ("allow_download_players", "0", CVAR_ARCHIVE);
                    971:        allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE);
                    972:        allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE);
                    973:        allow_download_maps       = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE);
                    974: 
1.1       root      975:        sv_noreload = Cvar_Get ("sv_noreload", "0", 0);
                    976: 
1.1.1.3   root      977:        sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH);
                    978: 
1.1       root      979:        public_server = Cvar_Get ("public", "0", 0);
                    980: 
                    981:        sv_reconnect_limit = Cvar_Get ("sv_reconnect_limit", "3", CVAR_ARCHIVE);
                    982: 
                    983:        SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer));
                    984: }
                    985: 
                    986: /*
                    987: ==================
                    988: SV_FinalMessage
                    989: 
                    990: Used by SV_Shutdown to send a final message to all
                    991: connected clients before the server goes down.  The messages are sent immediately,
                    992: not just stuck on the outgoing message list, because the server is going
                    993: to totally exit after returning from this function.
                    994: ==================
                    995: */
                    996: void SV_FinalMessage (char *message, qboolean reconnect)
                    997: {
                    998:        int                     i;
                    999:        client_t        *cl;
                   1000:        
                   1001:        SZ_Clear (&net_message);
                   1002:        MSG_WriteByte (&net_message, svc_print);
                   1003:        MSG_WriteByte (&net_message, PRINT_HIGH);
                   1004:        MSG_WriteString (&net_message, message);
                   1005: 
                   1006:        if (reconnect)
                   1007:                MSG_WriteByte (&net_message, svc_reconnect);
                   1008:        else
                   1009:                MSG_WriteByte (&net_message, svc_disconnect);
                   1010: 
                   1011:        // send it twice
                   1012:        // stagger the packets to crutch operating system limited buffers
                   1013: 
                   1014:        for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
                   1015:                if (cl->state >= cs_connected)
                   1016:                        Netchan_Transmit (&cl->netchan, net_message.cursize
                   1017:                        , net_message.data);
                   1018: 
                   1019:        for (i=0, cl = svs.clients ; i<maxclients->value ; i++, cl++)
                   1020:                if (cl->state >= cs_connected)
                   1021:                        Netchan_Transmit (&cl->netchan, net_message.cursize
                   1022:                        , net_message.data);
                   1023: }
                   1024: 
                   1025: 
                   1026: 
                   1027: /*
                   1028: ================
                   1029: SV_Shutdown
                   1030: 
                   1031: Called when each game quits,
                   1032: before Sys_Quit or Sys_Error
                   1033: ================
                   1034: */
                   1035: void SV_Shutdown (char *finalmsg, qboolean reconnect)
                   1036: {
                   1037:        if (svs.clients)
                   1038:                SV_FinalMessage (finalmsg, reconnect);
                   1039: 
                   1040:        Master_Shutdown ();
                   1041:        SV_ShutdownGameProgs ();
                   1042: 
                   1043:        // free current level
                   1044:        if (sv.demofile)
                   1045:                fclose (sv.demofile);
                   1046:        memset (&sv, 0, sizeof(sv));
                   1047:        Com_SetServerState (sv.state);
                   1048: 
                   1049:        // free server static data
                   1050:        if (svs.clients)
                   1051:                Z_Free (svs.clients);
                   1052:        if (svs.client_entities)
                   1053:                Z_Free (svs.client_entities);
                   1054:        if (svs.demofile)
                   1055:                fclose (svs.demofile);
                   1056:        memset (&svs, 0, sizeof(svs));
                   1057: }
                   1058: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.