Annotation of quake2/server/sv_main.c, revision 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.