1 /*
2 Copyright (C) 1996-1997 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 */
20 // cl_parse.c -- parse a message received from the server
21
22 #include "cdaudio.h"
23 #include "client.h"
24 #include "cmd.h"
25 #include "console.h"
26 #include "model.h"
27 #include "pmove.h"
28 #include "quakedef.h"
29 #include "sbar.h"
30 #include "screen.h"
31 #include "sys.h"
32
33 #include "d_iface.h"
34
35 static const char *svc_strings[] = {
36 "svc_bad",
37 "svc_nop",
38 "svc_disconnect",
39 "svc_updatestat",
40 "svc_version", // [long] server version
41 "svc_setview", // [short] entity number
42 "svc_sound", // <see code>
43 "svc_time", // [float] server time
44 "svc_print", // [string] null terminated string
45 "svc_stufftext", // [string] stuffed into clients console buffer
46 // the string should be \n terminated
47 "svc_setangle", // [vec3] set the view angle to this absolute
48 // value
49
50 "svc_serverdata", // [long] version ...
51 "svc_lightstyle", // [byte] [string]
52 "svc_updatename", // [byte] [string]
53 "svc_updatefrags", // [byte] [short]
54 "svc_clientdata", // <shortbits + data>
55 "svc_stopsound", // <see code>
56 "svc_updatecolors", // [byte] [byte]
57 "svc_particle", // [vec3] <variable>
58 "svc_damage", // [byte] impact [byte] blood [vec3] from
59
60 "svc_spawnstatic",
61 "OBSOLETE svc_spawnbinary",
62 "svc_spawnbaseline",
63
64 "svc_temp_entity", // <variable>
65 "svc_setpause",
66 "svc_signonnum",
67 "svc_centerprint",
68 "svc_killedmonster",
69 "svc_foundsecret",
70 "svc_spawnstaticsound",
71 "svc_intermission",
72 "svc_finale",
73
74 "svc_cdtrack",
75 "svc_sellscreen",
76
77 "svc_smallkick",
78 "svc_bigkick",
79
80 "svc_updateping",
81 "svc_updateentertime",
82
83 "svc_updatestatlong",
84 "svc_muzzleflash",
85 "svc_updateuserinfo",
86 "svc_download",
87 "svc_playerinfo",
88 "svc_nails",
89 "svc_choke",
90 "svc_modellist",
91 "svc_soundlist",
92 "svc_packetentities",
93 "svc_deltapacketentities",
94 "svc_maxspeed",
95 "svc_entgravity",
96
97 "svc_setinfo",
98 "svc_serverinfo",
99 "svc_updatepl",
100 "NEW PROTOCOL",
101 "NEW PROTOCOL",
102 "NEW PROTOCOL",
103 "NEW PROTOCOL",
104 "NEW PROTOCOL",
105 "NEW PROTOCOL",
106 "NEW PROTOCOL",
107 "NEW PROTOCOL",
108 "NEW PROTOCOL",
109 "NEW PROTOCOL",
110 "NEW PROTOCOL",
111 "NEW PROTOCOL",
112 "NEW PROTOCOL"
113 };
114
115 static int oldparsecountmod;
116
117 int parsecountmod;
118 double parsecounttime;
119
120 int cl_spikeindex, cl_playerindex, cl_flagindex;
121
122 //=============================================================================
123
124 int packet_latency[NET_TIMINGS];
125
126 int
CL_CalcNet(void)127 CL_CalcNet(void)
128 {
129 int a, i;
130 frame_t *frame;
131 int lost;
132
133 for (i = cls.netchan.outgoing_sequence - UPDATE_BACKUP + 1;
134 i <= cls.netchan.outgoing_sequence; i++) {
135 frame = &cl.frames[i & UPDATE_MASK];
136 if (frame->receivedtime == -1)
137 packet_latency[i & NET_TIMINGSMASK] = 9999; // dropped
138 else if (frame->receivedtime == -2)
139 packet_latency[i & NET_TIMINGSMASK] = 10000; // choked
140 else if (frame->invalid)
141 packet_latency[i & NET_TIMINGSMASK] = 9998; // invalid delta
142 else
143 packet_latency[i & NET_TIMINGSMASK] =
144 (frame->receivedtime - frame->senttime) * 20;
145 }
146
147 lost = 0;
148 for (a = 0; a < NET_TIMINGS; a++) {
149 i = (cls.netchan.outgoing_sequence - a) & NET_TIMINGSMASK;
150 if (packet_latency[i] == 9999)
151 lost++;
152 }
153 return lost * 100 / NET_TIMINGS;
154 }
155
156 //=============================================================================
157
158 /*
159 ===============
160 CL_CheckOrDownloadFile
161
162 Returns true if the file exists, otherwise it attempts
163 to start a download from the server.
164 ===============
165 */
166 qboolean
CL_CheckOrDownloadFile(char * filename)167 CL_CheckOrDownloadFile(char *filename)
168 {
169 FILE *f;
170 int maxlen;
171
172 if (strstr(filename, "..")) {
173 Con_Printf("Refusing to download a path with ..\n");
174 return true;
175 }
176
177 COM_FOpenFile(filename, &f);
178 if (f) { // it exists, no need to download
179 fclose(f);
180 return true;
181 }
182 /* can't download when recording */
183 if (cls.demorecording) {
184 Con_Printf("Unable to download %s in record mode.\n",
185 cls.downloadname);
186 return true;
187 }
188 /* can't download when playback */
189 if (cls.demoplayback)
190 return true;
191
192 snprintf(cls.downloadname, sizeof(cls.downloadname), "%s", filename);
193 Con_Printf("Downloading %s...\n", cls.downloadname);
194
195 /*
196 * download to a temp name, and only rename to the real name when
197 * done, so if interrupted a runt file wont be left
198 */
199 strcpy(cls.downloadtempname, cls.downloadname); /* same size */
200 COM_StripExtension(cls.downloadtempname);
201
202 /* make sure the .tmp extension fits... */
203 maxlen = sizeof(cls.downloadtempname);
204 if (strlen(cls.downloadtempname) + strlen(".tmp") > maxlen - 1) {
205 Con_Printf("Refusing download, pathname too long\n");
206 return true;
207 }
208 strcat(cls.downloadtempname, ".tmp");
209
210 MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
211 MSG_WriteStringf(&cls.netchan.message, "download %s", cls.downloadname);
212
213 cls.downloadnumber++;
214
215 return false;
216 }
217
218 /*
219 =================
220 Model_NextDownload
221 =================
222 */
223 static void
Model_NextDownload(void)224 Model_NextDownload(void)
225 {
226 char *s;
227 int i;
228
229 if (cls.downloadnumber == 0) {
230 Con_Printf("Checking models...\n");
231 cls.downloadnumber = 1;
232 }
233
234 cls.downloadtype = dl_model;
235 for (; cl.model_name[cls.downloadnumber][0]; cls.downloadnumber++) {
236 s = cl.model_name[cls.downloadnumber];
237 if (s[0] == '*')
238 continue; // inline brush model
239 if (!CL_CheckOrDownloadFile(s))
240 return; // started a download
241 }
242
243 for (i = 1; i < MAX_MODELS; i++) {
244 if (!cl.model_name[i][0])
245 break;
246
247 cl.model_precache[i] = Mod_ForName(cl.model_name[i], false);
248
249 if (!cl.model_precache[i]) {
250 Con_Printf
251 ("\nThe required model file '%s' could not be found or "
252 "downloaded.\n\n",
253 cl.model_name[i]);
254 Con_Printf("You may need to download or purchase a %s client "
255 "pack in order to play on this server.\n\n",
256 gamedirfile);
257 CL_Disconnect();
258 return;
259 }
260 }
261
262 // all done
263 cl.worldmodel = cl.model_precache[1];
264 R_NewMap();
265 Hunk_Check(); // make sure nothing is hurt
266
267 // done with modellist, request first of static signon messages
268 MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
269 MSG_WriteStringf(&cls.netchan.message, "prespawn %i 0 %i",
270 cl.servercount, cl.worldmodel->checksum2);
271 }
272
273 /*
274 =================
275 Sound_NextDownload
276 =================
277 */
278 static void
Sound_NextDownload(void)279 Sound_NextDownload(void)
280 {
281 char *s;
282 int i;
283
284 if (cls.downloadnumber == 0) {
285 Con_Printf("Checking sounds...\n");
286 cls.downloadnumber = 1;
287 }
288
289 cls.downloadtype = dl_sound;
290 for (; cl.sound_name[cls.downloadnumber][0]; cls.downloadnumber++) {
291 s = cl.sound_name[cls.downloadnumber];
292 if (!CL_CheckOrDownloadFile(va("sound/%s", s)))
293 return; // started a download
294 }
295
296 for (i = 1; i < MAX_SOUNDS; i++) {
297 if (!cl.sound_name[i][0])
298 break;
299 cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i]);
300 }
301
302 // done with sounds, request models now
303 memset(cl.model_precache, 0, sizeof(cl.model_precache));
304 cl_playerindex = -1;
305 cl_spikeindex = -1;
306 cl_flagindex = -1;
307 MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
308 MSG_WriteStringf(&cls.netchan.message, "modellist %i 0", cl.servercount);
309 }
310
311
312 /*
313 ======================
314 CL_RequestNextDownload
315 ======================
316 */
317 static void
CL_RequestNextDownload(void)318 CL_RequestNextDownload(void)
319 {
320 switch (cls.downloadtype) {
321 case dl_single:
322 break;
323 case dl_skin:
324 Skin_NextDownload();
325 break;
326 case dl_model:
327 Model_NextDownload();
328 break;
329 case dl_sound:
330 Sound_NextDownload();
331 break;
332 case dl_none:
333 default:
334 Con_DPrintf("Unknown download type.\n");
335 }
336 }
337
338 /*
339 =====================
340 CL_ParseDownload
341
342 A download message has been received from the server
343 =====================
344 */
345 static void
CL_ParseDownload(void)346 CL_ParseDownload(void)
347 {
348 int size, percent;
349 char name[1024];
350 int r;
351
352
353 // read the data
354 size = MSG_ReadShort();
355 percent = MSG_ReadByte();
356
357 if (cls.demoplayback) {
358 if (size > 0)
359 msg_readcount += size;
360 return; // not in demo playback
361 }
362
363 if (size == -1) {
364 Con_Printf("File not found.\n");
365 if (cls.download) {
366 Con_Printf("cls.download shouldn't have been set\n");
367 fclose(cls.download);
368 cls.download = NULL;
369 }
370 CL_RequestNextDownload();
371 return;
372 }
373 // open the file if not opened yet
374 if (!cls.download) {
375 if (strncmp(cls.downloadtempname, "skins/", 6))
376 sprintf(name, "%s/%s", com_gamedir, cls.downloadtempname);
377 else
378 sprintf(name, "qw/%s", cls.downloadtempname);
379
380 COM_CreatePath(name);
381
382 cls.download = fopen(name, "wb");
383 if (!cls.download) {
384 msg_readcount += size;
385 Con_Printf("Failed to open %s\n", cls.downloadtempname);
386 CL_RequestNextDownload();
387 return;
388 }
389 }
390
391 fwrite(net_message.data + msg_readcount, 1, size, cls.download);
392 msg_readcount += size;
393
394 if (percent != 100) {
395 // change display routines by zoid
396 // request next block
397 #if 0
398 Con_Printf(".");
399 if (10 * (percent / 10) != cls.downloadpercent) {
400 cls.downloadpercent = 10 * (percent / 10);
401 Con_Printf("%i%%", cls.downloadpercent);
402 }
403 #endif
404 cls.downloadpercent = percent;
405
406 MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
407 MSG_WriteString(&cls.netchan.message, "nextdl");
408 } else {
409 char oldn[MAX_OSPATH];
410 char newn[MAX_OSPATH];
411
412 #if 0
413 Con_Printf("100%%\n");
414 #endif
415
416 fclose(cls.download);
417
418 // rename the temp file to it's final name
419 if (strcmp(cls.downloadtempname, cls.downloadname)) {
420 if (strncmp(cls.downloadtempname, "skins/", 6)) {
421 sprintf(oldn, "%s/%s", com_gamedir, cls.downloadtempname);
422 sprintf(newn, "%s/%s", com_gamedir, cls.downloadname);
423 } else {
424 sprintf(oldn, "qw/%s", cls.downloadtempname);
425 sprintf(newn, "qw/%s", cls.downloadname);
426 }
427 r = rename(oldn, newn);
428 if (r)
429 Con_Printf("failed to rename.\n");
430 }
431
432 cls.download = NULL;
433 cls.downloadpercent = 0;
434
435 // get another file if needed
436
437 CL_RequestNextDownload();
438 }
439 }
440
441 static byte *upload_data;
442 static int upload_pos;
443 static int upload_size;
444
445 void
CL_NextUpload(void)446 CL_NextUpload(void)
447 {
448 byte buffer[1024];
449 int r;
450 int percent;
451 int size;
452
453 if (!upload_data)
454 return;
455
456 r = upload_size - upload_pos;
457 if (r > 768)
458 r = 768;
459 memcpy(buffer, upload_data + upload_pos, r);
460 MSG_WriteByte(&cls.netchan.message, clc_upload);
461 MSG_WriteShort(&cls.netchan.message, r);
462
463 upload_pos += r;
464 size = upload_size;
465 if (!size)
466 size = 1;
467 percent = upload_pos * 100 / size;
468 MSG_WriteByte(&cls.netchan.message, percent);
469 SZ_Write(&cls.netchan.message, buffer, r);
470
471 Con_DPrintf("UPLOAD: %6d: %d written\n", upload_pos - r, r);
472
473 if (upload_pos != upload_size)
474 return;
475
476 Con_Printf("Upload completed\n");
477
478 free(upload_data);
479 upload_data = 0;
480 upload_pos = upload_size = 0;
481 }
482
483 void
CL_StartUpload(byte * data,int size)484 CL_StartUpload(byte *data, int size)
485 {
486 if (cls.state < ca_onserver)
487 return; // gotta be connected
488
489 // override
490 if (upload_data)
491 free(upload_data);
492
493 Con_DPrintf("Upload starting of %d...\n", size);
494
495 upload_data = malloc(size);
496 memcpy(upload_data, data, size);
497 upload_size = size;
498 upload_pos = 0;
499
500 CL_NextUpload();
501 }
502
503 qboolean
CL_IsUploading(void)504 CL_IsUploading(void)
505 {
506 if (upload_data)
507 return true;
508 return false;
509 }
510
511 void
CL_StopUpload(void)512 CL_StopUpload(void)
513 {
514 if (upload_data)
515 free(upload_data);
516 upload_data = NULL;
517 }
518
519 /*
520 =====================================================================
521
522 SERVER CONNECTING MESSAGES
523
524 =====================================================================
525 */
526
527 /*
528 ==================
529 CL_ParseServerData
530 ==================
531 */
532 static void
CL_ParseServerData(void)533 CL_ParseServerData(void)
534 {
535 char *str;
536 FILE *f;
537 char fn[MAX_OSPATH];
538 qboolean cflag = false;
539 int protover;
540
541 Con_DPrintf("Serverdata packet received.\n");
542 //
543 // wipe the client_state_t struct
544 //
545 CL_ClearState();
546
547 // parse protocol version number
548 // allow 2.2 and 2.29 demos to play
549 protover = MSG_ReadLong();
550 if (protover != PROTOCOL_VERSION &&
551 !(cls.demoplayback
552 && (protover == 26 || protover == 27 || protover == 28)))
553 Host_EndGame("Server returned version %i, not %i\n"
554 "You probably need to upgrade.\n"
555 "Check http://disenchant.net/",
556 protover, PROTOCOL_VERSION);
557
558 cl.servercount = MSG_ReadLong();
559
560 // game directory
561 str = MSG_ReadString();
562
563 if (strcasecmp(gamedirfile, str)) {
564 // save current config
565 Host_WriteConfiguration();
566 cflag = true;
567 }
568
569 COM_Gamedir(str);
570
571 //ZOID--run the autoexec.cfg in the gamedir
572 //if it exists
573 if (cflag) {
574 snprintf(fn, sizeof(fn), "%s/%s", com_gamedir, "config.cfg");
575 if ((f = fopen(fn, "r")) != NULL) {
576 fclose(f);
577 Cbuf_AddText("cl_warncmd 0\n");
578 Cbuf_AddText("exec config.cfg\n");
579 Cbuf_AddText("exec frontend.cfg\n");
580 Cbuf_AddText("cl_warncmd 1\n");
581 }
582 }
583 // parse player slot, high bit means spectator
584 cl.playernum = MSG_ReadByte();
585 if (cl.playernum & 128) {
586 cl.spectator = true;
587 cl.playernum &= ~128;
588 }
589 // get the full level name
590 str = MSG_ReadString();
591 snprintf(cl.levelname, sizeof(cl.levelname), "%s", str);
592
593 // get the movevars
594 movevars.gravity = MSG_ReadFloat();
595 movevars.stopspeed = MSG_ReadFloat();
596 movevars.maxspeed = MSG_ReadFloat();
597 movevars.spectatormaxspeed = MSG_ReadFloat();
598 movevars.accelerate = MSG_ReadFloat();
599 movevars.airaccelerate = MSG_ReadFloat();
600 movevars.wateraccelerate = MSG_ReadFloat();
601 movevars.friction = MSG_ReadFloat();
602 movevars.waterfriction = MSG_ReadFloat();
603 movevars.entgravity = MSG_ReadFloat();
604
605 // seperate the printfs so the server message can have a color
606 Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36"
607 "\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
608 Con_Printf("%c%s\n", 2, str);
609
610 // ask for the sound list next
611 memset(cl.sound_name, 0, sizeof(cl.sound_name));
612 MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
613 MSG_WriteStringf(&cls.netchan.message, "soundlist %i 0", cl.servercount);
614
615 // now waiting for downloads, etc
616 cls.state = ca_onserver;
617 }
618
619 /*
620 ==================
621 CL_ParseSoundlist
622 ==================
623 */
624 static void
CL_ParseSoundlist(void)625 CL_ParseSoundlist(void)
626 {
627 const int NAMELEN = sizeof(cl.sound_name[0]);
628
629 char *str, *name;
630 int numsounds;
631 int n;
632
633 // precache sounds
634 numsounds = MSG_ReadByte();
635 for (;;) {
636 str = MSG_ReadString();
637 if (!str[0])
638 break;
639 numsounds++;
640 if (numsounds == MAX_SOUNDS)
641 Host_EndGame("Server sent too many sound_precache");
642 name = cl.sound_name[numsounds];
643 snprintf(name, NAMELEN, "%s", str);
644 }
645
646 n = MSG_ReadByte();
647 if (n) {
648 MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
649 MSG_WriteStringf(&cls.netchan.message, "soundlist %i %i",
650 cl.servercount, n);
651 return;
652 }
653
654 cls.downloadnumber = 0;
655 cls.downloadtype = dl_sound;
656 Sound_NextDownload();
657 }
658
659 /*
660 ==================
661 CL_ParseModellist
662 ==================
663 */
664 static void
CL_ParseModellist(void)665 CL_ParseModellist(void)
666 {
667 const int NAMELEN = sizeof(cl.model_name[0]);
668 int nummodels;
669 char *str, *name;
670 int n;
671
672 // precache models and note certain default indexes
673 nummodels = MSG_ReadByte();
674
675 for (;;) {
676 str = MSG_ReadString();
677 if (!str[0])
678 break;
679 nummodels++;
680 if (nummodels == MAX_MODELS)
681 Host_EndGame("Server sent too many model_precache");
682 name = cl.model_name[nummodels];
683 snprintf(name, NAMELEN, "%s", str);
684
685 if (!strcmp(name, "progs/spike.mdl"))
686 cl_spikeindex = nummodels;
687 if (!strcmp(name, "progs/player.mdl"))
688 cl_playerindex = nummodels;
689 if (!strcmp(name, "progs/flag.mdl"))
690 cl_flagindex = nummodels;
691 }
692
693 n = MSG_ReadByte();
694
695 if (n) {
696 MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
697 MSG_WriteStringf(&cls.netchan.message, "modellist %i %i",
698 cl.servercount, n);
699 return;
700 }
701
702 cls.downloadnumber = 0;
703 cls.downloadtype = dl_model;
704 Model_NextDownload();
705 }
706
707 /*
708 ==================
709 CL_ParseBaseline
710 ==================
711 */
712 static void
CL_ParseBaseline(entity_state_t * es)713 CL_ParseBaseline(entity_state_t *es)
714 {
715 int i;
716
717 es->modelindex = MSG_ReadByte();
718 es->frame = MSG_ReadByte();
719 es->colormap = MSG_ReadByte();
720 es->skinnum = MSG_ReadByte();
721 for (i = 0; i < 3; i++) {
722 es->origin[i] = MSG_ReadCoord();
723 es->angles[i] = MSG_ReadAngle();
724 }
725 }
726
727
728
729 /*
730 =====================
731 CL_ParseStatic
732
733 Static entities are non-interactive world objects
734 like torches
735 =====================
736 */
737 static void
CL_ParseStatic(void)738 CL_ParseStatic(void)
739 {
740 entity_t *ent;
741 int i;
742 entity_state_t es;
743
744 CL_ParseBaseline(&es);
745
746 i = cl.num_statics;
747 if (i >= MAX_STATIC_ENTITIES)
748 Host_EndGame("Too many static entities");
749 ent = &cl_static_entities[i];
750 cl.num_statics++;
751
752 // copy it to the current state
753 ent->model = cl.model_precache[es.modelindex];
754 ent->frame = es.frame;
755 ent->colormap = vid.colormap;
756 ent->skinnum = es.skinnum;
757
758 VectorCopy(es.origin, ent->origin);
759 VectorCopy(es.angles, ent->angles);
760
761 R_AddEfrags(ent);
762 }
763
764 /*
765 ===================
766 CL_ParseStaticSound
767 ===================
768 */
769 static void
CL_ParseStaticSound(void)770 CL_ParseStaticSound(void)
771 {
772 vec3_t org;
773 int sound_num, vol, atten;
774 int i;
775
776 for (i = 0; i < 3; i++)
777 org[i] = MSG_ReadCoord();
778 sound_num = MSG_ReadByte();
779 vol = MSG_ReadByte();
780 atten = MSG_ReadByte();
781
782 S_StaticSound(cl.sound_precache[sound_num], org, vol, atten);
783 }
784
785
786
787 /*
788 =====================================================================
789
790 ACTION MESSAGES
791
792 =====================================================================
793 */
794
795 /*
796 ==================
797 CL_ParseStartSoundPacket
798 ==================
799 */
800 static void
CL_ParseStartSoundPacket(void)801 CL_ParseStartSoundPacket(void)
802 {
803 vec3_t pos;
804 int channel, ent;
805 int sound_num;
806 int volume;
807 float attenuation;
808 int i;
809
810 channel = MSG_ReadShort();
811
812 if (channel & SND_VOLUME)
813 volume = MSG_ReadByte();
814 else
815 volume = DEFAULT_SOUND_PACKET_VOLUME;
816
817 if (channel & SND_ATTENUATION)
818 attenuation = MSG_ReadByte() / 64.0;
819 else
820 attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
821
822 sound_num = MSG_ReadByte();
823
824 for (i = 0; i < 3; i++)
825 pos[i] = MSG_ReadCoord();
826
827 ent = (channel >> 3) & 1023;
828 channel &= 7;
829
830 if (ent > MAX_EDICTS)
831 Host_EndGame("%s: ent = %i", __func__, ent);
832
833 S_StartSound(ent, channel, cl.sound_precache[sound_num], pos,
834 volume / 255.0, attenuation);
835 }
836
837
838 /*
839 ==================
840 CL_ParseClientdata
841
842 Server information pertaining to this client only, sent every frame
843 ==================
844 */
845 static void
CL_ParseClientdata(void)846 CL_ParseClientdata(void)
847 {
848 int i;
849 float latency;
850 frame_t *frame;
851
852 // calculate simulated time of message
853 oldparsecountmod = parsecountmod;
854
855 i = cls.netchan.incoming_acknowledged;
856 cl.parsecount = i;
857 i &= UPDATE_MASK;
858 parsecountmod = i;
859 frame = &cl.frames[i];
860 parsecounttime = cl.frames[i].senttime;
861
862 frame->receivedtime = realtime;
863
864 // calculate latency
865 latency = frame->receivedtime - frame->senttime;
866
867 if (latency < 0 || latency > 1.0) {
868 // Con_Printf ("Odd latency: %5.2f\n", latency);
869 } else {
870 // drift the average latency towards the observed latency
871 if (latency < cls.latency)
872 cls.latency = latency;
873 else
874 cls.latency += 0.001; // drift up, so correction are needed
875 }
876 }
877
878 /*
879 =====================
880 CL_NewTranslation
881 =====================
882 */
883 static void
CL_NewTranslation(int slot)884 CL_NewTranslation(int slot)
885 {
886 int i, j;
887 int top, bottom;
888 byte *dest, *source;
889 player_info_t *player;
890 char *skin;
891
892 if (slot > MAX_CLIENTS)
893 Sys_Error("%s: slot > MAX_CLIENTS", __func__);
894
895 player = &cl.players[slot];
896
897 skin = Info_ValueForKey(player->userinfo, "skin");
898 COM_StripExtension(skin);
899
900 if (player->skin && !strcasecmp(skin, player->skin->name))
901 player->skin = NULL;
902
903 if (player->_topcolor != player->topcolor ||
904 player->_bottomcolor != player->bottomcolor || !player->skin) {
905 player->_topcolor = player->topcolor;
906 player->_bottomcolor = player->bottomcolor;
907
908 dest = player->translations;
909 source = vid.colormap;
910 memcpy(dest, vid.colormap, sizeof(player->translations));
911 top = player->topcolor;
912 if (top > 13 || top < 0)
913 top = 13;
914 top *= 16;
915 bottom = player->bottomcolor;
916 if (bottom > 13 || bottom < 0)
917 bottom = 13;
918 bottom *= 16;
919
920 for (i = 0; i < VID_GRADES; i++, dest += 256, source += 256) {
921 /* the artists made some backwards ranges. sigh. */
922 if (top < 128)
923 memcpy(dest + TOP_RANGE, source + top, 16);
924 else
925 for (j = 0; j < 16; j++)
926 dest[TOP_RANGE + j] = source[top + 15 - j];
927
928 if (bottom < 128)
929 memcpy(dest + BOTTOM_RANGE, source + bottom, 16);
930 else
931 for (j = 0; j < 16; j++)
932 dest[BOTTOM_RANGE + j] = source[bottom + 15 - j];
933 }
934 }
935 }
936
937 /*
938 ==============
939 CL_ProcessUserinfo
940 ==============
941 */
942 static void
CL_ProcessUserInfo(int slot,player_info_t * player)943 CL_ProcessUserInfo(int slot, player_info_t * player)
944 {
945 char *name;
946
947 name = Info_ValueForKey(player->userinfo, "name");
948 snprintf(player->name, sizeof(player->name), "%s", name);
949
950 player->topcolor = atoi(Info_ValueForKey(player->userinfo, "topcolor"));
951 player->bottomcolor =
952 atoi(Info_ValueForKey(player->userinfo, "bottomcolor"));
953 if (Info_ValueForKey(player->userinfo, "*spectator")[0])
954 player->spectator = true;
955 else
956 player->spectator = false;
957
958 if (cls.state == ca_active)
959 Skin_Find(player);
960
961 Sbar_Changed();
962 CL_NewTranslation(slot);
963 }
964
965 /*
966 ==============
967 CL_UpdateUserinfo
968 ==============
969 */
970 static void
CL_UpdateUserinfo(void)971 CL_UpdateUserinfo(void)
972 {
973 player_info_t *player;
974 char *info;
975 int slot;
976
977 slot = MSG_ReadByte();
978 if (slot >= MAX_CLIENTS)
979 Host_EndGame("%s: svc_updateuserinfo > MAX_SCOREBOARD", __func__);
980
981 player = &cl.players[slot];
982 player->userid = MSG_ReadLong();
983
984 info = MSG_ReadString();
985 snprintf(player->userinfo, sizeof(player->userinfo), "%s", info);
986 CL_ProcessUserInfo(slot, player);
987 }
988
989 /*
990 ==============
991 CL_SetInfo
992 ==============
993 */
994 static void
CL_SetInfo(void)995 CL_SetInfo(void)
996 {
997 char key[MAX_MSGLEN];
998 char value[MAX_MSGLEN];
999 player_info_t *player;
1000 int slot;
1001
1002 slot = MSG_ReadByte();
1003 if (slot >= MAX_CLIENTS)
1004 Host_EndGame("%s: svc_setinfo > MAX_SCOREBOARD", __func__);
1005
1006 player = &cl.players[slot];
1007 snprintf(key, sizeof(key), "%s", MSG_ReadString());
1008 snprintf(value, sizeof(value), "%s", MSG_ReadString());
1009
1010 Con_DPrintf("SETINFO %s: %s=%s\n", player->name, key, value);
1011
1012 Info_SetValueForKey(player->userinfo, key, value, MAX_INFO_STRING);
1013
1014 CL_ProcessUserInfo(slot, player);
1015 }
1016
1017 /*
1018 ==============
1019 CL_ServerInfo
1020 ==============
1021 */
1022 static void
CL_ServerInfo(void)1023 CL_ServerInfo(void)
1024 {
1025 char key[MAX_MSGLEN];
1026 char value[MAX_MSGLEN];
1027
1028 snprintf(key, sizeof(key), "%s", MSG_ReadString());
1029 snprintf(value, sizeof(value), "%s", MSG_ReadString());
1030
1031 Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
1032
1033 Info_SetValueForKey(cl.serverinfo, key, value, MAX_SERVERINFO_STRING);
1034 }
1035
1036 /*
1037 =====================
1038 CL_SetStat
1039 =====================
1040 */
1041 static void
CL_SetStat(int stat,int value)1042 CL_SetStat(int stat, int value)
1043 {
1044 int j;
1045
1046 if (stat < 0 || stat >= MAX_CL_STATS)
1047 Sys_Error("%s: %i is invalid", __func__, stat);
1048
1049 Sbar_Changed();
1050
1051 if (stat == STAT_ITEMS) { // set flash times
1052 Sbar_Changed();
1053 for (j = 0; j < 32; j++)
1054 if ((value & (1 << j)) && !(cl.stats[stat] & (1 << j)))
1055 cl.item_gettime[j] = cl.time;
1056 }
1057
1058 cl.stats[stat] = value;
1059 }
1060
1061 /*
1062 ==============
1063 CL_MuzzleFlash
1064 ==============
1065 */
1066 static void
CL_MuzzleFlash(void)1067 CL_MuzzleFlash(void)
1068 {
1069 vec3_t fv, rv, uv;
1070 dlight_t *dl;
1071 int i;
1072 player_state_t *pl;
1073
1074 i = MSG_ReadShort();
1075
1076 if ((unsigned)(i - 1) >= MAX_CLIENTS)
1077 return;
1078
1079 pl = &cl.frames[parsecountmod].playerstate[i - 1];
1080
1081 dl = CL_AllocDlight(i);
1082 VectorCopy(pl->origin, dl->origin);
1083 AngleVectors(pl->viewangles, fv, rv, uv);
1084
1085 VectorMA(dl->origin, 18, fv, dl->origin);
1086 dl->radius = 200 + (rand() & 31);
1087 dl->minlight = 32;
1088 dl->die = cl.time + 0.1;
1089 dl->color = dl_colors[DLIGHT_FLASH];
1090 }
1091
1092
1093 #define SHOWNET(x) \
1094 do { \
1095 if(cl_shownet.value == 2) \
1096 Con_Printf ("%3i:%s\n", msg_readcount - 1, x); \
1097 } while (0)
1098
1099 /*
1100 =====================
1101 CL_ParseServerMessage
1102 =====================
1103 */
1104 int received_framecount;
1105 void
CL_ParseServerMessage(void)1106 CL_ParseServerMessage(void)
1107 {
1108 int cmd;
1109 char *s;
1110 int i, j;
1111
1112 received_framecount = host_framecount;
1113 cl.last_servermessage = realtime;
1114 CL_ClearProjectiles();
1115
1116 //
1117 // if recording demos, copy the message out
1118 //
1119 if (cl_shownet.value == 1)
1120 Con_Printf("%i ", net_message.cursize);
1121 else if (cl_shownet.value == 2)
1122 Con_Printf("------------------\n");
1123
1124
1125 CL_ParseClientdata();
1126
1127 //
1128 // parse the message
1129 //
1130 while (1) {
1131 if (msg_badread)
1132 Host_EndGame("%s: Bad server message", __func__);
1133
1134 cmd = MSG_ReadByte();
1135
1136 if (cmd == -1) {
1137 msg_readcount++; // so the EOM showner has the right value
1138 SHOWNET("END OF MESSAGE");
1139 break;
1140 }
1141
1142 SHOWNET(svc_strings[cmd]);
1143
1144 // other commands
1145 switch (cmd) {
1146 case svc_nop:
1147 // Con_Printf ("svc_nop\n");
1148 break;
1149
1150 case svc_disconnect:
1151 if (cls.state == ca_connected)
1152 Host_EndGame("Server disconnected\n"
1153 "Server version may not be compatible");
1154 else
1155 Host_EndGame("Server disconnected");
1156 break;
1157
1158 case svc_print:
1159 i = MSG_ReadByte();
1160 if (i == PRINT_CHAT) {
1161 S_LocalSound("misc/talk.wav");
1162 con_ormask = 128;
1163 }
1164 Con_Printf("%s", MSG_ReadString());
1165 con_ormask = 0;
1166 break;
1167
1168 case svc_centerprint:
1169 SCR_CenterPrint(MSG_ReadString());
1170 break;
1171
1172 case svc_stufftext:
1173 Cbuf_AddText("%s", MSG_ReadString());
1174 break;
1175
1176 case svc_damage:
1177 V_ParseDamage();
1178 break;
1179
1180 case svc_serverdata:
1181 Cbuf_Execute(); // make sure any stuffed commands are done
1182 CL_ParseServerData();
1183 vid.recalc_refdef = true; // leave full screen intermission
1184 break;
1185
1186 case svc_setangle:
1187 for (i = 0; i < 3; i++)
1188 cl.viewangles[i] = MSG_ReadAngle();
1189 // cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0;
1190 break;
1191
1192 case svc_lightstyle:
1193 i = MSG_ReadByte();
1194 if (i >= MAX_LIGHTSTYLES)
1195 Sys_Error("svc_lightstyle > MAX_LIGHTSTYLES");
1196 s = MSG_ReadString();
1197 snprintf(cl_lightstyle[i].map, MAX_STYLESTRING, "%s", s);
1198 cl_lightstyle[i].length = strlen(cl_lightstyle[i].map);
1199 break;
1200
1201 case svc_sound:
1202 CL_ParseStartSoundPacket();
1203 break;
1204
1205 case svc_stopsound:
1206 i = MSG_ReadShort();
1207 S_StopSound(i >> 3, i & 7);
1208 break;
1209
1210 case svc_updatefrags:
1211 Sbar_Changed();
1212 i = MSG_ReadByte();
1213 if (i >= MAX_CLIENTS)
1214 Host_EndGame("%s: svc_updatefrags > MAX_SCOREBOARD", __func__);
1215 cl.players[i].frags = MSG_ReadShort();
1216 break;
1217
1218 case svc_updateping:
1219 i = MSG_ReadByte();
1220 if (i >= MAX_CLIENTS)
1221 Host_EndGame("%s: svc_updateping > MAX_SCOREBOARD", __func__);
1222 cl.players[i].ping = MSG_ReadShort();
1223 break;
1224
1225 case svc_updatepl:
1226 i = MSG_ReadByte();
1227 if (i >= MAX_CLIENTS)
1228 Host_EndGame("%s: svc_updatepl > MAX_SCOREBOARD", __func__);
1229 cl.players[i].pl = MSG_ReadByte();
1230 break;
1231
1232 case svc_updateentertime:
1233 // time is sent over as seconds ago
1234 i = MSG_ReadByte();
1235 if (i >= MAX_CLIENTS)
1236 Host_EndGame("%s: svc_updateentertime > MAX_SCOREBOARD",
1237 __func__);
1238 cl.players[i].entertime = realtime - MSG_ReadFloat();
1239 break;
1240
1241 case svc_spawnbaseline:
1242 i = MSG_ReadShort();
1243 CL_ParseBaseline(&cl_baselines[i]);
1244 break;
1245 case svc_spawnstatic:
1246 CL_ParseStatic();
1247 break;
1248 case svc_temp_entity:
1249 CL_ParseTEnt();
1250 break;
1251
1252 case svc_killedmonster:
1253 cl.stats[STAT_MONSTERS]++;
1254 break;
1255
1256 case svc_foundsecret:
1257 cl.stats[STAT_SECRETS]++;
1258 break;
1259
1260 case svc_updatestat:
1261 i = MSG_ReadByte();
1262 j = MSG_ReadByte();
1263 CL_SetStat(i, j);
1264 break;
1265 case svc_updatestatlong:
1266 i = MSG_ReadByte();
1267 j = MSG_ReadLong();
1268 CL_SetStat(i, j);
1269 break;
1270
1271 case svc_spawnstaticsound:
1272 CL_ParseStaticSound();
1273 break;
1274
1275 case svc_cdtrack:
1276 cl.cdtrack = MSG_ReadByte();
1277 CDAudio_Play((byte)cl.cdtrack, true);
1278 break;
1279
1280 case svc_intermission:
1281 cl.intermission = 1;
1282 cl.completed_time = realtime;
1283 vid.recalc_refdef = true; // go to full screen
1284 for (i = 0; i < 3; i++)
1285 cl.simorg[i] = MSG_ReadCoord();
1286 for (i = 0; i < 3; i++)
1287 cl.simangles[i] = MSG_ReadAngle();
1288 VectorCopy(vec3_origin, cl.simvel);
1289 break;
1290
1291 case svc_finale:
1292 cl.intermission = 2;
1293 cl.completed_time = realtime;
1294 vid.recalc_refdef = true; // go to full screen
1295 SCR_CenterPrint(MSG_ReadString());
1296 break;
1297
1298 case svc_sellscreen:
1299 Cmd_ExecuteString("help");
1300 break;
1301
1302 case svc_smallkick:
1303 cl.punchangle = -2;
1304 break;
1305 case svc_bigkick:
1306 cl.punchangle = -4;
1307 break;
1308
1309 case svc_muzzleflash:
1310 CL_MuzzleFlash();
1311 break;
1312
1313 case svc_updateuserinfo:
1314 CL_UpdateUserinfo();
1315 break;
1316
1317 case svc_setinfo:
1318 CL_SetInfo();
1319 break;
1320
1321 case svc_serverinfo:
1322 CL_ServerInfo();
1323 break;
1324
1325 case svc_download:
1326 CL_ParseDownload();
1327 break;
1328
1329 case svc_playerinfo:
1330 CL_ParsePlayerinfo();
1331 break;
1332
1333 case svc_nails:
1334 CL_ParseProjectiles();
1335 break;
1336
1337 case svc_chokecount: // some preceding packets were choked
1338 i = MSG_ReadByte();
1339 for (j = 0; j < i; j++)
1340 cl.frames[(cls.netchan.incoming_acknowledged - 1 -
1341 j) & UPDATE_MASK].receivedtime = -2;
1342 break;
1343
1344 case svc_modellist:
1345 CL_ParseModellist();
1346 break;
1347
1348 case svc_soundlist:
1349 CL_ParseSoundlist();
1350 break;
1351
1352 case svc_packetentities:
1353 CL_ParsePacketEntities(false);
1354 break;
1355
1356 case svc_deltapacketentities:
1357 CL_ParsePacketEntities(true);
1358 break;
1359
1360 case svc_maxspeed:
1361 movevars.maxspeed = MSG_ReadFloat();
1362 break;
1363
1364 case svc_entgravity:
1365 movevars.entgravity = MSG_ReadFloat();
1366 break;
1367
1368 case svc_setpause:
1369 cl.paused = MSG_ReadByte();
1370 if (cl.paused)
1371 CDAudio_Pause();
1372 else
1373 CDAudio_Resume();
1374 break;
1375
1376 default:
1377 Host_EndGame("%s: Illegible server message", __func__);
1378 }
1379 }
1380
1381 CL_SetSolidEntities();
1382 }
1383