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

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

unix.superglobalmegacorp.com

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