cl_parse.c (18380B)
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 */ 20 // cl_parse.c -- parse a message received from the server 21 22 #include "client.h" 23 24 char *svc_strings[256] = 25 { 26 "svc_bad", 27 28 "svc_muzzleflash", 29 "svc_muzzlflash2", 30 "svc_temp_entity", 31 "svc_layout", 32 "svc_inventory", 33 34 "svc_nop", 35 "svc_disconnect", 36 "svc_reconnect", 37 "svc_sound", 38 "svc_print", 39 "svc_stufftext", 40 "svc_serverdata", 41 "svc_configstring", 42 "svc_spawnbaseline", 43 "svc_centerprint", 44 "svc_download", 45 "svc_playerinfo", 46 "svc_packetentities", 47 "svc_deltapacketentities", 48 "svc_frame" 49 }; 50 51 //============================================================================= 52 53 void CL_DownloadFileName(char *dest, int destlen, char *fn) 54 { 55 if (strncmp(fn, "players", 7) == 0) 56 Com_sprintf (dest, destlen, "%s/%s", BASEDIRNAME, fn); 57 else 58 Com_sprintf (dest, destlen, "%s/%s", FS_Gamedir(), fn); 59 } 60 61 /* 62 =============== 63 CL_CheckOrDownloadFile 64 65 Returns true if the file exists, otherwise it attempts 66 to start a download from the server. 67 =============== 68 */ 69 qboolean CL_CheckOrDownloadFile (char *filename) 70 { 71 FILE *fp; 72 char name[MAX_OSPATH]; 73 74 if (strstr (filename, "..")) 75 { 76 Com_Printf ("Refusing to download a path with ..\n"); 77 return true; 78 } 79 80 if (FS_LoadFile (filename, NULL) != -1) 81 { // it exists, no need to download 82 return true; 83 } 84 85 strcpy (cls.downloadname, filename); 86 87 // download to a temp name, and only rename 88 // to the real name when done, so if interrupted 89 // a runt file wont be left 90 COM_StripExtension (cls.downloadname, cls.downloadtempname); 91 strcat (cls.downloadtempname, ".tmp"); 92 93 //ZOID 94 // check to see if we already have a tmp for this file, if so, try to resume 95 // open the file if not opened yet 96 CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); 97 98 // FS_CreatePath (name); 99 100 fp = fopen (name, "r+b"); 101 if (fp) { // it exists 102 int len; 103 fseek(fp, 0, SEEK_END); 104 len = ftell(fp); 105 106 cls.download = fp; 107 108 // give the server an offset to start the download 109 Com_Printf ("Resuming %s\n", cls.downloadname); 110 MSG_WriteByte (&cls.netchan.message, clc_stringcmd); 111 MSG_WriteString (&cls.netchan.message, 112 va("download %s %i", cls.downloadname, len)); 113 } else { 114 Com_Printf ("Downloading %s\n", cls.downloadname); 115 MSG_WriteByte (&cls.netchan.message, clc_stringcmd); 116 MSG_WriteString (&cls.netchan.message, 117 va("download %s", cls.downloadname)); 118 } 119 120 cls.downloadnumber++; 121 122 return false; 123 } 124 125 /* 126 =============== 127 CL_Download_f 128 129 Request a download from the server 130 =============== 131 */ 132 void CL_Download_f (void) 133 { 134 char filename[MAX_OSPATH]; 135 136 if (Cmd_Argc() != 2) { 137 Com_Printf("Usage: download <filename>\n"); 138 return; 139 } 140 141 Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1)); 142 143 if (strstr (filename, "..")) 144 { 145 Com_Printf ("Refusing to download a path with ..\n"); 146 return; 147 } 148 149 if (FS_LoadFile (filename, NULL) != -1) 150 { // it exists, no need to download 151 Com_Printf("File already exists.\n"); 152 return; 153 } 154 155 strcpy (cls.downloadname, filename); 156 Com_Printf ("Downloading %s\n", cls.downloadname); 157 158 // download to a temp name, and only rename 159 // to the real name when done, so if interrupted 160 // a runt file wont be left 161 COM_StripExtension (cls.downloadname, cls.downloadtempname); 162 strcat (cls.downloadtempname, ".tmp"); 163 164 MSG_WriteByte (&cls.netchan.message, clc_stringcmd); 165 MSG_WriteString (&cls.netchan.message, 166 va("download %s", cls.downloadname)); 167 168 cls.downloadnumber++; 169 } 170 171 /* 172 ====================== 173 CL_RegisterSounds 174 ====================== 175 */ 176 void CL_RegisterSounds (void) 177 { 178 int i; 179 180 S_BeginRegistration (); 181 CL_RegisterTEntSounds (); 182 for (i=1 ; i<MAX_SOUNDS ; i++) 183 { 184 if (!cl.configstrings[CS_SOUNDS+i][0]) 185 break; 186 cl.sound_precache[i] = S_RegisterSound (cl.configstrings[CS_SOUNDS+i]); 187 Sys_SendKeyEvents (); // pump message loop 188 } 189 S_EndRegistration (); 190 } 191 192 193 /* 194 ===================== 195 CL_ParseDownload 196 197 A download message has been received from the server 198 ===================== 199 */ 200 void CL_ParseDownload (void) 201 { 202 int size, percent; 203 char name[MAX_OSPATH]; 204 int r; 205 206 // read the data 207 size = MSG_ReadShort (&net_message); 208 percent = MSG_ReadByte (&net_message); 209 if (size == -1) 210 { 211 Com_Printf ("Server does not have this file.\n"); 212 if (cls.download) 213 { 214 // if here, we tried to resume a file but the server said no 215 fclose (cls.download); 216 cls.download = NULL; 217 } 218 CL_RequestNextDownload (); 219 return; 220 } 221 222 // open the file if not opened yet 223 if (!cls.download) 224 { 225 CL_DownloadFileName(name, sizeof(name), cls.downloadtempname); 226 227 FS_CreatePath (name); 228 229 cls.download = fopen (name, "wb"); 230 if (!cls.download) 231 { 232 net_message.readcount += size; 233 Com_Printf ("Failed to open %s\n", cls.downloadtempname); 234 CL_RequestNextDownload (); 235 return; 236 } 237 } 238 239 fwrite (net_message.data + net_message.readcount, 1, size, cls.download); 240 net_message.readcount += size; 241 242 if (percent != 100) 243 { 244 // request next block 245 // change display routines by zoid 246 #if 0 247 Com_Printf ("."); 248 if (10*(percent/10) != cls.downloadpercent) 249 { 250 cls.downloadpercent = 10*(percent/10); 251 Com_Printf ("%i%%", cls.downloadpercent); 252 } 253 #endif 254 cls.downloadpercent = percent; 255 256 MSG_WriteByte (&cls.netchan.message, clc_stringcmd); 257 SZ_Print (&cls.netchan.message, "nextdl"); 258 } 259 else 260 { 261 char oldn[MAX_OSPATH]; 262 char newn[MAX_OSPATH]; 263 264 // Com_Printf ("100%%\n"); 265 266 fclose (cls.download); 267 268 // rename the temp file to it's final name 269 CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname); 270 CL_DownloadFileName(newn, sizeof(newn), cls.downloadname); 271 r = rename (oldn, newn); 272 if (r) 273 Com_Printf ("failed to rename.\n"); 274 275 cls.download = NULL; 276 cls.downloadpercent = 0; 277 278 // get another file if needed 279 280 CL_RequestNextDownload (); 281 } 282 } 283 284 285 /* 286 ===================================================================== 287 288 SERVER CONNECTING MESSAGES 289 290 ===================================================================== 291 */ 292 293 /* 294 ================== 295 CL_ParseServerData 296 ================== 297 */ 298 void CL_ParseServerData (void) 299 { 300 extern cvar_t *fs_gamedirvar; 301 char *str; 302 int i; 303 304 Com_DPrintf ("Serverdata packet received.\n"); 305 // 306 // wipe the client_state_t struct 307 // 308 CL_ClearState (); 309 cls.state = ca_connected; 310 311 // parse protocol version number 312 i = MSG_ReadLong (&net_message); 313 cls.serverProtocol = i; 314 315 // BIG HACK to let demos from release work with the 3.0x patch!!! 316 if (Com_ServerState() && PROTOCOL_VERSION == 34) 317 { 318 } 319 else if (i != PROTOCOL_VERSION) 320 Com_Error (ERR_DROP,"Server returned version %i, not %i", i, PROTOCOL_VERSION); 321 322 cl.servercount = MSG_ReadLong (&net_message); 323 cl.attractloop = MSG_ReadByte (&net_message); 324 325 // game directory 326 str = MSG_ReadString (&net_message); 327 strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1); 328 329 // set gamedir 330 if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string))) 331 Cvar_Set("game", str); 332 333 // parse player entity number 334 cl.playernum = MSG_ReadShort (&net_message); 335 336 // get the full level name 337 str = MSG_ReadString (&net_message); 338 339 if (cl.playernum == -1) 340 { // playing a cinematic or showing a pic, not a level 341 SCR_PlayCinematic (str); 342 } 343 else 344 { 345 // seperate the printfs so the server message can have a color 346 Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n"); 347 Com_Printf ("%c%s\n", 2, str); 348 349 // need to prep refresh at next oportunity 350 cl.refresh_prepped = false; 351 } 352 } 353 354 /* 355 ================== 356 CL_ParseBaseline 357 ================== 358 */ 359 void CL_ParseBaseline (void) 360 { 361 entity_state_t *es; 362 int bits; 363 int newnum; 364 entity_state_t nullstate; 365 366 memset (&nullstate, 0, sizeof(nullstate)); 367 368 newnum = CL_ParseEntityBits (&bits); 369 es = &cl_entities[newnum].baseline; 370 CL_ParseDelta (&nullstate, es, newnum, bits); 371 } 372 373 374 /* 375 ================ 376 CL_LoadClientinfo 377 378 ================ 379 */ 380 void CL_LoadClientinfo (clientinfo_t *ci, char *s) 381 { 382 int i; 383 char *t; 384 char model_name[MAX_QPATH]; 385 char skin_name[MAX_QPATH]; 386 char model_filename[MAX_QPATH]; 387 char skin_filename[MAX_QPATH]; 388 char weapon_filename[MAX_QPATH]; 389 390 strncpy(ci->cinfo, s, sizeof(ci->cinfo)); 391 ci->cinfo[sizeof(ci->cinfo)-1] = 0; 392 393 // isolate the player's name 394 strncpy(ci->name, s, sizeof(ci->name)); 395 ci->name[sizeof(ci->name)-1] = 0; 396 t = strstr (s, "\\"); 397 if (t) 398 { 399 ci->name[t-s] = 0; 400 s = t+1; 401 } 402 403 if (cl_noskins->value || *s == 0) 404 { 405 Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2"); 406 Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/weapon.md2"); 407 Com_sprintf (skin_filename, sizeof(skin_filename), "players/male/grunt.pcx"); 408 Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/male/grunt_i.pcx"); 409 ci->model = re.RegisterModel (model_filename); 410 memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel)); 411 ci->weaponmodel[0] = re.RegisterModel (weapon_filename); 412 ci->skin = re.RegisterSkin (skin_filename); 413 ci->icon = re.RegisterPic (ci->iconname); 414 } 415 else 416 { 417 // isolate the model name 418 strcpy (model_name, s); 419 t = strstr(model_name, "/"); 420 if (!t) 421 t = strstr(model_name, "\\"); 422 if (!t) 423 t = model_name; 424 *t = 0; 425 426 // isolate the skin name 427 strcpy (skin_name, s + strlen(model_name) + 1); 428 429 // model file 430 Com_sprintf (model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name); 431 ci->model = re.RegisterModel (model_filename); 432 if (!ci->model) 433 { 434 strcpy(model_name, "male"); 435 Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2"); 436 ci->model = re.RegisterModel (model_filename); 437 } 438 439 // skin file 440 Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name); 441 ci->skin = re.RegisterSkin (skin_filename); 442 443 // if we don't have the skin and the model wasn't male, 444 // see if the male has it (this is for CTF's skins) 445 if (!ci->skin && Q_stricmp(model_name, "male")) 446 { 447 // change model to male 448 strcpy(model_name, "male"); 449 Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2"); 450 ci->model = re.RegisterModel (model_filename); 451 452 // see if the skin exists for the male model 453 Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name); 454 ci->skin = re.RegisterSkin (skin_filename); 455 } 456 457 // if we still don't have a skin, it means that the male model didn't have 458 // it, so default to grunt 459 if (!ci->skin) { 460 // see if the skin exists for the male model 461 Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name, skin_name); 462 ci->skin = re.RegisterSkin (skin_filename); 463 } 464 465 // weapon file 466 for (i = 0; i < num_cl_weaponmodels; i++) { 467 Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]); 468 ci->weaponmodel[i] = re.RegisterModel(weapon_filename); 469 if (!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0) { 470 // try male 471 Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]); 472 ci->weaponmodel[i] = re.RegisterModel(weapon_filename); 473 } 474 if (!cl_vwep->value) 475 break; // only one when vwep is off 476 } 477 478 // icon file 479 Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name); 480 ci->icon = re.RegisterPic (ci->iconname); 481 } 482 483 // must have loaded all data types to be valud 484 if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0]) 485 { 486 ci->skin = NULL; 487 ci->icon = NULL; 488 ci->model = NULL; 489 ci->weaponmodel[0] = NULL; 490 return; 491 } 492 } 493 494 /* 495 ================ 496 CL_ParseClientinfo 497 498 Load the skin, icon, and model for a client 499 ================ 500 */ 501 void CL_ParseClientinfo (int player) 502 { 503 char *s; 504 clientinfo_t *ci; 505 506 s = cl.configstrings[player+CS_PLAYERSKINS]; 507 508 ci = &cl.clientinfo[player]; 509 510 CL_LoadClientinfo (ci, s); 511 } 512 513 514 /* 515 ================ 516 CL_ParseConfigString 517 ================ 518 */ 519 void CL_ParseConfigString (void) 520 { 521 int i; 522 char *s; 523 524 i = MSG_ReadShort (&net_message); 525 if (i < 0 || i >= MAX_CONFIGSTRINGS) 526 Com_Error (ERR_DROP, "configstring > MAX_CONFIGSTRINGS"); 527 s = MSG_ReadString(&net_message); 528 strcpy (cl.configstrings[i], s); 529 530 // do something apropriate 531 532 if (i >= CS_LIGHTS && i < CS_LIGHTS+MAX_LIGHTSTYLES) 533 CL_SetLightstyle (i - CS_LIGHTS); 534 else if (i == CS_CDTRACK) 535 { 536 if (cl.refresh_prepped) 537 CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true); 538 } 539 else if (i >= CS_MODELS && i < CS_MODELS+MAX_MODELS) 540 { 541 if (cl.refresh_prepped) 542 { 543 cl.model_draw[i-CS_MODELS] = re.RegisterModel (cl.configstrings[i]); 544 if (cl.configstrings[i][0] == '*') 545 cl.model_clip[i-CS_MODELS] = CM_InlineModel (cl.configstrings[i]); 546 else 547 cl.model_clip[i-CS_MODELS] = NULL; 548 } 549 } 550 else if (i >= CS_SOUNDS && i < CS_SOUNDS+MAX_MODELS) 551 { 552 if (cl.refresh_prepped) 553 cl.sound_precache[i-CS_SOUNDS] = S_RegisterSound (cl.configstrings[i]); 554 } 555 else if (i >= CS_IMAGES && i < CS_IMAGES+MAX_MODELS) 556 { 557 if (cl.refresh_prepped) 558 cl.image_precache[i-CS_IMAGES] = re.RegisterPic (cl.configstrings[i]); 559 } 560 else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS+MAX_CLIENTS) 561 { 562 if (cl.refresh_prepped) 563 CL_ParseClientinfo (i-CS_PLAYERSKINS); 564 } 565 } 566 567 568 /* 569 ===================================================================== 570 571 ACTION MESSAGES 572 573 ===================================================================== 574 */ 575 576 /* 577 ================== 578 CL_ParseStartSoundPacket 579 ================== 580 */ 581 void CL_ParseStartSoundPacket(void) 582 { 583 vec3_t pos_v; 584 float *pos; 585 int channel, ent; 586 int sound_num; 587 float volume; 588 float attenuation; 589 int flags; 590 float ofs; 591 592 flags = MSG_ReadByte (&net_message); 593 sound_num = MSG_ReadByte (&net_message); 594 595 if (flags & SND_VOLUME) 596 volume = MSG_ReadByte (&net_message) / 255.0; 597 else 598 volume = DEFAULT_SOUND_PACKET_VOLUME; 599 600 if (flags & SND_ATTENUATION) 601 attenuation = MSG_ReadByte (&net_message) / 64.0; 602 else 603 attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; 604 605 if (flags & SND_OFFSET) 606 ofs = MSG_ReadByte (&net_message) / 1000.0; 607 else 608 ofs = 0; 609 610 if (flags & SND_ENT) 611 { // entity reletive 612 channel = MSG_ReadShort(&net_message); 613 ent = channel>>3; 614 if (ent > MAX_EDICTS) 615 Com_Error (ERR_DROP,"CL_ParseStartSoundPacket: ent = %i", ent); 616 617 channel &= 7; 618 } 619 else 620 { 621 ent = 0; 622 channel = 0; 623 } 624 625 if (flags & SND_POS) 626 { // positioned in space 627 MSG_ReadPos (&net_message, pos_v); 628 629 pos = pos_v; 630 } 631 else // use entity number 632 pos = NULL; 633 634 if (!cl.sound_precache[sound_num]) 635 return; 636 637 S_StartSound (pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs); 638 } 639 640 641 void SHOWNET(char *s) 642 { 643 if (cl_shownet->value>=2) 644 Com_Printf ("%3i:%s\n", net_message.readcount-1, s); 645 } 646 647 /* 648 ===================== 649 CL_ParseServerMessage 650 ===================== 651 */ 652 void CL_ParseServerMessage (void) 653 { 654 int cmd; 655 char *s; 656 int i; 657 658 // 659 // if recording demos, copy the message out 660 // 661 if (cl_shownet->value == 1) 662 Com_Printf ("%i ",net_message.cursize); 663 else if (cl_shownet->value >= 2) 664 Com_Printf ("------------------\n"); 665 666 667 // 668 // parse the message 669 // 670 while (1) 671 { 672 if (net_message.readcount > net_message.cursize) 673 { 674 Com_Error (ERR_DROP,"CL_ParseServerMessage: Bad server message"); 675 break; 676 } 677 678 cmd = MSG_ReadByte (&net_message); 679 680 if (cmd == -1) 681 { 682 SHOWNET("END OF MESSAGE"); 683 break; 684 } 685 686 if (cl_shownet->value>=2) 687 { 688 if (!svc_strings[cmd]) 689 Com_Printf ("%3i:BAD CMD %i\n", net_message.readcount-1,cmd); 690 else 691 SHOWNET(svc_strings[cmd]); 692 } 693 694 // other commands 695 switch (cmd) 696 { 697 default: 698 Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n"); 699 break; 700 701 case svc_nop: 702 // Com_Printf ("svc_nop\n"); 703 break; 704 705 case svc_disconnect: 706 Com_Error (ERR_DISCONNECT,"Server disconnected\n"); 707 break; 708 709 case svc_reconnect: 710 Com_Printf ("Server disconnected, reconnecting\n"); 711 if (cls.download) { 712 //ZOID, close download 713 fclose (cls.download); 714 cls.download = NULL; 715 } 716 cls.state = ca_connecting; 717 cls.connect_time = -99999; // CL_CheckForResend() will fire immediately 718 break; 719 720 case svc_print: 721 i = MSG_ReadByte (&net_message); 722 if (i == PRINT_CHAT) 723 { 724 S_StartLocalSound ("misc/talk.wav"); 725 con.ormask = 128; 726 } 727 Com_Printf ("%s", MSG_ReadString (&net_message)); 728 con.ormask = 0; 729 break; 730 731 case svc_centerprint: 732 SCR_CenterPrint (MSG_ReadString (&net_message)); 733 break; 734 735 case svc_stufftext: 736 s = MSG_ReadString (&net_message); 737 Com_DPrintf ("stufftext: %s\n", s); 738 Cbuf_AddText (s); 739 break; 740 741 case svc_serverdata: 742 Cbuf_Execute (); // make sure any stuffed commands are done 743 CL_ParseServerData (); 744 break; 745 746 case svc_configstring: 747 CL_ParseConfigString (); 748 break; 749 750 case svc_sound: 751 CL_ParseStartSoundPacket(); 752 break; 753 754 case svc_spawnbaseline: 755 CL_ParseBaseline (); 756 break; 757 758 case svc_temp_entity: 759 CL_ParseTEnt (); 760 break; 761 762 case svc_muzzleflash: 763 CL_ParseMuzzleFlash (); 764 break; 765 766 case svc_muzzleflash2: 767 CL_ParseMuzzleFlash2 (); 768 break; 769 770 case svc_download: 771 CL_ParseDownload (); 772 break; 773 774 case svc_frame: 775 CL_ParseFrame (); 776 break; 777 778 case svc_inventory: 779 CL_ParseInventory (); 780 break; 781 782 case svc_layout: 783 s = MSG_ReadString (&net_message); 784 strncpy (cl.layout, s, sizeof(cl.layout)-1); 785 break; 786 787 case svc_playerinfo: 788 case svc_packetentities: 789 case svc_deltapacketentities: 790 Com_Error (ERR_DROP, "Out of place frame data"); 791 break; 792 } 793 } 794 795 CL_AddNetgraph (); 796 797 // 798 // we don't know if it is ok to save a demo message until 799 // after we have parsed the frame 800 // 801 if (cls.demorecording && !cls.demowaiting) 802 CL_WriteDemoMessage (); 803 804 } 805 806