1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20// sv_user.c -- server code for moving users 21 22#include "qwsvdef.h" 23 24edict_t *sv_player; 25 26usercmd_t cmd; 27 28cvar_t cl_rollspeed = {"cl_rollspeed", "200"}; 29cvar_t cl_rollangle = {"cl_rollangle", "2.0"}; 30cvar_t sv_spectalk = {"sv_spectalk", "1"}; 31 32cvar_t sv_mapcheck = {"sv_mapcheck", "1"}; 33 34extern vec3_t player_mins; 35 36extern int fp_messages, fp_persecond, fp_secondsdead; 37extern char fp_msg[]; 38extern cvar_t pausable; 39 40/* 41============================================================ 42 43USER STRINGCMD EXECUTION 44 45host_client and sv_player will be valid. 46============================================================ 47*/ 48 49/* 50================ 51SV_New_f 52 53Sends the first message from the server to a connected client. 54This will be sent on the initial connection and upon each server load. 55================ 56*/ 57void SV_New_f (void) 58{ 59 char *gamedir; 60 int playernum; 61 62 if (host_client->state == cs_spawned) 63 return; 64 65 host_client->state = cs_connected; 66 host_client->connection_started = realtime; 67 68 // send the info about the new client to all connected clients 69// SV_FullClientUpdate (host_client, &sv.reliable_datagram); 70// host_client->sendinfo = true; 71 72 gamedir = Info_ValueForKey (svs.info, "*gamedir"); 73 if (!gamedir[0]) 74 gamedir = "qw"; 75 76//NOTE: This doesn't go through ClientReliableWrite since it's before the user 77//spawns. These functions are written to not overflow 78 if (host_client->num_backbuf) { 79 Con_Printf("WARNING %s: [SV_New] Back buffered (%d0, clearing", host_client->name, host_client->netchan.message.cursize); 80 host_client->num_backbuf = 0; 81 SZ_Clear(&host_client->netchan.message); 82 } 83 84 // send the serverdata 85 MSG_WriteByte (&host_client->netchan.message, svc_serverdata); 86 MSG_WriteLong (&host_client->netchan.message, PROTOCOL_VERSION); 87 MSG_WriteLong (&host_client->netchan.message, svs.spawncount); 88 MSG_WriteString (&host_client->netchan.message, gamedir); 89 90 playernum = NUM_FOR_EDICT(host_client->edict)-1; 91 if (host_client->spectator) 92 playernum |= 128; 93 MSG_WriteByte (&host_client->netchan.message, playernum); 94 95 // send full levelname 96 MSG_WriteString (&host_client->netchan.message, PR_GetString(sv.edicts->v.message)); 97 98 // send the movevars 99 MSG_WriteFloat(&host_client->netchan.message, movevars.gravity); 100 MSG_WriteFloat(&host_client->netchan.message, movevars.stopspeed); 101 MSG_WriteFloat(&host_client->netchan.message, movevars.maxspeed); 102 MSG_WriteFloat(&host_client->netchan.message, movevars.spectatormaxspeed); 103 MSG_WriteFloat(&host_client->netchan.message, movevars.accelerate); 104 MSG_WriteFloat(&host_client->netchan.message, movevars.airaccelerate); 105 MSG_WriteFloat(&host_client->netchan.message, movevars.wateraccelerate); 106 MSG_WriteFloat(&host_client->netchan.message, movevars.friction); 107 MSG_WriteFloat(&host_client->netchan.message, movevars.waterfriction); 108 MSG_WriteFloat(&host_client->netchan.message, movevars.entgravity); 109 110 // send music 111 MSG_WriteByte (&host_client->netchan.message, svc_cdtrack); 112 MSG_WriteByte (&host_client->netchan.message, sv.edicts->v.sounds); 113 114 // send server info string 115 MSG_WriteByte (&host_client->netchan.message, svc_stufftext); 116 MSG_WriteString (&host_client->netchan.message, va("fullserverinfo \"%s\"\n", svs.info) ); 117} 118 119/* 120================== 121SV_Soundlist_f 122================== 123*/ 124void SV_Soundlist_f (void) 125{ 126 char **s; 127 int n; 128 129 if (host_client->state != cs_connected) 130 { 131 Con_Printf ("soundlist not valid -- allready spawned\n"); 132 return; 133 } 134 135 // handle the case of a level changing while a client was connecting 136 if ( atoi(Cmd_Argv(1)) != svs.spawncount ) 137 { 138 Con_Printf ("SV_Soundlist_f from different level\n"); 139 SV_New_f (); 140 return; 141 } 142 143 n = atoi(Cmd_Argv(2)); 144 145//NOTE: This doesn't go through ClientReliableWrite since it's before the user 146//spawns. These functions are written to not overflow 147 if (host_client->num_backbuf) { 148 Con_Printf("WARNING %s: [SV_Soundlist] Back buffered (%d0, clearing", host_client->name, host_client->netchan.message.cursize); 149 host_client->num_backbuf = 0; 150 SZ_Clear(&host_client->netchan.message); 151 } 152 153 MSG_WriteByte (&host_client->netchan.message, svc_soundlist); 154 MSG_WriteByte (&host_client->netchan.message, n); 155 for (s = sv.sound_precache+1 + n ; 156 *s && host_client->netchan.message.cursize < (MAX_MSGLEN/2); 157 s++, n++) 158 MSG_WriteString (&host_client->netchan.message, *s); 159 160 MSG_WriteByte (&host_client->netchan.message, 0); 161 162 // next msg 163 if (*s) 164 MSG_WriteByte (&host_client->netchan.message, n); 165 else 166 MSG_WriteByte (&host_client->netchan.message, 0); 167} 168 169/* 170================== 171SV_Modellist_f 172================== 173*/ 174void SV_Modellist_f (void) 175{ 176 char **s; 177 int n; 178 179 if (host_client->state != cs_connected) 180 { 181 Con_Printf ("modellist not valid -- allready spawned\n"); 182 return; 183 } 184 185 // handle the case of a level changing while a client was connecting 186 if ( atoi(Cmd_Argv(1)) != svs.spawncount ) 187 { 188 Con_Printf ("SV_Modellist_f from different level\n"); 189 SV_New_f (); 190 return; 191 } 192 193 n = atoi(Cmd_Argv(2)); 194 195//NOTE: This doesn't go through ClientReliableWrite since it's before the user 196//spawns. These functions are written to not overflow 197 if (host_client->num_backbuf) { 198 Con_Printf("WARNING %s: [SV_Modellist] Back buffered (%d0, clearing", host_client->name, host_client->netchan.message.cursize); 199 host_client->num_backbuf = 0; 200 SZ_Clear(&host_client->netchan.message); 201 } 202 203 MSG_WriteByte (&host_client->netchan.message, svc_modellist); 204 MSG_WriteByte (&host_client->netchan.message, n); 205 for (s = sv.model_precache+1+n ; 206 *s && host_client->netchan.message.cursize < (MAX_MSGLEN/2); 207 s++, n++) 208 MSG_WriteString (&host_client->netchan.message, *s); 209 MSG_WriteByte (&host_client->netchan.message, 0); 210 211 // next msg 212 if (*s) 213 MSG_WriteByte (&host_client->netchan.message, n); 214 else 215 MSG_WriteByte (&host_client->netchan.message, 0); 216} 217 218/* 219================== 220SV_PreSpawn_f 221================== 222*/ 223void SV_PreSpawn_f (void) 224{ 225 unsigned buf; 226 unsigned check; 227 228 if (host_client->state != cs_connected) 229 { 230 Con_Printf ("prespawn not valid -- allready spawned\n"); 231 return; 232 } 233 234 // handle the case of a level changing while a client was connecting 235 if ( atoi(Cmd_Argv(1)) != svs.spawncount ) 236 { 237 Con_Printf ("SV_PreSpawn_f from different level\n"); 238 SV_New_f (); 239 return; 240 } 241 242 buf = atoi(Cmd_Argv(2)); 243 if (buf >= sv.num_signon_buffers) 244 buf = 0; 245 246 if (!buf) { 247 // should be three numbers following containing checksums 248 check = atoi(Cmd_Argv(3)); 249 250// Con_DPrintf("Client check = %d\n", check); 251 252 if (sv_mapcheck.value && check != sv.worldmodel->checksum && 253 check != sv.worldmodel->checksum2) { 254 SV_ClientPrintf (host_client, PRINT_HIGH, 255 "Map model file does not match (%s), %i != %i/%i.\n" 256 "You may need a new version of the map, or the proper install files.\n", 257 sv.modelname, check, sv.worldmodel->checksum, sv.worldmodel->checksum2); 258 SV_DropClient (host_client); 259 return; 260 } 261 host_client->checksum = check; 262 } 263 264//NOTE: This doesn't go through ClientReliableWrite since it's before the user 265//spawns. These functions are written to not overflow 266 if (host_client->num_backbuf) { 267 Con_Printf("WARNING %s: [SV_PreSpawn] Back buffered (%d0, clearing", host_client->name, host_client->netchan.message.cursize); 268 host_client->num_backbuf = 0; 269 SZ_Clear(&host_client->netchan.message); 270 } 271 272 SZ_Write (&host_client->netchan.message, 273 sv.signon_buffers[buf], 274 sv.signon_buffer_size[buf]); 275 276 buf++; 277 if (buf == sv.num_signon_buffers) 278 { // all done prespawning 279 MSG_WriteByte (&host_client->netchan.message, svc_stufftext); 280 MSG_WriteString (&host_client->netchan.message, va("cmd spawn %i 0\n",svs.spawncount) ); 281 } 282 else 283 { // need to prespawn more 284 MSG_WriteByte (&host_client->netchan.message, svc_stufftext); 285 MSG_WriteString (&host_client->netchan.message, 286 va("cmd prespawn %i %i\n", svs.spawncount, buf) ); 287 } 288} 289 290/* 291================== 292SV_Spawn_f 293================== 294*/ 295void SV_Spawn_f (void) 296{ 297 int i; 298 client_t *client; 299 edict_t *ent; 300 eval_t *val; 301 int n; 302 303 if (host_client->state != cs_connected) 304 { 305 Con_Printf ("Spawn not valid -- allready spawned\n"); 306 return; 307 } 308 309// handle the case of a level changing while a client was connecting 310 if ( atoi(Cmd_Argv(1)) != svs.spawncount ) 311 { 312 Con_Printf ("SV_Spawn_f from different level\n"); 313 SV_New_f (); 314 return; 315 } 316 317 n = atoi(Cmd_Argv(2)); 318 319 // make sure n is valid 320 if ( n < 0 || n > MAX_CLIENTS ) 321 { 322 Con_Printf ("SV_Spawn_f invalid client start\n"); 323 SV_New_f (); 324 return; 325 } 326 327 328 329// send all current names, colors, and frag counts 330 // FIXME: is this a good thing? 331 SZ_Clear (&host_client->netchan.message); 332 333// send current status of all other players 334 335 // normally this could overflow, but no need to check due to backbuf 336 for (i=n, client = svs.clients + n ; i<MAX_CLIENTS ; i++, client++) 337 SV_FullClientUpdateToClient (client, host_client); 338 339// send all current light styles 340 for (i=0 ; i<MAX_LIGHTSTYLES ; i++) 341 { 342 ClientReliableWrite_Begin (host_client, svc_lightstyle, 343 3 + (sv.lightstyles[i] ? strlen(sv.lightstyles[i]) : 1)); 344 ClientReliableWrite_Byte (host_client, (char)i); 345 ClientReliableWrite_String (host_client, sv.lightstyles[i]); 346 } 347 348 // set up the edict 349 ent = host_client->edict; 350 351 memset (&ent->v, 0, progs->entityfields * 4); 352 ent->v.colormap = NUM_FOR_EDICT(ent); 353 ent->v.team = 0; // FIXME 354 ent->v.netname = PR_SetString(host_client->name); 355 356 host_client->entgravity = 1.0; 357 val = GetEdictFieldValue(ent, "gravity"); 358 if (val) 359 val->_float = 1.0; 360 host_client->maxspeed = sv_maxspeed.value; 361 val = GetEdictFieldValue(ent, "maxspeed"); 362 if (val) 363 val->_float = sv_maxspeed.value; 364 365// 366// force stats to be updated 367// 368 memset (host_client->stats, 0, sizeof(host_client->stats)); 369 370 ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); 371 ClientReliableWrite_Byte (host_client, STAT_TOTALSECRETS); 372 ClientReliableWrite_Long (host_client, pr_global_struct->total_secrets); 373 374 ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); 375 ClientReliableWrite_Byte (host_client, STAT_TOTALMONSTERS); 376 ClientReliableWrite_Long (host_client, pr_global_struct->total_monsters); 377 378 ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); 379 ClientReliableWrite_Byte (host_client, STAT_SECRETS); 380 ClientReliableWrite_Long (host_client, pr_global_struct->found_secrets); 381 382 ClientReliableWrite_Begin (host_client, svc_updatestatlong, 6); 383 ClientReliableWrite_Byte (host_client, STAT_MONSTERS); 384 ClientReliableWrite_Long (host_client, pr_global_struct->killed_monsters); 385 386 // get the client to check and download skins 387 // when that is completed, a begin command will be issued 388 ClientReliableWrite_Begin (host_client, svc_stufftext, 8); 389 ClientReliableWrite_String (host_client, "skins\n" ); 390 391} 392 393/* 394================== 395SV_SpawnSpectator 396================== 397*/ 398void SV_SpawnSpectator (void) 399{ 400 int i; 401 edict_t *e; 402 403 VectorCopy (vec3_origin, sv_player->v.origin); 404 VectorCopy (vec3_origin, sv_player->v.view_ofs); 405 sv_player->v.view_ofs[2] = 22; 406 407 // search for an info_playerstart to spawn the spectator at 408 for (i=MAX_CLIENTS-1 ; i<sv.num_edicts ; i++) 409 { 410 e = EDICT_NUM(i); 411 if (!strcmp(PR_GetString(e->v.classname), "info_player_start")) 412 { 413 VectorCopy (e->v.origin, sv_player->v.origin); 414 return; 415 } 416 } 417 418} 419 420/* 421================== 422SV_Begin_f 423================== 424*/ 425void SV_Begin_f (void) 426{ 427 unsigned pmodel = 0, emodel = 0; 428 int i; 429 430 if (host_client->state == cs_spawned) 431 return; // don't begin again 432 433 host_client->state = cs_spawned; 434 435 // handle the case of a level changing while a client was connecting 436 if ( atoi(Cmd_Argv(1)) != svs.spawncount ) 437 { 438 Con_Printf ("SV_Begin_f from different level\n"); 439 SV_New_f (); 440 return; 441 } 442 443 if (host_client->spectator) 444 { 445 SV_SpawnSpectator (); 446 447 if (SpectatorConnect) { 448 // copy spawn parms out of the client_t 449 for (i=0 ; i< NUM_SPAWN_PARMS ; i++) 450 (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; 451 452 // call the spawn function 453 pr_global_struct->time = sv.time; 454 pr_global_struct->self = EDICT_TO_PROG(sv_player); 455 PR_ExecuteProgram (SpectatorConnect); 456 } 457 } 458 else 459 { 460 // copy spawn parms out of the client_t 461 for (i=0 ; i< NUM_SPAWN_PARMS ; i++) 462 (&pr_global_struct->parm1)[i] = host_client->spawn_parms[i]; 463 464 // call the spawn function 465 pr_global_struct->time = sv.time; 466 pr_global_struct->self = EDICT_TO_PROG(sv_player); 467 PR_ExecuteProgram (pr_global_struct->ClientConnect); 468 469 // actually spawn the player 470 pr_global_struct->time = sv.time; 471 pr_global_struct->self = EDICT_TO_PROG(sv_player); 472 PR_ExecuteProgram (pr_global_struct->PutClientInServer); 473 } 474 475 // clear the net statistics, because connecting gives a bogus picture 476 host_client->netchan.frame_latency = 0; 477 host_client->netchan.frame_rate = 0; 478 host_client->netchan.drop_count = 0; 479 host_client->netchan.good_count = 0; 480 481 //check he's not cheating 482 483 pmodel = atoi(Info_ValueForKey (host_client->userinfo, "pmodel")); 484 emodel = atoi(Info_ValueForKey (host_client->userinfo, "emodel")); 485 486 if (pmodel != sv.model_player_checksum || 487 emodel != sv.eyes_player_checksum) 488 SV_BroadcastPrintf (PRINT_HIGH, "%s WARNING: non standard player/eyes model detected\n", host_client->name); 489 490 // if we are paused, tell the client 491 if (sv.paused) { 492 ClientReliableWrite_Begin (host_client, svc_setpause, 2); 493 ClientReliableWrite_Byte (host_client, sv.paused); 494 SV_ClientPrintf(host_client, PRINT_HIGH, "Server is paused.\n"); 495 } 496 497#if 0 498// 499// send a fixangle over the reliable channel to make sure it gets there 500// Never send a roll angle, because savegames can catch the server 501// in a state where it is expecting the client to correct the angle 502// and it won't happen if the game was just loaded, so you wind up 503// with a permanent head tilt 504 ent = EDICT_NUM( 1 + (host_client - svs.clients) ); 505 MSG_WriteByte (&host_client->netchan.message, svc_setangle); 506 for (i=0 ; i < 2 ; i++) 507 MSG_WriteAngle (&host_client->netchan.message, ent->v.angles[i] ); 508 MSG_WriteAngle (&host_client->netchan.message, 0 ); 509#endif 510} 511 512//============================================================================= 513 514/* 515================== 516SV_NextDownload_f 517================== 518*/ 519void SV_NextDownload_f (void) 520{ 521 byte buffer[1024]; 522 int r; 523 int percent; 524 int size; 525 526 if (!host_client->download) 527 return; 528 529 r = host_client->downloadsize - host_client->downloadcount; 530 if (r > 768) 531 r = 768; 532 r = fread (buffer, 1, r, host_client->download); 533 ClientReliableWrite_Begin (host_client, svc_download, 6+r); 534 ClientReliableWrite_Short (host_client, r); 535 536 host_client->downloadcount += r; 537 size = host_client->downloadsize; 538 if (!size) 539 size = 1; 540 percent = host_client->downloadcount*100/size; 541 ClientReliableWrite_Byte (host_client, percent); 542 ClientReliableWrite_SZ (host_client, buffer, r); 543 544 if (host_client->downloadcount != host_client->downloadsize) 545 return; 546 547 fclose (host_client->download); 548 host_client->download = NULL; 549 550} 551 552void OutofBandPrintf(netadr_t where, char *fmt, ...) 553{ 554 va_list argptr; 555 char send[1024]; 556 557 send[0] = 0xff; 558 send[1] = 0xff; 559 send[2] = 0xff; 560 send[3] = 0xff; 561 send[4] = A2C_PRINT; 562 va_start (argptr, fmt); 563 vsprintf (send+5, fmt, argptr); 564 va_end (argptr); 565 566 NET_SendPacket (strlen(send)+1, send, where); 567} 568 569/* 570================== 571SV_NextUpload 572================== 573*/ 574void SV_NextUpload (void) 575{ 576 byte buffer[1024]; 577 int r; 578 int percent; 579 int size; 580 client_t *client; 581 582 if (!*host_client->uploadfn) { 583 SV_ClientPrintf(host_client, PRINT_HIGH, "Upload denied\n"); 584 ClientReliableWrite_Begin (host_client, svc_stufftext, 8); 585 ClientReliableWrite_String (host_client, "stopul"); 586 587 // suck out rest of packet 588 size = MSG_ReadShort (); MSG_ReadByte (); 589 msg_readcount += size; 590 return; 591 } 592 593 size = MSG_ReadShort (); 594 percent = MSG_ReadByte (); 595 596 if (!host_client->upload) 597 { 598 host_client->upload = fopen(host_client->uploadfn, "wb"); 599 if (!host_client->upload) { 600 Sys_Printf("Can't create %s\n", host_client->uploadfn); 601 ClientReliableWrite_Begin (host_client, svc_stufftext, 8); 602 ClientReliableWrite_String (host_client, "stopul"); 603 *host_client->uploadfn = 0; 604 return; 605 } 606 Sys_Printf("Receiving %s from %d...\n", host_client->uploadfn, host_client->userid); 607 if (host_client->remote_snap) 608 OutofBandPrintf(host_client->snap_from, "Server receiving %s from %d...\n", host_client->uploadfn, host_client->userid); 609 } 610 611 fwrite (net_message.data + msg_readcount, 1, size, host_client->upload); 612 msg_readcount += size; 613 614Con_DPrintf ("UPLOAD: %d received\n", size); 615 616 if (percent != 100) { 617 ClientReliableWrite_Begin (host_client, svc_stufftext, 8); 618 ClientReliableWrite_String (host_client, "nextul\n"); 619 } else { 620 fclose (host_client->upload); 621 host_client->upload = NULL; 622 623 Sys_Printf("%s upload completed.\n", host_client->uploadfn); 624 625 if (host_client->remote_snap) { 626 char *p; 627 628 if ((p = strchr(host_client->uploadfn, '/')) != NULL) 629 p++; 630 else 631 p = host_client->uploadfn; 632 OutofBandPrintf(host_client->snap_from, "%s upload completed.\nTo download, enter:\ndownload %s\n", 633 host_client->uploadfn, p); 634 } 635 } 636 637} 638 639/* 640================== 641SV_BeginDownload_f 642================== 643*/ 644void SV_BeginDownload_f(void) 645{ 646 char *name; 647 extern cvar_t allow_download; 648 extern cvar_t allow_download_skins; 649 extern cvar_t allow_download_models; 650 extern cvar_t allow_download_sounds; 651 extern cvar_t allow_download_maps; 652 extern int file_from_pak; // ZOID did file come from pak? 653 654 name = Cmd_Argv(1); 655// hacked by zoid to allow more conrol over download 656 // first off, no .. or global allow check 657 if (strstr (name, "..") || !allow_download.value 658 // leading dot is no good 659 || *name == '.' 660 // leading slash bad as well, must be in subdir 661 || *name == '/' 662 // next up, skin check 663 || (strncmp(name, "skins/", 6) == 0 && !allow_download_skins.value) 664 // now models 665 || (strncmp(name, "progs/", 6) == 0 && !allow_download_models.value) 666 // now sounds 667 || (strncmp(name, "sound/", 6) == 0 && !allow_download_sounds.value) 668 // now maps (note special case for maps, must not be in pak) 669 || (strncmp(name, "maps/", 6) == 0 && !allow_download_maps.value) 670 // MUST be in a subdirectory 671 || !strstr (name, "/") ) 672 { // don't allow anything with .. path 673 ClientReliableWrite_Begin (host_client, svc_download, 4); 674 ClientReliableWrite_Short (host_client, -1); 675 ClientReliableWrite_Byte (host_client, 0); 676 return; 677 } 678 679 if (host_client->download) { 680 fclose (host_client->download); 681 host_client->download = NULL; 682 } 683 684 // lowercase name (needed for casesen file systems) 685 { 686 char *p; 687 688 for (p = name; *p; p++) 689 *p = (char)tolower(*p); 690 } 691 692 693 host_client->downloadsize = COM_FOpenFile (name, &host_client->download); 694 host_client->downloadcount = 0; 695 696 if (!host_client->download 697 // special check for maps, if it came from a pak file, don't allow 698 // download ZOID 699 || (strncmp(name, "maps/", 5) == 0 && file_from_pak)) 700 { 701 if (host_client->download) { 702 fclose(host_client->download); 703 host_client->download = NULL; 704 } 705 706 Sys_Printf ("Couldn't download %s to %s\n", name, host_client->name); 707 ClientReliableWrite_Begin (host_client, svc_download, 4); 708 ClientReliableWrite_Short (host_client, -1); 709 ClientReliableWrite_Byte (host_client, 0); 710 return; 711 } 712 713 SV_NextDownload_f (); 714 Sys_Printf ("Downloading %s to %s\n", name, host_client->name); 715} 716 717//============================================================================= 718 719/* 720================== 721SV_Say 722================== 723*/ 724void SV_Say (qboolean team) 725{ 726 client_t *client; 727 int j, tmp; 728 char *p; 729 char text[2048]; 730 char t1[32], *t2; 731 732 if (Cmd_Argc () < 2) 733 return; 734 735 if (team) 736 { 737 strncpy (t1, Info_ValueForKey (host_client->userinfo, "team"), 31); 738 t1[31] = 0; 739 } 740 741 if (host_client->spectator && (!sv_spectalk.value || team)) 742 sprintf (text, "[SPEC] %s: ", host_client->name); 743 else if (team) 744 sprintf (text, "(%s): ", host_client->name); 745 else { 746 sprintf (text, "%s: ", host_client->name); 747 } 748 749 if (fp_messages) { 750 if (!sv.paused && realtime<host_client->lockedtill) { 751 SV_ClientPrintf(host_client, PRINT_CHAT, 752 "You can't talk for %d more seconds\n", 753 (int) (host_client->lockedtill - realtime)); 754 return; 755 } 756 tmp = host_client->whensaidhead - fp_messages + 1; 757 if (tmp < 0) 758 tmp = 10+tmp; 759 if (!sv.paused && 760 host_client->whensaid[tmp] && (realtime-host_client->whensaid[tmp] < fp_persecond)) { 761 host_client->lockedtill = realtime + fp_secondsdead; 762 if (fp_msg[0]) 763 SV_ClientPrintf(host_client, PRINT_CHAT, 764 "FloodProt: %s\n", fp_msg); 765 else 766 SV_ClientPrintf(host_client, PRINT_CHAT, 767 "FloodProt: You can't talk for %d seconds.\n", fp_secondsdead); 768 return; 769 } 770 host_client->whensaidhead++; 771 if (host_client->whensaidhead > 9) 772 host_client->whensaidhead = 0; 773 host_client->whensaid[host_client->whensaidhead] = realtime; 774 } 775 776 p = Cmd_Args(); 777 778 if (*p == '"') 779 { 780 p++; 781 p[Q_strlen(p)-1] = 0; 782 } 783 784 Q_strcat(text, p); 785 Q_strcat(text, "\n"); 786 787 Sys_Printf ("%s", text); 788 789 for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) 790 { 791 if (client->state != cs_spawned) 792 continue; 793 if (host_client->spectator && !sv_spectalk.value) 794 if (!client->spectator) 795 continue; 796 797 if (team) 798 { 799 // the spectator team 800 if (host_client->spectator) { 801 if (!client->spectator) 802 continue; 803 } else { 804 t2 = Info_ValueForKey (client->userinfo, "team"); 805 if (strcmp(t1, t2) || client->spectator) 806 continue; // on different teams 807 } 808 } 809 SV_ClientPrintf(client, PRINT_CHAT, "%s", text); 810 } 811} 812 813 814/* 815================== 816SV_Say_f 817================== 818*/ 819void SV_Say_f(void) 820{ 821 SV_Say (false); 822} 823/* 824================== 825SV_Say_Team_f 826================== 827*/ 828void SV_Say_Team_f(void) 829{ 830 SV_Say (true); 831} 832 833 834 835//============================================================================ 836 837/* 838================= 839SV_Pings_f 840 841The client is showing the scoreboard, so send new ping times for all 842clients 843================= 844*/ 845void SV_Pings_f (void) 846{ 847 client_t *client; 848 int j; 849 850 for (j = 0, client = svs.clients; j < MAX_CLIENTS; j++, client++) 851 { 852 if (client->state != cs_spawned) 853 continue; 854 855 ClientReliableWrite_Begin (host_client, svc_updateping, 4); 856 ClientReliableWrite_Byte (host_client, j); 857 ClientReliableWrite_Short (host_client, SV_CalcPing(client)); 858 ClientReliableWrite_Begin (host_client, svc_updatepl, 4); 859 ClientReliableWrite_Byte (host_client, j); 860 ClientReliableWrite_Byte (host_client, client->lossage); 861 } 862} 863 864 865 866/* 867================== 868SV_Kill_f 869================== 870*/ 871void SV_Kill_f (void) 872{ 873 if (sv_player->v.health <= 0) 874 { 875 SV_ClientPrintf (host_client, PRINT_HIGH, "Can't suicide -- allready dead!\n"); 876 return; 877 } 878 879 pr_global_struct->time = sv.time; 880 pr_global_struct->self = EDICT_TO_PROG(sv_player); 881 PR_ExecuteProgram (pr_global_struct->ClientKill); 882} 883 884/* 885================== 886SV_TogglePause 887================== 888*/ 889void SV_TogglePause (const char *msg) 890{ 891 int i; 892 client_t *cl; 893 894 sv.paused ^= 1; 895 896 if (msg) 897 SV_BroadcastPrintf (PRINT_HIGH, "%s", msg); 898 899 // send notification to all clients 900 for (i=0, cl = svs.clients ; i<MAX_CLIENTS ; i++, cl++) 901 { 902 if (!cl->state) 903 continue; 904 ClientReliableWrite_Begin (cl, svc_setpause, 2); 905 ClientReliableWrite_Byte (cl, sv.paused); 906 } 907} 908 909 910/* 911================== 912SV_Pause_f 913================== 914*/ 915void SV_Pause_f (void) 916{ 917 int i; 918 client_t *cl; 919 char st[sizeof(host_client->name) + 32]; 920 921 if (!pausable.value) { 922 SV_ClientPrintf (host_client, PRINT_HIGH, "Pause not allowed.\n"); 923 return; 924 } 925 926 if (host_client->spectator) { 927 SV_ClientPrintf (host_client, PRINT_HIGH, "Spectators can not pause.\n"); 928 return; 929 } 930 931 if (sv.paused) 932 sprintf (st, "%s paused the game\n", host_client->name); 933 else 934 sprintf (st, "%s unpaused the game\n", host_client->name); 935 936 SV_TogglePause(st); 937} 938 939 940/* 941================= 942SV_Drop_f 943 944The client is going to disconnect, so remove the connection immediately 945================= 946*/ 947void SV_Drop_f (void) 948{ 949 SV_EndRedirect (); 950 if (!host_client->spectator) 951 SV_BroadcastPrintf (PRINT_HIGH, "%s dropped\n", host_client->name); 952 SV_DropClient (host_client); 953} 954 955/* 956================= 957SV_PTrack_f 958 959Change the bandwidth estimate for a client 960================= 961*/ 962void SV_PTrack_f (void) 963{ 964 int i; 965 edict_t *ent, *tent; 966 967 if (!host_client->spectator) 968 return; 969 970 if (Cmd_Argc() != 2) 971 { 972 // turn off tracking 973 host_client->spec_track = 0; 974 ent = EDICT_NUM(host_client - svs.clients + 1); 975 tent = EDICT_NUM(0); 976 ent->v.goalentity = EDICT_TO_PROG(tent); 977 return; 978 } 979 980 i = atoi(Cmd_Argv(1)); 981 if (i < 0 || i >= MAX_CLIENTS || svs.clients[i].state != cs_spawned || 982 svs.clients[i].spectator) { 983 SV_ClientPrintf (host_client, PRINT_HIGH, "Invalid client to track\n"); 984 host_client->spec_track = 0; 985 ent = EDICT_NUM(host_client - svs.clients + 1); 986 tent = EDICT_NUM(0); 987 ent->v.goalentity = EDICT_TO_PROG(tent); 988 return; 989 } 990 host_client->spec_track = i + 1; // now tracking 991 992 ent = EDICT_NUM(host_client - svs.clients + 1); 993 tent = EDICT_NUM(i + 1); 994 ent->v.goalentity = EDICT_TO_PROG(tent); 995} 996 997 998/* 999================= 1000SV_Rate_f 1001 1002Change the bandwidth estimate for a client 1003================= 1004*/ 1005void SV_Rate_f (void) 1006{ 1007 int rate; 1008 1009 if (Cmd_Argc() != 2) 1010 { 1011 SV_ClientPrintf (host_client, PRINT_HIGH, "Current rate is %i\n", 1012 (int)(1.0/host_client->netchan.rate + 0.5)); 1013 return; 1014 } 1015 1016 rate = atoi(Cmd_Argv(1)); 1017 if (rate < 500) 1018 rate = 500; 1019 if (rate > 10000) 1020 rate = 10000; 1021 1022 SV_ClientPrintf (host_client, PRINT_HIGH, "Net rate set to %i\n", rate); 1023 host_client->netchan.rate = 1.0/rate; 1024} 1025 1026 1027/* 1028================= 1029SV_Msg_f 1030 1031Change the message level for a client 1032================= 1033*/ 1034void SV_Msg_f (void) 1035{ 1036 if (Cmd_Argc() != 2) 1037 { 1038 SV_ClientPrintf (host_client, PRINT_HIGH, "Current msg level is %i\n", 1039 host_client->messagelevel); 1040 return; 1041 } 1042 1043 host_client->messagelevel = atoi(Cmd_Argv(1)); 1044 1045 SV_ClientPrintf (host_client, PRINT_HIGH, "Msg level set to %i\n", host_client->messagelevel); 1046} 1047 1048/* 1049================== 1050SV_SetInfo_f 1051 1052Allow clients to change userinfo 1053================== 1054*/ 1055void SV_SetInfo_f (void) 1056{ 1057 int i; 1058 char oldval[MAX_INFO_STRING]; 1059 1060 1061 if (Cmd_Argc() == 1) 1062 { 1063 Con_Printf ("User info settings:\n"); 1064 Info_Print (host_client->userinfo); 1065 return; 1066 } 1067 1068 if (Cmd_Argc() != 3) 1069 { 1070 Con_Printf ("usage: setinfo [ <key> <value> ]\n"); 1071 return; 1072 } 1073 1074 if (Cmd_Argv(1)[0] == '*') 1075 return; // don't set priveledged values 1076 1077 strcpy(oldval, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); 1078 1079 Info_SetValueForKey (host_client->userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING); 1080// name is extracted below in ExtractFromUserInfo 1081// strncpy (host_client->name, Info_ValueForKey (host_client->userinfo, "name") 1082// , sizeof(host_client->name)-1); 1083// SV_FullClientUpdate (host_client, &sv.reliable_datagram); 1084// host_client->sendinfo = true; 1085 1086 if (!strcmp(Info_ValueForKey(host_client->userinfo, Cmd_Argv(1)), oldval)) 1087 return; // key hasn't changed 1088 1089 // process any changed values 1090 SV_ExtractFromUserinfo (host_client); 1091 1092 i = host_client - svs.clients; 1093 MSG_WriteByte (&sv.reliable_datagram, svc_setinfo); 1094 MSG_WriteByte (&sv.reliable_datagram, i); 1095 MSG_WriteString (&sv.reliable_datagram, Cmd_Argv(1)); 1096 MSG_WriteString (&sv.reliable_datagram, Info_ValueForKey(host_client->userinfo, Cmd_Argv(1))); 1097} 1098 1099/* 1100================== 1101SV_ShowServerinfo_f 1102 1103Dumps the serverinfo info string 1104================== 1105*/ 1106void SV_ShowServerinfo_f (void) 1107{ 1108 Info_Print (svs.info); 1109} 1110 1111void SV_NoSnap_f(void) 1112{ 1113 if (*host_client->uploadfn) { 1114 *host_client->uploadfn = 0; 1115 SV_BroadcastPrintf (PRINT_HIGH, "%s refused remote screenshot\n", host_client->name); 1116 } 1117} 1118 1119typedef struct 1120{ 1121 char *name; 1122 void (*func) (void); 1123} ucmd_t; 1124 1125ucmd_t ucmds[] = 1126{ 1127 {"new", SV_New_f}, 1128 {"modellist", SV_Modellist_f}, 1129 {"soundlist", SV_Soundlist_f}, 1130 {"prespawn", SV_PreSpawn_f}, 1131 {"spawn", SV_Spawn_f}, 1132 {"begin", SV_Begin_f}, 1133 1134 {"drop", SV_Drop_f}, 1135 {"pings", SV_Pings_f}, 1136 1137// issued by hand at client consoles 1138 {"rate", SV_Rate_f}, 1139 {"kill", SV_Kill_f}, 1140 {"pause", SV_Pause_f}, 1141 {"msg", SV_Msg_f}, 1142 1143 {"say", SV_Say_f}, 1144 {"say_team", SV_Say_Team_f}, 1145 1146 {"setinfo", SV_SetInfo_f}, 1147 1148 {"serverinfo", SV_ShowServerinfo_f}, 1149 1150 {"download", SV_BeginDownload_f}, 1151 {"nextdl", SV_NextDownload_f}, 1152 1153 {"ptrack", SV_PTrack_f}, //ZOID - used with autocam 1154 1155 {"snap", SV_NoSnap_f}, 1156 1157 {NULL, NULL} 1158}; 1159 1160/* 1161================== 1162SV_ExecuteUserCommand 1163================== 1164*/ 1165void SV_ExecuteUserCommand (char *s) 1166{ 1167 ucmd_t *u; 1168 1169 Cmd_TokenizeString (s); 1170 sv_player = host_client->edict; 1171 1172 SV_BeginRedirect (RD_CLIENT); 1173 1174 for (u=ucmds ; u->name ; u++) 1175 if (!strcmp (Cmd_Argv(0), u->name) ) 1176 { 1177 u->func (); 1178 break; 1179 } 1180 1181 if (!u->name) 1182 Con_Printf ("Bad user command: %s\n", Cmd_Argv(0)); 1183 1184 SV_EndRedirect (); 1185} 1186 1187/* 1188=========================================================================== 1189 1190USER CMD EXECUTION 1191 1192=========================================================================== 1193*/ 1194 1195/* 1196=============== 1197V_CalcRoll 1198 1199Used by view and sv_user 1200=============== 1201*/ 1202float V_CalcRoll (vec3_t angles, vec3_t velocity) 1203{ 1204 vec3_t forward, right, up; 1205 float sign; 1206 float side; 1207 float value; 1208 1209 AngleVectors (angles, forward, right, up); 1210 side = DotProduct (velocity, right); 1211 sign = side < 0 ? -1 : 1; 1212 side = fabs(side); 1213 1214 value = cl_rollangle.value; 1215 1216 if (side < cl_rollspeed.value) 1217 side = side * value / cl_rollspeed.value; 1218 else 1219 side = value; 1220 1221 return side*sign; 1222 1223} 1224 1225 1226 1227 1228//============================================================================ 1229 1230vec3_t pmove_mins, pmove_maxs; 1231 1232/* 1233==================== 1234AddLinksToPmove 1235 1236==================== 1237*/ 1238void AddLinksToPmove ( areanode_t *node ) 1239{ 1240 link_t *l, *next; 1241 edict_t *check; 1242 int pl; 1243 int i; 1244 physent_t *pe; 1245 1246 pl = EDICT_TO_PROG(sv_player); 1247 1248 // touch linked edicts 1249 for (l = node->solid_edicts.next ; l != &node->solid_edicts ; l = next) 1250 { 1251 next = l->next; 1252 check = EDICT_FROM_AREA(l); 1253 1254 if (check->v.owner == pl) 1255 continue; // player's own missile 1256 if (check->v.solid == SOLID_BSP 1257 || check->v.solid == SOLID_BBOX 1258 || check->v.solid == SOLID_SLIDEBOX) 1259 { 1260 if (check == sv_player) 1261 continue; 1262 1263 for (i=0 ; i<3 ; i++) 1264 if (check->v.absmin[i] > pmove_maxs[i] 1265 || check->v.absmax[i] < pmove_mins[i]) 1266 break; 1267 if (i != 3) 1268 continue; 1269 if (pmove.numphysent == MAX_PHYSENTS) 1270 return; 1271 pe = &pmove.physents[pmove.numphysent]; 1272 pmove.numphysent++; 1273 1274 VectorCopy (check->v.origin, pe->origin); 1275 pe->info = NUM_FOR_EDICT(check); 1276 if (check->v.solid == SOLID_BSP) 1277 pe->model = sv.models[(int)(check->v.modelindex)]; 1278 else 1279 { 1280 pe->model = NULL; 1281 VectorCopy (check->v.mins, pe->mins); 1282 VectorCopy (check->v.maxs, pe->maxs); 1283 } 1284 } 1285 } 1286 1287// recurse down both sides 1288 if (node->axis == -1) 1289 return; 1290 1291 if ( pmove_maxs[node->axis] > node->dist ) 1292 AddLinksToPmove ( node->children[0] ); 1293 if ( pmove_mins[node->axis] < node->dist ) 1294 AddLinksToPmove ( node->children[1] ); 1295} 1296 1297 1298/* 1299================ 1300AddAllEntsToPmove 1301 1302For debugging 1303================ 1304*/ 1305void AddAllEntsToPmove (void) 1306{ 1307 int e; 1308 edict_t *check; 1309 int i; 1310 physent_t *pe; 1311 int pl; 1312 1313 pl = EDICT_TO_PROG(sv_player); 1314 check = NEXT_EDICT(sv.edicts); 1315 for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check)) 1316 { 1317 if (check->free) 1318 continue; 1319 if (check->v.owner == pl) 1320 continue; 1321 if (check->v.solid == SOLID_BSP 1322 || check->v.solid == SOLID_BBOX 1323 || check->v.solid == SOLID_SLIDEBOX) 1324 { 1325 if (check == sv_player) 1326 continue; 1327 1328 for (i=0 ; i<3 ; i++) 1329 if (check->v.absmin[i] > pmove_maxs[i] 1330 || check->v.absmax[i] < pmove_mins[i]) 1331 break; 1332 if (i != 3) 1333 continue; 1334 pe = &pmove.physents[pmove.numphysent]; 1335 1336 VectorCopy (check->v.origin, pe->origin); 1337 pmove.physents[pmove.numphysent].info = e; 1338 if (check->v.solid == SOLID_BSP) 1339 pe->model = sv.models[(int)(check->v.modelindex)]; 1340 else 1341 { 1342 pe->model = NULL; 1343 VectorCopy (check->v.mins, pe->mins); 1344 VectorCopy (check->v.maxs, pe->maxs); 1345 } 1346 1347 if (++pmove.numphysent == MAX_PHYSENTS) 1348 break; 1349 } 1350 } 1351} 1352 1353/* 1354=========== 1355SV_PreRunCmd 1356=========== 1357Done before running a player command. Clears the touch array 1358*/ 1359byte playertouch[(MAX_EDICTS+7)/8]; 1360 1361void SV_PreRunCmd(void) 1362{ 1363 memset(playertouch, 0, sizeof(playertouch)); 1364} 1365 1366/* 1367=========== 1368SV_RunCmd 1369=========== 1370*/ 1371void SV_RunCmd (usercmd_t *ucmd) 1372{ 1373 edict_t *ent; 1374 int i, n; 1375 int oldmsec; 1376 1377 cmd = *ucmd; 1378 1379 // chop up very long commands 1380 if (cmd.msec > 50) 1381 { 1382 oldmsec = ucmd->msec; 1383 cmd.msec = oldmsec/2; 1384 SV_RunCmd (&cmd); 1385 cmd.msec = oldmsec/2; 1386 cmd.impulse = 0; 1387 SV_RunCmd (&cmd); 1388 return; 1389 } 1390 1391 if (!sv_player->v.fixangle) 1392 VectorCopy (ucmd->angles, sv_player->v.v_angle); 1393 1394 sv_player->v.button0 = ucmd->buttons & 1; 1395 sv_player->v.button2 = (ucmd->buttons & 2)>>1; 1396 if (ucmd->impulse) 1397 sv_player->v.impulse = ucmd->impulse; 1398 1399// 1400// angles 1401// show 1/3 the pitch angle and all the roll angle 1402 if (sv_player->v.health > 0) 1403 { 1404 if (!sv_player->v.fixangle) 1405 { 1406 sv_player->v.angles[PITCH] = -sv_player->v.v_angle[PITCH]/3; 1407 sv_player->v.angles[YAW] = sv_player->v.v_angle[YAW]; 1408 } 1409 sv_player->v.angles[ROLL] = 1410 V_CalcRoll (sv_player->v.angles, sv_player->v.velocity)*4; 1411 } 1412 1413 host_frametime = ucmd->msec * 0.001; 1414 if (host_frametime > 0.1) 1415 host_frametime = 0.1; 1416 1417 if (!host_client->spectator) 1418 { 1419 pr_global_struct->frametime = host_frametime; 1420 1421 pr_global_struct->time = sv.time; 1422 pr_global_struct->self = EDICT_TO_PROG(sv_player); 1423 PR_ExecuteProgram (pr_global_struct->PlayerPreThink); 1424 1425 SV_RunThink (sv_player); 1426 } 1427 1428 for (i=0 ; i<3 ; i++) 1429 pmove.origin[i] = sv_player->v.origin[i] + (sv_player->v.mins[i] - player_mins[i]); 1430 VectorCopy (sv_player->v.velocity, pmove.velocity); 1431 VectorCopy (sv_player->v.v_angle, pmove.angles); 1432 1433 pmove.spectator = host_client->spectator; 1434 pmove.waterjumptime = sv_player->v.teleport_time; 1435 pmove.numphysent = 1; 1436 pmove.physents[0].model = sv.worldmodel; 1437 pmove.cmd = *ucmd; 1438 pmove.dead = sv_player->v.health <= 0; 1439 pmove.oldbuttons = host_client->oldbuttons; 1440 1441 movevars.entgravity = host_client->entgravity; 1442 movevars.maxspeed = host_client->maxspeed; 1443 1444 for (i=0 ; i<3 ; i++) 1445 { 1446 pmove_mins[i] = pmove.origin[i] - 256; 1447 pmove_maxs[i] = pmove.origin[i] + 256; 1448 } 1449#if 1 1450 AddLinksToPmove ( sv_areanodes ); 1451#else 1452 AddAllEntsToPmove (); 1453#endif 1454 1455#if 0 1456{ 1457 int before, after; 1458 1459before = PM_TestPlayerPosition (pmove.origin); 1460 PlayerMove (); 1461after = PM_TestPlayerPosition (pmove.origin); 1462 1463if (sv_player->v.health > 0 && before && !after ) 1464 Con_Printf ("player %s got stuck in playermove!!!!\n", host_client->name); 1465} 1466#else 1467 PlayerMove (); 1468#endif 1469 1470 host_client->oldbuttons = pmove.oldbuttons; 1471 sv_player->v.teleport_time = pmove.waterjumptime; 1472 sv_player->v.waterlevel = waterlevel; 1473 sv_player->v.watertype = watertype; 1474 if (onground != -1) 1475 { 1476 sv_player->v.flags = (int)sv_player->v.flags | FL_ONGROUND; 1477 sv_player->v.groundentity = EDICT_TO_PROG(EDICT_NUM(pmove.physents[onground].info)); 1478 } 1479 else 1480 sv_player->v.flags = (int)sv_player->v.flags & ~FL_ONGROUND; 1481 for (i=0 ; i<3 ; i++) 1482 sv_player->v.origin[i] = pmove.origin[i] - (sv_player->v.mins[i] - player_mins[i]); 1483 1484#if 0 1485 // truncate velocity the same way the net protocol will 1486 for (i=0 ; i<3 ; i++) 1487 sv_player->v.velocity[i] = (int)pmove.velocity[i]; 1488#else 1489 VectorCopy (pmove.velocity, sv_player->v.velocity); 1490#endif 1491 1492 VectorCopy (pmove.angles, sv_player->v.v_angle); 1493 1494 if (!host_client->spectator) 1495 { 1496 // link into place and touch triggers 1497 SV_LinkEdict (sv_player, true); 1498 1499 // touch other objects 1500 for (i=0 ; i<pmove.numtouch ; i++) 1501 { 1502 n = pmove.physents[pmove.touchindex[i]].info; 1503 ent = EDICT_NUM(n); 1504 if (!ent->v.touch || (playertouch[n/8]&(1<<(n%8)))) 1505 continue; 1506 pr_global_struct->self = EDICT_TO_PROG(ent); 1507 pr_global_struct->other = EDICT_TO_PROG(sv_player); 1508 PR_ExecuteProgram (ent->v.touch); 1509 playertouch[n/8] |= 1 << (n%8); 1510 } 1511 } 1512} 1513 1514/* 1515=========== 1516SV_PostRunCmd 1517=========== 1518Done after running a player command. 1519*/ 1520void SV_PostRunCmd(void) 1521{ 1522 // run post-think 1523 1524 if (!host_client->spectator) { 1525 pr_global_struct->time = sv.time; 1526 pr_global_struct->self = EDICT_TO_PROG(sv_player); 1527 PR_ExecuteProgram (pr_global_struct->PlayerPostThink); 1528 SV_RunNewmis (); 1529 } else if (SpectatorThink) { 1530 pr_global_struct->time = sv.time; 1531 pr_global_struct->self = EDICT_TO_PROG(sv_player); 1532 PR_ExecuteProgram (SpectatorThink); 1533 } 1534} 1535 1536 1537/* 1538=================== 1539SV_ExecuteClientMessage 1540 1541The current net_message is parsed for the given client 1542=================== 1543*/ 1544void SV_ExecuteClientMessage (client_t *cl) 1545{ 1546 int c; 1547 char *s; 1548 usercmd_t oldest, oldcmd, newcmd; 1549 client_frame_t *frame; 1550 vec3_t o; 1551 qboolean move_issued = false; //only allow one move command 1552 int checksumIndex; 1553 byte checksum, calculatedChecksum; 1554 int seq_hash; 1555 1556 // calc ping time 1557 frame = &cl->frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; 1558 frame->ping_time = realtime - frame->senttime; 1559 1560 // make sure the reply sequence number matches the incoming 1561 // sequence number 1562 if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence) 1563 cl->netchan.outgoing_sequence = cl->netchan.incoming_sequence; 1564 else 1565 cl->send_message = false; // don't reply, sequences have slipped 1566 1567 // save time for ping calculations 1568 cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].senttime = realtime; 1569 cl->frames[cl->netchan.outgoing_sequence & UPDATE_MASK].ping_time = -1; 1570 1571 host_client = cl; 1572 sv_player = host_client->edict; 1573 1574// seq_hash = (cl->netchan.incoming_sequence & 0xffff) ; // ^ QW_CHECK_HASH; 1575 seq_hash = cl->netchan.incoming_sequence; 1576 1577 // mark time so clients will know how much to predict 1578 // other players 1579 cl->localtime = sv.time; 1580 cl->delta_sequence = -1; // no delta unless requested 1581 while (1) 1582 { 1583 if (msg_badread) 1584 { 1585 Con_Printf ("SV_ReadClientMessage: badread\n"); 1586 SV_DropClient (cl); 1587 return; 1588 } 1589 1590 c = MSG_ReadByte (); 1591 if (c == -1) 1592 break; 1593 1594 switch (c) 1595 { 1596 default: 1597 Con_Printf ("SV_ReadClientMessage: unknown command char\n"); 1598 SV_DropClient (cl); 1599 return; 1600 1601 case clc_nop: 1602 break; 1603 1604 case clc_delta: 1605 cl->delta_sequence = MSG_ReadByte (); 1606 break; 1607 1608 case clc_move: 1609 if (move_issued) 1610 return; // someone is trying to cheat... 1611 1612 move_issued = true; 1613 1614 checksumIndex = MSG_GetReadCount(); 1615 checksum = (byte)MSG_ReadByte (); 1616 1617 // read loss percentage 1618 cl->lossage = MSG_ReadByte(); 1619 1620 MSG_ReadDeltaUsercmd (&nullcmd, &oldest); 1621 MSG_ReadDeltaUsercmd (&oldest, &oldcmd); 1622 MSG_ReadDeltaUsercmd (&oldcmd, &newcmd); 1623 1624 if ( cl->state != cs_spawned ) 1625 break; 1626 1627 // if the checksum fails, ignore the rest of the packet 1628 calculatedChecksum = COM_BlockSequenceCRCByte( 1629 net_message.data + checksumIndex + 1, 1630 MSG_GetReadCount() - checksumIndex - 1, 1631 seq_hash); 1632 1633 if (calculatedChecksum != checksum) 1634 { 1635 Con_DPrintf ("Failed command checksum for %s(%d) (%d != %d)\n", 1636 cl->name, cl->netchan.incoming_sequence, checksum, calculatedChecksum); 1637 return; 1638 } 1639 1640 if (!sv.paused) { 1641 SV_PreRunCmd(); 1642 1643 if (net_drop < 20) 1644 { 1645 while (net_drop > 2) 1646 { 1647 SV_RunCmd (&cl->lastcmd); 1648 net_drop--; 1649 } 1650 if (net_drop > 1) 1651 SV_RunCmd (&oldest); 1652 if (net_drop > 0) 1653 SV_RunCmd (&oldcmd); 1654 } 1655 SV_RunCmd (&newcmd); 1656 1657 SV_PostRunCmd(); 1658 } 1659 1660 cl->lastcmd = newcmd; 1661 cl->lastcmd.buttons = 0; // avoid multiple fires on lag 1662 break; 1663 1664 1665 case clc_stringcmd: 1666 s = MSG_ReadString (); 1667 SV_ExecuteUserCommand (s); 1668 break; 1669 1670 case clc_tmove: 1671 o[0] = MSG_ReadCoord(); 1672 o[1] = MSG_ReadCoord(); 1673 o[2] = MSG_ReadCoord(); 1674 // only allowed by spectators 1675 if (host_client->spectator) { 1676 VectorCopy(o, sv_player->v.origin); 1677 SV_LinkEdict(sv_player, false); 1678 } 1679 break; 1680 1681 case clc_upload: 1682 SV_NextUpload(); 1683 break; 1684 1685 } 1686 } 1687} 1688 1689/* 1690============== 1691SV_UserInit 1692============== 1693*/ 1694void SV_UserInit (void) 1695{ 1696 Cvar_RegisterVariable (&cl_rollspeed); 1697 Cvar_RegisterVariable (&cl_rollangle); 1698 Cvar_RegisterVariable (&sv_spectalk); 1699 Cvar_RegisterVariable (&sv_mapcheck); 1700} 1701 1702 1703