Annotation of quake2/server/sv_user.c, revision 1.1.1.3

1.1.1.3 ! root        1: /*
        !             2: Copyright (C) 1997-2001 Id Software, Inc.
        !             3: 
        !             4: This program is free software; you can redistribute it and/or
        !             5: modify it under the terms of the GNU General Public License
        !             6: as published by the Free Software Foundation; either version 2
        !             7: of the License, or (at your option) any later version.
        !             8: 
        !             9: This program is distributed in the hope that it will be useful,
        !            10: but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
        !            12: 
        !            13: See the GNU General Public License for more details.
        !            14: 
        !            15: You should have received a copy of the GNU General Public License
        !            16: along with this program; if not, write to the Free Software
        !            17: Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
        !            18: 
        !            19: */
1.1       root       20: // sv_user.c -- server code for moving users
                     21: 
                     22: #include "server.h"
                     23: 
                     24: edict_t        *sv_player;
                     25: 
                     26: /*
                     27: ============================================================
                     28: 
                     29: USER STRINGCMD EXECUTION
                     30: 
                     31: sv_client and sv_player will be valid.
                     32: ============================================================
                     33: */
                     34: 
                     35: /*
                     36: ==================
                     37: SV_BeginDemoServer
                     38: ==================
                     39: */
                     40: void SV_BeginDemoserver (void)
                     41: {
                     42:        char            name[MAX_OSPATH];
                     43: 
                     44:        Com_sprintf (name, sizeof(name), "demos/%s", sv.name);
                     45:        FS_FOpenFile (name, &sv.demofile);
                     46:        if (!sv.demofile)
                     47:                Com_Error (ERR_DROP, "Couldn't open %s\n", name);
                     48: }
                     49: 
                     50: /*
                     51: ================
                     52: SV_New_f
                     53: 
                     54: Sends the first message from the server to a connected client.
                     55: This will be sent on the initial connection and upon each server load.
                     56: ================
                     57: */
                     58: void SV_New_f (void)
                     59: {
                     60:        char            *gamedir;
                     61:        int                     playernum;
                     62:        edict_t         *ent;
                     63: 
                     64:        Com_DPrintf ("New() from %s\n", sv_client->name);
                     65: 
                     66:        if (sv_client->state != cs_connected)
                     67:        {
                     68:                Com_Printf ("New not valid -- already spawned\n");
                     69:                return;
                     70:        }
                     71: 
                     72:        // demo servers just dump the file message
                     73:        if (sv.state == ss_demo)
                     74:        {
                     75:                SV_BeginDemoserver ();
                     76:                return;
                     77:        }
                     78: 
                     79:        //
                     80:        // serverdata needs to go over for all types of servers
                     81:        // to make sure the protocol is right, and to set the gamedir
                     82:        //
                     83:        gamedir = Cvar_VariableString ("gamedir");
                     84: 
                     85:        // send the serverdata
                     86:        MSG_WriteByte (&sv_client->netchan.message, svc_serverdata);
                     87:        MSG_WriteLong (&sv_client->netchan.message, PROTOCOL_VERSION);
                     88:        MSG_WriteLong (&sv_client->netchan.message, svs.spawncount);
                     89:        MSG_WriteByte (&sv_client->netchan.message, sv.attractloop);
                     90:        MSG_WriteString (&sv_client->netchan.message, gamedir);
                     91: 
                     92:        if (sv.state == ss_cinematic || sv.state == ss_pic)
                     93:                playernum = -1;
                     94:        else
                     95:                playernum = sv_client - svs.clients;
                     96:        MSG_WriteShort (&sv_client->netchan.message, playernum);
                     97: 
                     98:        // send full levelname
                     99:        MSG_WriteString (&sv_client->netchan.message, sv.configstrings[CS_NAME]);
                    100: 
                    101:        //
                    102:        // game server
                    103:        // 
                    104:        if (sv.state == ss_game)
                    105:        {
                    106:                // set up the entity for the client
                    107:                ent = EDICT_NUM(playernum+1);
                    108:                ent->s.number = playernum+1;
                    109:                sv_client->edict = ent;
                    110:                memset (&sv_client->lastcmd, 0, sizeof(sv_client->lastcmd));
                    111: 
                    112:                // begin fetching configstrings
                    113:                MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
                    114:                MSG_WriteString (&sv_client->netchan.message, va("cmd configstrings %i 0\n",svs.spawncount) );
                    115:        }
                    116: 
                    117: }
                    118: 
                    119: /*
                    120: ==================
                    121: SV_Configstrings_f
                    122: ==================
                    123: */
                    124: void SV_Configstrings_f (void)
                    125: {
                    126:        int                     start;
                    127: 
                    128:        Com_DPrintf ("Configstrings() from %s\n", sv_client->name);
                    129: 
                    130:        if (sv_client->state != cs_connected)
                    131:        {
                    132:                Com_Printf ("configstrings not valid -- already spawned\n");
                    133:                return;
                    134:        }
                    135: 
                    136:        // handle the case of a level changing while a client was connecting
                    137:        if ( atoi(Cmd_Argv(1)) != svs.spawncount )
                    138:        {
                    139:                Com_Printf ("SV_Configstrings_f from different level\n");
                    140:                SV_New_f ();
                    141:                return;
                    142:        }
                    143:        
                    144:        start = atoi(Cmd_Argv(2));
                    145: 
                    146:        // write a packet full of data
                    147: 
                    148:        while ( sv_client->netchan.message.cursize < MAX_MSGLEN/2 
                    149:                && start < MAX_CONFIGSTRINGS)
                    150:        {
                    151:                if (sv.configstrings[start][0])
                    152:                {
                    153:                        MSG_WriteByte (&sv_client->netchan.message, svc_configstring);
                    154:                        MSG_WriteShort (&sv_client->netchan.message, start);
                    155:                        MSG_WriteString (&sv_client->netchan.message, sv.configstrings[start]);
                    156:                }
                    157:                start++;
                    158:        }
                    159: 
                    160:        // send next command
                    161: 
                    162:        if (start == MAX_CONFIGSTRINGS)
                    163:        {
                    164:                MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
                    165:                MSG_WriteString (&sv_client->netchan.message, va("cmd baselines %i 0\n",svs.spawncount) );
                    166:        }
                    167:        else
                    168:        {
                    169:                MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
                    170:                MSG_WriteString (&sv_client->netchan.message, va("cmd configstrings %i %i\n",svs.spawncount, start) );
                    171:        }
                    172: }
                    173: 
                    174: /*
                    175: ==================
                    176: SV_Baselines_f
                    177: ==================
                    178: */
                    179: void SV_Baselines_f (void)
                    180: {
                    181:        int             start;
                    182:        entity_state_t  nullstate;
                    183:        entity_state_t  *base;
                    184: 
                    185:        Com_DPrintf ("Baselines() from %s\n", sv_client->name);
                    186: 
                    187:        if (sv_client->state != cs_connected)
                    188:        {
                    189:                Com_Printf ("baselines not valid -- already spawned\n");
                    190:                return;
                    191:        }
                    192:        
                    193:        // handle the case of a level changing while a client was connecting
                    194:        if ( atoi(Cmd_Argv(1)) != svs.spawncount )
                    195:        {
                    196:                Com_Printf ("SV_Baselines_f from different level\n");
                    197:                SV_New_f ();
                    198:                return;
                    199:        }
                    200:        
                    201:        start = atoi(Cmd_Argv(2));
                    202: 
                    203:        memset (&nullstate, 0, sizeof(nullstate));
                    204: 
                    205:        // write a packet full of data
                    206: 
                    207:        while ( sv_client->netchan.message.cursize <  MAX_MSGLEN/2
                    208:                && start < MAX_EDICTS)
                    209:        {
                    210:                base = &sv.baselines[start];
                    211:                if (base->modelindex || base->sound || base->effects)
                    212:                {
                    213:                        MSG_WriteByte (&sv_client->netchan.message, svc_spawnbaseline);
1.1.1.2   root      214:                        MSG_WriteDeltaEntity (&nullstate, base, &sv_client->netchan.message, true, true);
1.1       root      215:                }
                    216:                start++;
                    217:        }
                    218: 
                    219:        // send next command
                    220: 
                    221:        if (start == MAX_EDICTS)
                    222:        {
                    223:                MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
1.1.1.2   root      224:                MSG_WriteString (&sv_client->netchan.message, va("precache %i\n", svs.spawncount) );
1.1       root      225:        }
                    226:        else
                    227:        {
                    228:                MSG_WriteByte (&sv_client->netchan.message, svc_stufftext);
                    229:                MSG_WriteString (&sv_client->netchan.message, va("cmd baselines %i %i\n",svs.spawncount, start) );
                    230:        }
                    231: }
                    232: 
                    233: /*
                    234: ==================
                    235: SV_Begin_f
                    236: ==================
                    237: */
                    238: void SV_Begin_f (void)
                    239: {
                    240:        Com_DPrintf ("Begin() from %s\n", sv_client->name);
                    241: 
                    242:        // handle the case of a level changing while a client was connecting
                    243:        if ( atoi(Cmd_Argv(1)) != svs.spawncount )
                    244:        {
                    245:                Com_Printf ("SV_Begin_f from different level\n");
                    246:                SV_New_f ();
                    247:                return;
                    248:        }
                    249: 
                    250:        sv_client->state = cs_spawned;
1.1.1.3 ! root      251:        
1.1       root      252:        // call the game begin function
                    253:        ge->ClientBegin (sv_player);
                    254: 
                    255:        Cbuf_InsertFromDefer ();
                    256: }
                    257: 
                    258: //=============================================================================
                    259: 
                    260: /*
                    261: ==================
                    262: SV_NextDownload_f
                    263: ==================
                    264: */
                    265: void SV_NextDownload_f (void)
                    266: {
                    267:        int             r;
                    268:        int             percent;
                    269:        int             size;
                    270: 
                    271:        if (!sv_client->download)
                    272:                return;
                    273: 
                    274:        r = sv_client->downloadsize - sv_client->downloadcount;
                    275:        if (r > 1024)
                    276:                r = 1024;
                    277: 
                    278:        MSG_WriteByte (&sv_client->netchan.message, svc_download);
                    279:        MSG_WriteShort (&sv_client->netchan.message, r);
                    280: 
                    281:        sv_client->downloadcount += r;
                    282:        size = sv_client->downloadsize;
                    283:        if (!size)
                    284:                size = 1;
                    285:        percent = sv_client->downloadcount*100/size;
                    286:        MSG_WriteByte (&sv_client->netchan.message, percent);
                    287:        SZ_Write (&sv_client->netchan.message,
                    288:                sv_client->download + sv_client->downloadcount - r, r);
                    289: 
                    290:        if (sv_client->downloadcount != sv_client->downloadsize)
                    291:                return;
                    292: 
                    293:        FS_FreeFile (sv_client->download);
                    294:        sv_client->download = NULL;
                    295: }
                    296: 
                    297: /*
                    298: ==================
                    299: SV_BeginDownload_f
                    300: ==================
                    301: */
                    302: void SV_BeginDownload_f(void)
                    303: {
                    304:        char    *name;
                    305:        extern  cvar_t *allow_download;
1.1.1.2   root      306:        extern  cvar_t *allow_download_players;
                    307:        extern  cvar_t *allow_download_models;
                    308:        extern  cvar_t *allow_download_sounds;
                    309:        extern  cvar_t *allow_download_maps;
                    310:        extern  int             file_from_pak; // ZOID did file come from pak?
                    311:        int offset = 0;
1.1       root      312: 
                    313:        name = Cmd_Argv(1);
1.1.1.2   root      314: 
                    315:        if (Cmd_Argc() > 2)
                    316:                offset = atoi(Cmd_Argv(2)); // downloaded offset
                    317: 
                    318:        // hacked by zoid to allow more conrol over download
                    319:        // first off, no .. or global allow check
1.1       root      320:        if (strstr (name, "..") || !allow_download->value
1.1.1.2   root      321:                // leading dot is no good
                    322:                || *name == '.' 
                    323:                // leading slash bad as well, must be in subdir
                    324:                || *name == '/'
                    325:                // next up, skin check
                    326:                || (strncmp(name, "players/", 6) == 0 && !allow_download_players->value)
                    327:                // now models
                    328:                || (strncmp(name, "models/", 6) == 0 && !allow_download_models->value)
                    329:                // now sounds
                    330:                || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds->value)
                    331:                // now maps (note special case for maps, must not be in pak)
                    332:                || (strncmp(name, "maps/", 6) == 0 && !allow_download_maps->value)
                    333:                // MUST be in a subdirectory    
                    334:                || !strstr (name, "/") )        
1.1       root      335:        {       // don't allow anything with .. path
                    336:                MSG_WriteByte (&sv_client->netchan.message, svc_download);
                    337:                MSG_WriteShort (&sv_client->netchan.message, -1);
                    338:                MSG_WriteByte (&sv_client->netchan.message, 0);
                    339:                return;
                    340:        }
                    341: 
1.1.1.2   root      342: 
1.1       root      343:        if (sv_client->download)
                    344:                FS_FreeFile (sv_client->download);
                    345: 
                    346:        sv_client->downloadsize = FS_LoadFile (name, (void **)&sv_client->download);
1.1.1.2   root      347:        sv_client->downloadcount = offset;
1.1       root      348: 
1.1.1.2   root      349:        if (offset > sv_client->downloadsize)
                    350:                sv_client->downloadcount = sv_client->downloadsize;
                    351: 
                    352:        if (!sv_client->download
                    353:                // special check for maps, if it came from a pak file, don't allow
                    354:                // download  ZOID
                    355:                || (strncmp(name, "maps/", 5) == 0 && file_from_pak))
1.1       root      356:        {
                    357:                Com_DPrintf ("Couldn't download %s to %s\n", name, sv_client->name);
1.1.1.2   root      358:                if (sv_client->download) {
                    359:                        FS_FreeFile (sv_client->download);
                    360:                        sv_client->download = NULL;
                    361:                }
                    362: 
1.1       root      363:                MSG_WriteByte (&sv_client->netchan.message, svc_download);
                    364:                MSG_WriteShort (&sv_client->netchan.message, -1);
                    365:                MSG_WriteByte (&sv_client->netchan.message, 0);
                    366:                return;
                    367:        }
                    368: 
                    369:        SV_NextDownload_f ();
                    370:        Com_DPrintf ("Downloading %s to %s\n", name, sv_client->name);
                    371: }
                    372: 
                    373: 
1.1.1.2   root      374: 
1.1       root      375: //============================================================================
                    376: 
                    377: 
                    378: /*
                    379: =================
                    380: SV_Disconnect_f
                    381: 
                    382: The client is going to disconnect, so remove the connection immediately
                    383: =================
                    384: */
                    385: void SV_Disconnect_f (void)
                    386: {
                    387: //     SV_EndRedirect ();
                    388:        SV_DropClient (sv_client);      
                    389: }
                    390: 
                    391: 
                    392: /*
                    393: ==================
                    394: SV_ShowServerinfo_f
                    395: 
                    396: Dumps the serverinfo info string
                    397: ==================
                    398: */
                    399: void SV_ShowServerinfo_f (void)
                    400: {
                    401:        Info_Print (Cvar_Serverinfo());
                    402: }
                    403: 
                    404: 
                    405: void SV_Nextserver (void)
                    406: {
                    407:        char    *v;
                    408: 
1.1.1.2   root      409:        //ZOID, ss_pic can be nextserver'd in coop mode
                    410:        if (sv.state == ss_game || (sv.state == ss_pic && !Cvar_VariableValue("coop")))
1.1       root      411:                return;         // can't nextserver while playing a normal game
                    412: 
                    413:        svs.spawncount++;       // make sure another doesn't sneak in
                    414:        v = Cvar_VariableString ("nextserver");
                    415:        if (!v[0])
                    416:                Cbuf_AddText ("killserver\n");
                    417:        else
                    418:        {
                    419:                Cbuf_AddText (v);
                    420:                Cbuf_AddText ("\n");
                    421:        }
                    422:        Cvar_Set ("nextserver","");
                    423: }
                    424: 
                    425: /*
                    426: ==================
                    427: SV_Nextserver_f
                    428: 
                    429: A cinematic has completed or been aborted by a client, so move
                    430: to the next server,
                    431: ==================
                    432: */
                    433: void SV_Nextserver_f (void)
                    434: {
1.1.1.2   root      435:        if ( atoi(Cmd_Argv(1)) != svs.spawncount ) {
                    436:                Com_DPrintf ("Nextserver() from wrong level, from %s\n", sv_client->name);
1.1       root      437:                return;         // leftover from last server
1.1.1.2   root      438:        }
                    439: 
                    440:        Com_DPrintf ("Nextserver() from %s\n", sv_client->name);
1.1       root      441: 
                    442:        SV_Nextserver ();
                    443: }
                    444: 
                    445: typedef struct
                    446: {
                    447:        char    *name;
                    448:        void    (*func) (void);
                    449: } ucmd_t;
                    450: 
                    451: ucmd_t ucmds[] =
                    452: {
                    453:        // auto issued
                    454:        {"new", SV_New_f},
                    455:        {"configstrings", SV_Configstrings_f},
                    456:        {"baselines", SV_Baselines_f},
                    457:        {"begin", SV_Begin_f},
                    458: 
                    459:        {"nextserver", SV_Nextserver_f},
                    460: 
                    461:        {"disconnect", SV_Disconnect_f},
                    462: 
                    463:        // issued by hand at client consoles    
                    464:        {"info", SV_ShowServerinfo_f},
                    465: 
                    466:        {"download", SV_BeginDownload_f},
                    467:        {"nextdl", SV_NextDownload_f},
                    468: 
                    469:        {NULL, NULL}
                    470: };
                    471: 
                    472: /*
                    473: ==================
                    474: SV_ExecuteUserCommand
                    475: ==================
                    476: */
                    477: void SV_ExecuteUserCommand (char *s)
                    478: {
                    479:        ucmd_t  *u;
                    480:        
                    481:        Cmd_TokenizeString (s, true);
                    482:        sv_player = sv_client->edict;
                    483: 
                    484: //     SV_BeginRedirect (RD_CLIENT);
                    485: 
                    486:        for (u=ucmds ; u->name ; u++)
                    487:                if (!strcmp (Cmd_Argv(0), u->name) )
                    488:                {
                    489:                        u->func ();
                    490:                        break;
                    491:                }
                    492: 
                    493:        if (!u->name && sv.state == ss_game)
                    494:                ge->ClientCommand (sv_player);
                    495: 
                    496: //     SV_EndRedirect ();
                    497: }
                    498: 
                    499: /*
                    500: ===========================================================================
                    501: 
                    502: USER CMD EXECUTION
                    503: 
                    504: ===========================================================================
                    505: */
                    506: 
                    507: 
1.1.1.2   root      508: 
                    509: void SV_ClientThink (client_t *cl, usercmd_t *cmd)
                    510: 
1.1       root      511: {
                    512:        cl->commandMsec -= cmd->msec;
1.1.1.2   root      513: 
1.1       root      514:        if (cl->commandMsec < 0 && sv_enforcetime->value )
                    515:        {
                    516:                Com_DPrintf ("commandMsec underflow from %s\n", cl->name);
                    517:                return;
                    518:        }
1.1.1.2   root      519: 
1.1       root      520:        ge->ClientThink (cl->edict, cmd);
                    521: }
                    522: 
1.1.1.2   root      523: 
                    524: 
1.1       root      525: #define        MAX_STRINGCMDS  8
                    526: /*
                    527: ===================
                    528: SV_ExecuteClientMessage
                    529: 
                    530: The current net_message is parsed for the given client
                    531: ===================
                    532: */
                    533: void SV_ExecuteClientMessage (client_t *cl)
                    534: {
                    535:        int             c;
                    536:        char    *s;
1.1.1.2   root      537: 
1.1       root      538:        usercmd_t       nullcmd;
                    539:        usercmd_t       oldest, oldcmd, newcmd;
                    540:        int             net_drop;
                    541:        int             stringCmdCount;
                    542:        int             checksum, calculatedChecksum;
                    543:        int             checksumIndex;
                    544:        qboolean        move_issued;
1.1.1.2   root      545:        int             lastframe;
1.1       root      546: 
                    547:        sv_client = cl;
                    548:        sv_player = sv_client->edict;
                    549: 
                    550:        // only allow one move command
                    551:        move_issued = false;
                    552:        stringCmdCount = 0;
                    553: 
                    554:        while (1)
                    555:        {
                    556:                if (net_message.readcount > net_message.cursize)
                    557:                {
                    558:                        Com_Printf ("SV_ReadClientMessage: badread\n");
                    559:                        SV_DropClient (cl);
                    560:                        return;
                    561:                }       
                    562: 
                    563:                c = MSG_ReadByte (&net_message);
                    564:                if (c == -1)
                    565:                        break;
                    566:                                
                    567:                switch (c)
                    568:                {
                    569:                default:
                    570:                        Com_Printf ("SV_ReadClientMessage: unknown command char\n");
                    571:                        SV_DropClient (cl);
                    572:                        return;
                    573:                                                
                    574:                case clc_nop:
                    575:                        break;
                    576: 
                    577:                case clc_userinfo:
                    578:                        strncpy (cl->userinfo, MSG_ReadString (&net_message), sizeof(cl->userinfo)-1);
                    579:                        SV_UserinfoChanged (cl);
                    580:                        break;
                    581: 
                    582:                case clc_move:
                    583:                        if (move_issued)
                    584:                                return;         // someone is trying to cheat...
                    585: 
1.1.1.2   root      586:                        move_issued = true;
1.1       root      587:                        checksumIndex = net_message.readcount;
                    588:                        checksum = MSG_ReadByte (&net_message);
1.1.1.2   root      589:                        lastframe = MSG_ReadLong (&net_message);
                    590:                        if (lastframe != cl->lastframe) {
                    591:                                cl->lastframe = lastframe;
                    592:                                if (cl->lastframe > 0) {
                    593:                                        cl->frame_latency[cl->lastframe&(LATENCY_COUNTS-1)] = 
                    594:                                                svs.realtime - cl->frames[cl->lastframe & UPDATE_MASK].senttime;
                    595:                                }
                    596:                        }
1.1       root      597: 
                    598:                        memset (&nullcmd, 0, sizeof(nullcmd));
                    599:                        MSG_ReadDeltaUsercmd (&net_message, &nullcmd, &oldest);
                    600:                        MSG_ReadDeltaUsercmd (&net_message, &oldest, &oldcmd);
                    601:                        MSG_ReadDeltaUsercmd (&net_message, &oldcmd, &newcmd);
                    602: 
                    603:                        if ( cl->state != cs_spawned )
                    604:                        {
                    605:                                cl->lastframe = -1;
                    606:                                break;
                    607:                        }
                    608: 
                    609:                        // if the checksum fails, ignore the rest of the packet
1.1.1.2   root      610:                        calculatedChecksum = COM_BlockSequenceCRCByte (
1.1       root      611:                                net_message.data + checksumIndex + 1,
                    612:                                net_message.readcount - checksumIndex - 1,
                    613:                                cl->netchan.incoming_sequence);
                    614: 
                    615:                        if (calculatedChecksum != checksum)
                    616:                        {
1.1.1.2   root      617:                                Com_DPrintf ("Failed command checksum for %s (%d != %d)/%d\n", 
                    618:                                        cl->name, calculatedChecksum, checksum, 
                    619:                                        cl->netchan.incoming_sequence);
1.1       root      620:                                return;
                    621:                        }
                    622: 
                    623:                        if (!sv_paused->value)
                    624:                        {
                    625:                                net_drop = cl->netchan.dropped;
                    626:                                if (net_drop < 20)
                    627:                                {
1.1.1.2   root      628: 
1.1       root      629: //if (net_drop > 2)
1.1.1.2   root      630: 
1.1       root      631: //     Com_Printf ("drop %i\n", net_drop);
                    632:                                        while (net_drop > 2)
                    633:                                        {
1.1.1.2   root      634:                                                SV_ClientThink (cl, &cl->lastcmd);
                    635: 
1.1       root      636:                                                net_drop--;
                    637:                                        }
                    638:                                        if (net_drop > 1)
1.1.1.2   root      639:                                                SV_ClientThink (cl, &oldest);
                    640: 
1.1       root      641:                                        if (net_drop > 0)
1.1.1.2   root      642:                                                SV_ClientThink (cl, &oldcmd);
                    643: 
1.1       root      644:                                }
1.1.1.2   root      645:                                SV_ClientThink (cl, &newcmd);
1.1       root      646:                        }
                    647: 
                    648:                        cl->lastcmd = newcmd;
                    649:                        break;
                    650: 
                    651:                case clc_stringcmd:     
                    652:                        s = MSG_ReadString (&net_message);
                    653: 
                    654:                        // malicious users may try using too many string commands
                    655:                        if (++stringCmdCount < MAX_STRINGCMDS)
                    656:                                SV_ExecuteUserCommand (s);
1.1.1.2   root      657: 
1.1       root      658:                        if (cl->state == cs_zombie)
                    659:                                return; // disconnect command
                    660:                        break;
                    661:                }
                    662:        }
                    663: }
                    664: 

unix.superglobalmegacorp.com

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