1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8
9 This file is part of the OpenJK source code.
10
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24
25 // cl_parse.c -- parse a message received from the server
26
27 #include "client.h"
28 #include "cl_cgameapi.h"
29 #include "qcommon/stringed_ingame.h"
30
31 #ifdef USE_INTERNAL_ZLIB
32 #include "zlib/zlib.h"
33 #else
34 #include <zlib.h>
35 #endif
36
37 static char hiddenCvarVal[128];
38
39 char *svc_strings[256] = {
40 "svc_bad",
41
42 "svc_nop",
43 "svc_gamestate",
44 "svc_configstring",
45 "svc_baseline",
46 "svc_serverCommand",
47 "svc_download",
48 "svc_snapshot",
49 "svc_setgame",
50 "svc_mapchange",
51 };
52
SHOWNET(msg_t * msg,char * s)53 void SHOWNET( msg_t *msg, char *s) {
54 if ( cl_shownet->integer >= 2) {
55 Com_Printf ("%3i:%s\n", msg->readcount-1, s);
56 }
57 }
58
59 /*
60 =========================================================================
61
62 MESSAGE PARSING
63
64 =========================================================================
65 */
66
67 /*
68 ==================
69 CL_DeltaEntity
70
71 Parses deltas from the given base and adds the resulting entity
72 to the current frame
73 ==================
74 */
CL_DeltaEntity(msg_t * msg,clSnapshot_t * frame,int newnum,entityState_t * old,qboolean unchanged)75 void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old,
76 qboolean unchanged) {
77 entityState_t *state;
78
79 // save the parsed entity state into the big circular buffer so
80 // it can be used as the source for a later delta
81 state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
82
83 if ( unchanged )
84 {
85 *state = *old;
86 }
87 else
88 {
89 MSG_ReadDeltaEntity( msg, old, state, newnum );
90 }
91
92 if ( state->number == (MAX_GENTITIES-1) ) {
93 return; // entity was delta removed
94 }
95 cl.parseEntitiesNum++;
96 frame->numEntities++;
97 }
98
99 /*
100 ==================
101 CL_ParsePacketEntities
102
103 ==================
104 */
CL_ParsePacketEntities(msg_t * msg,clSnapshot_t * oldframe,clSnapshot_t * newframe)105 void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
106 int newnum;
107 entityState_t *oldstate;
108 int oldindex, oldnum;
109
110 newframe->parseEntitiesNum = cl.parseEntitiesNum;
111 newframe->numEntities = 0;
112
113 // delta from the entities present in oldframe
114 oldindex = 0;
115 oldstate = NULL;
116 if (!oldframe) {
117 oldnum = 99999;
118 } else {
119 if ( oldindex >= oldframe->numEntities ) {
120 oldnum = 99999;
121 } else {
122 oldstate = &cl.parseEntities[
123 (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
124 oldnum = oldstate->number;
125 }
126 }
127
128 while ( 1 ) {
129 // read the entity index number
130 newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
131
132 if ( newnum == (MAX_GENTITIES-1) ) {
133 break;
134 }
135
136 if ( msg->readcount > msg->cursize ) {
137 Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
138 }
139
140 while ( oldnum < newnum ) {
141 // one or more entities from the old packet are unchanged
142 if ( cl_shownet->integer == 3 ) {
143 Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
144 }
145 CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
146
147 oldindex++;
148
149 if ( oldindex >= oldframe->numEntities ) {
150 oldnum = 99999;
151 } else {
152 oldstate = &cl.parseEntities[
153 (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
154 oldnum = oldstate->number;
155 }
156 }
157 if (oldnum == newnum) {
158 // delta from previous state
159 if ( cl_shownet->integer == 3 ) {
160 Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum);
161 }
162 CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
163
164 oldindex++;
165
166 if ( oldindex >= oldframe->numEntities ) {
167 oldnum = 99999;
168 } else {
169 oldstate = &cl.parseEntities[
170 (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
171 oldnum = oldstate->number;
172 }
173 continue;
174 }
175
176 if ( oldnum > newnum ) {
177 // delta from baseline
178 if ( cl_shownet->integer == 3 ) {
179 Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum);
180 }
181 CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
182 continue;
183 }
184
185 }
186
187 // any remaining entities in the old frame are copied over
188 while ( oldnum != 99999 ) {
189 // one or more entities from the old packet are unchanged
190 if ( cl_shownet->integer == 3 ) {
191 Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
192 }
193 CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
194
195 oldindex++;
196
197 if ( oldindex >= oldframe->numEntities ) {
198 oldnum = 99999;
199 } else {
200 oldstate = &cl.parseEntities[
201 (oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
202 oldnum = oldstate->number;
203 }
204 }
205 }
206
207
208 /*
209 ================
210 CL_ParseSnapshot
211
212 If the snapshot is parsed properly, it will be copied to
213 cl.snap and saved in cl.snapshots[]. If the snapshot is invalid
214 for any reason, no changes to the state will be made at all.
215 ================
216 */
CL_ParseSnapshot(msg_t * msg)217 void CL_ParseSnapshot( msg_t *msg ) {
218 int len;
219 clSnapshot_t *old;
220 clSnapshot_t newSnap;
221 int deltaNum;
222 int oldMessageNum;
223 int i, packetNum;
224
225 // get the reliable sequence acknowledge number
226 // NOTE: now sent with all server to client messages
227 //clc.reliableAcknowledge = MSG_ReadLong( msg );
228
229 // read in the new snapshot to a temporary buffer
230 // we will only copy to cl.snap if it is valid
231 Com_Memset (&newSnap, 0, sizeof(newSnap));
232
233 // we will have read any new server commands in this
234 // message before we got to svc_snapshot
235 newSnap.serverCommandNum = clc.serverCommandSequence;
236
237 newSnap.serverTime = MSG_ReadLong( msg );
238
239 // if we were just unpaused, we can only *now* really let the
240 // change come into effect or the client hangs.
241 cl_paused->modified = qfalse;
242
243 newSnap.messageNum = clc.serverMessageSequence;
244
245 deltaNum = MSG_ReadByte( msg );
246 if ( !deltaNum ) {
247 newSnap.deltaNum = -1;
248 } else {
249 newSnap.deltaNum = newSnap.messageNum - deltaNum;
250 }
251 newSnap.snapFlags = MSG_ReadByte( msg );
252
253 // If the frame is delta compressed from data that we
254 // no longer have available, we must suck up the rest of
255 // the frame, but not use it, then ask for a non-compressed
256 // message
257 if ( newSnap.deltaNum <= 0 ) {
258 newSnap.valid = qtrue; // uncompressed frame
259 old = NULL;
260 clc.demowaiting = qfalse; // we can start recording now
261 } else {
262 old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
263 if ( !old->valid ) {
264 // should never happen
265 Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
266 while ( ( newSnap.deltaNum & PACKET_MASK ) != ( newSnap.messageNum & PACKET_MASK ) && !old->valid ) {
267 newSnap.deltaNum++;
268 old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
269 }
270 if ( old->valid ) {
271 Com_Printf ("Found more recent frame to delta from.\n");
272 }
273 }
274 if ( !old->valid ) {
275 Com_Printf ("Failed to find more recent frame to delta from.\n");
276 } else if ( old->messageNum != newSnap.deltaNum ) {
277 // The frame that the server did the delta from
278 // is too old, so we can't reconstruct it properly.
279 Com_Printf ("Delta frame too old.\n");
280 } else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
281 Com_DPrintf ("Delta parseEntitiesNum too old.\n");
282 } else {
283 newSnap.valid = qtrue; // valid delta parse
284 }
285 }
286
287 // read areamask
288 len = MSG_ReadByte( msg );
289
290 if((unsigned)len > sizeof(newSnap.areamask))
291 {
292 Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask", len);
293 return;
294 }
295
296 MSG_ReadData( msg, &newSnap.areamask, len);
297
298 // read playerinfo
299 SHOWNET( msg, "playerstate" );
300 if ( old ) {
301 MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
302 if (newSnap.ps.m_iVehicleNum)
303 { //this means we must have written our vehicle's ps too
304 MSG_ReadDeltaPlayerstate( msg, &old->vps, &newSnap.vps, qtrue );
305 }
306 } else {
307 MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
308 if (newSnap.ps.m_iVehicleNum)
309 { //this means we must have written our vehicle's ps too
310 MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.vps, qtrue );
311 }
312 }
313
314 // read packet entities
315 SHOWNET( msg, "packet entities" );
316 CL_ParsePacketEntities( msg, old, &newSnap );
317
318 // if not valid, dump the entire thing now that it has
319 // been properly read
320 if ( !newSnap.valid ) {
321 return;
322 }
323
324 // clear the valid flags of any snapshots between the last
325 // received and this one, so if there was a dropped packet
326 // it won't look like something valid to delta from next
327 // time we wrap around in the buffer
328 oldMessageNum = cl.snap.messageNum + 1;
329
330 if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
331 oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
332 }
333 for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
334 cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
335 }
336
337 // copy to the current good spot
338 cl.snap = newSnap;
339 cl.snap.ping = 999;
340 // calculate ping time
341 for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
342 packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
343 if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
344 cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
345 break;
346 }
347 }
348 // save the frame off in the backup array for later delta comparisons
349 cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
350
351 if (cl_shownet->integer == 3) {
352 Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum,
353 cl.snap.deltaNum, cl.snap.ping );
354 }
355
356 cl.newSnapshots = qtrue;
357 }
358
359
360 /*
361 ================
362 CL_ParseSetGame
363
364 rww - Update fs_game, this message is so we can use the ext_data
365 *_overrides.txt files for mods.
366 ================
367 */
368 void MSG_CheckNETFPSFOverrides(qboolean psfOverrides);
369 void FS_UpdateGamedir(void);
CL_ParseSetGame(msg_t * msg)370 void CL_ParseSetGame( msg_t *msg )
371 {
372 char newGameDir[MAX_QPATH];
373 int i = 0;
374 char next;
375
376 while (i < MAX_QPATH)
377 {
378 next = MSG_ReadByte( msg );
379
380 if (next)
381 { //if next is 0 then we have finished reading to the end of the message
382 newGameDir[i] = next;
383 }
384 else
385 {
386 break;
387 }
388 i++;
389 }
390 newGameDir[i] = 0;
391
392 if(FS_CheckDirTraversal(newGameDir))
393 {
394 Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", newGameDir);
395 return;
396 }
397
398 if(!FS_FilenameCompare(newGameDir, BASEGAME))
399 Cvar_Set("fs_game", "");
400 else
401 Cvar_Set("fs_game", newGameDir);
402
403 if(!(Cvar_Flags("fs_game") & CVAR_MODIFIED))
404 return;
405
406 //Update the search path for the mod dir
407 FS_UpdateGamedir();
408
409 //Now update the overrides manually
410 MSG_CheckNETFPSFOverrides(qfalse);
411 MSG_CheckNETFPSFOverrides(qtrue);
412 }
413
414
415 //=====================================================================
416
417 int cl_connectedToPureServer;
418 int cl_connectedToCheatServer;
419
420 /*
421 ==================
422 CL_SystemInfoChanged
423
424 The systeminfo configstring has been changed, so parse
425 new information out of it. This will happen at every
426 gamestate, and possibly during gameplay.
427 ==================
428 */
CL_SystemInfoChanged(void)429 void CL_SystemInfoChanged( void ) {
430 char *systemInfo;
431 const char *s, *t;
432 char key[BIG_INFO_KEY];
433 char value[BIG_INFO_VALUE];
434 qboolean gameSet;
435
436 systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
437 // NOTE TTimo:
438 // when the serverId changes, any further messages we send to the server will use this new serverId
439 // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
440 // in some cases, outdated cp commands might get sent with this new serverId
441 cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
442
443 // don't set any vars when playing a demo
444 if ( clc.demoplaying ) {
445 return;
446 }
447
448 s = Info_ValueForKey( systemInfo, "sv_cheats" );
449 cl_connectedToCheatServer = atoi( s );
450 if ( !cl_connectedToCheatServer )
451 {
452 Cvar_SetCheatState();
453 }
454
455 // check pure server string
456 s = Info_ValueForKey( systemInfo, "sv_paks" );
457 t = Info_ValueForKey( systemInfo, "sv_pakNames" );
458 FS_PureServerSetLoadedPaks( s, t );
459
460 s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
461 t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
462 FS_PureServerSetReferencedPaks( s, t );
463
464 gameSet = qfalse;
465 // scan through all the variables in the systeminfo and locally set cvars to match
466 s = systemInfo;
467 while ( s ) {
468 Info_NextPair( &s, key, value );
469 if ( !key[0] ) {
470 break;
471 }
472 // ehw!
473 if ( !Q_stricmp( key, "fs_game" ) ) {
474 if(FS_CheckDirTraversal(value))
475 {
476 Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value);
477 continue;
478 }
479
480 if(!FS_FilenameCompare(value, BASEGAME))
481 {
482 Q_strncpyz(value, "", sizeof(value));
483 }
484
485 gameSet = qtrue;
486 }
487 Cvar_Server_Set( key, value );
488 }
489 // if game folder should not be set and it is set at the client side
490 if ( !gameSet && *Cvar_VariableString("fs_game") ) {
491 Cvar_Set( "fs_game", "" );
492 }
493 cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
494 }
495
496 /*
497 ==================
498 CL_ParseGamestate
499 ==================
500 */
CL_ParseGamestate(msg_t * msg)501 void CL_ParseGamestate( msg_t *msg ) {
502 int i;
503 entityState_t *es;
504 int newnum;
505 entityState_t nullstate;
506 int cmd;
507 char *s;
508
509 Con_Close();
510
511 clc.connectPacketCount = 0;
512
513 // wipe local client state
514 CL_ClearState();
515
516 // a gamestate always marks a server command sequence
517 clc.serverCommandSequence = MSG_ReadLong( msg );
518
519 // parse all the configstrings and baselines
520 cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings
521 while ( 1 ) {
522 cmd = MSG_ReadByte( msg );
523
524 if ( cmd == svc_EOF ) {
525 break;
526 }
527
528 if ( cmd == svc_configstring ) {
529 int len, start;
530
531 start = msg->readcount;
532
533 i = MSG_ReadShort( msg );
534 if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
535 Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
536 }
537 s = MSG_ReadBigString( msg );
538
539 if (cl_shownet->integer >= 2)
540 {
541 Com_Printf("%3i: %d: %s\n", start, i, s);
542 }
543
544 /*
545 if (i == CS_SERVERINFO)
546 { //get the special value here
547 char *f = strstr(s, "g_debugMelee");
548 if (f)
549 {
550 while (*f && *f != '\\')
551 { //find the \ after it
552 f++;
553 }
554 if (*f == '\\')
555 { //got it
556 int i = 0;
557
558 f++;
559 while (*f && *f != '\\' && i < 128)
560 {
561 hiddenCvarVal[i] = *f;
562 i++;
563 f++;
564 }
565 hiddenCvarVal[i] = 0;
566
567 //resume here
568 s = f;
569 }
570 }
571 }
572 */
573
574 len = strlen( s );
575
576 if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
577 Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
578 }
579
580 // append it to the gameState string buffer
581 cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
582 Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
583 cl.gameState.dataCount += len + 1;
584 } else if ( cmd == svc_baseline ) {
585 newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
586 if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
587 Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
588 }
589 Com_Memset (&nullstate, 0, sizeof(nullstate));
590 es = &cl.entityBaselines[ newnum ];
591 MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
592 } else {
593 Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
594 }
595 }
596
597 clc.clientNum = MSG_ReadLong(msg);
598 // read the checksum feed
599 clc.checksumFeed = MSG_ReadLong( msg );
600
601 // Throw away the info for the old RMG system.
602 MSG_ReadShort (msg);
603
604
605 // parse serverId and other cvars
606 CL_SystemInfoChanged();
607
608 // reinitialize the filesystem if the game directory has changed
609 if( FS_ConditionalRestart( clc.checksumFeed ) ) {
610 // don't set to true because we yet have to start downloading
611 // enabling this can cause double loading of a map when connecting to
612 // a server which has a different game directory set
613 //clc.downloadRestart = qtrue;
614 }
615
616 // This used to call CL_StartHunkUsers, but now we enter the download state before loading the
617 // cgame
618 CL_InitDownloads();
619
620 // make sure the game starts
621 Cvar_Set( "cl_paused", "0" );
622 }
623
624
625 //=====================================================================
626
627 /*
628 =====================
629 CL_ParseDownload
630
631 A download message has been received from the server
632 =====================
633 */
CL_ParseDownload(msg_t * msg)634 void CL_ParseDownload ( msg_t *msg ) {
635 int size;
636 unsigned char data[MAX_MSGLEN];
637 uint16_t block;
638
639 if (!*clc.downloadTempName) {
640 Com_Printf("Server sending download, but no download was requested\n");
641 CL_AddReliableCommand("stopdl", qfalse);
642 return;
643 }
644
645 // read the data
646 block = MSG_ReadShort ( msg );
647
648 if ( !block && !clc.downloadBlock )
649 {
650 // block zero is special, contains file size
651 clc.downloadSize = MSG_ReadLong ( msg );
652
653 Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
654
655 if (clc.downloadSize < 0)
656 {
657 Com_Error(ERR_DROP, "%s", MSG_ReadString( msg ) );
658 return;
659 }
660 }
661
662 size = /*(unsigned short)*/MSG_ReadShort ( msg );
663 if (size < 0 || size > (int)sizeof(data))
664 {
665 Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk", size);
666 return;
667 }
668
669 MSG_ReadData( msg, data, size );
670
671 if((clc.downloadBlock & 0xFFFF) != block)
672 {
673 Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", (clc.downloadBlock & 0xFFFF), block);
674 return;
675 }
676
677 // open the file if not opened yet
678 if (!clc.download)
679 {
680 clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
681
682 if (!clc.download) {
683 Com_Printf( "Could not create %s\n", clc.downloadTempName );
684 CL_AddReliableCommand( "stopdl", qfalse );
685 CL_NextDownload();
686 return;
687 }
688 }
689
690 if (size)
691 FS_Write( data, size, clc.download );
692
693 CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock), qfalse );
694 clc.downloadBlock++;
695
696 clc.downloadCount += size;
697
698 // So UI gets access to it
699 Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
700
701 if (!size) { // A zero length block means EOF
702 if (clc.download) {
703 FS_FCloseFile( clc.download );
704 clc.download = 0;
705
706 // rename the file
707 FS_SV_Rename ( clc.downloadTempName, clc.downloadName, qfalse );
708 }
709
710 // send intentions now
711 // We need this because without it, we would hold the last nextdl and then start
712 // loading right away. If we take a while to load, the server is happily trying
713 // to send us that last block over and over.
714 // Write it twice to help make sure we acknowledge the download
715 CL_WritePacket();
716 CL_WritePacket();
717
718 // get another file if needed
719 CL_NextDownload ();
720 }
721 }
722
CL_GetValueForHidden(const char * s)723 int CL_GetValueForHidden(const char *s)
724 { //string arg here just in case I want to add more sometime and make a lookup table
725 return atoi(hiddenCvarVal);
726 }
727
728 /*
729 =====================
730 CL_ParseCommandString
731
732 Command strings are just saved off until cgame asks for them
733 when it transitions a snapshot
734 =====================
735 */
CL_ParseCommandString(msg_t * msg)736 void CL_ParseCommandString( msg_t *msg ) {
737 char *s;
738 int seq;
739 int index;
740
741 seq = MSG_ReadLong( msg );
742 s = MSG_ReadString( msg );
743
744 // see if we have already executed stored it off
745 if ( clc.serverCommandSequence >= seq ) {
746 return;
747 }
748 clc.serverCommandSequence = seq;
749
750 index = seq & (MAX_RELIABLE_COMMANDS-1);
751 /*
752 if (s[0] == 'c' && s[1] == 's' && s[2] == ' ' && s[3] == '0' && s[4] == ' ')
753 { //yes.. we seem to have an incoming server info.
754 char *f = strstr(s, "g_debugMelee");
755 if (f)
756 {
757 while (*f && *f != '\\')
758 { //find the \ after it
759 f++;
760 }
761 if (*f == '\\')
762 { //got it
763 int i = 0;
764
765 f++;
766 while (*f && *f != '\\' && i < 128)
767 {
768 hiddenCvarVal[i] = *f;
769 i++;
770 f++;
771 }
772 hiddenCvarVal[i] = 0;
773
774 //don't worry about backing over beginning of string I guess,
775 //we already know we successfully strstr'd the initial string
776 //which exceeds this length.
777 //MSG_ReadString appears to just return a static buffer so I
778 //can stomp over its contents safely.
779 f--;
780 *f = '\"';
781 f--;
782 *f = ' ';
783 f--;
784 *f = '0';
785 f--;
786 *f = ' ';
787 f--;
788 *f = 's';
789 f--;
790 *f = 'c';
791
792 //the normal configstring gets to start here...
793 s = f;
794 }
795 }
796 }
797 */
798 Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
799 }
800
801
802 /*
803 =====================
804 CL_ParseServerMessage
805 =====================
806 */
CL_ParseServerMessage(msg_t * msg)807 void CL_ParseServerMessage( msg_t *msg ) {
808 int cmd;
809
810 if ( cl_shownet->integer == 1 ) {
811 Com_Printf ("%i ",msg->cursize);
812 } else if ( cl_shownet->integer >= 2 ) {
813 Com_Printf ("------------------\n");
814 }
815
816 MSG_Bitstream(msg);
817
818 // get the reliable sequence acknowledge number
819 clc.reliableAcknowledge = MSG_ReadLong( msg );
820 //
821 if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
822 clc.reliableAcknowledge = clc.reliableSequence;
823 }
824
825 //
826 // parse the message
827 //
828 while ( 1 ) {
829 if ( msg->readcount > msg->cursize ) {
830 Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
831 break;
832 }
833
834 cmd = MSG_ReadByte( msg );
835
836 if ( cmd == svc_EOF) {
837 SHOWNET( msg, "END OF MESSAGE" );
838 break;
839 }
840
841 if ( cl_shownet->integer >= 2 ) {
842 if ( !svc_strings[cmd] ) {
843 Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
844 } else {
845 SHOWNET( msg, svc_strings[cmd] );
846 }
847 }
848
849 // other commands
850 switch ( cmd ) {
851 default:
852 Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
853 break;
854 case svc_nop:
855 break;
856 case svc_serverCommand:
857 CL_ParseCommandString( msg );
858 break;
859 case svc_gamestate:
860 CL_ParseGamestate( msg );
861 break;
862 case svc_snapshot:
863 CL_ParseSnapshot( msg );
864 break;
865 case svc_setgame:
866 CL_ParseSetGame( msg );
867 break;
868 case svc_download:
869 CL_ParseDownload( msg );
870 break;
871 case svc_mapchange:
872 if ( cls.cgameStarted )
873 CGVM_MapChange();
874 break;
875 }
876 }
877 }
878