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 "quakedef.h" 23 24edict_t *sv_player; 25 26extern cvar_t sv_friction; 27cvar_t sv_edgefriction = CVAR2("edgefriction", "2"); 28extern cvar_t sv_stopspeed; 29 30static vec3_t forward, right, up; 31 32vec3_t wishdir; 33float wishspeed; 34 35// world 36float *angles; 37float *origin; 38float *velocity; 39 40qboolean onground; 41 42usercmd_t cmd; 43 44cvar_t sv_idealpitchscale = CVAR2("sv_idealpitchscale","0.8"); 45 46 47/* 48=============== 49SV_SetIdealPitch 50=============== 51*/ 52#define MAX_FORWARD 6 53void SV_SetIdealPitch (void) 54{ 55 float angleval, sinval, cosval; 56 trace_t tr; 57 vec3_t top, bottom; 58 float z[MAX_FORWARD]; 59 int i, j; 60 int step, dir, steps; 61 62 if (!((int)sv_player->u.v.flags & FL_ONGROUND)) 63 return; 64 65 angleval = sv_player->u.v.angles[YAW] * M_PI*2 / 360; 66 sinval = sin(angleval); 67 cosval = cos(angleval); 68 69 for (i=0 ; i<MAX_FORWARD ; i++) 70 { 71 top[0] = sv_player->u.v.origin[0] + cosval*(i+3)*12; 72 top[1] = sv_player->u.v.origin[1] + sinval*(i+3)*12; 73 top[2] = sv_player->u.v.origin[2] + sv_player->u.v.view_ofs[2]; 74 75 bottom[0] = top[0]; 76 bottom[1] = top[1]; 77 bottom[2] = top[2] - 160; 78 79 tr = SV_Move (top, vec3_origin, vec3_origin, bottom, 1, sv_player); 80 if (tr.allsolid) 81 return; // looking at a wall, leave ideal the way is was 82 83 if (tr.fraction == 1) 84 return; // near a dropoff 85 86 z[i] = top[2] + tr.fraction*(bottom[2]-top[2]); 87 } 88 89 dir = 0; 90 steps = 0; 91 for (j=1 ; j<i ; j++) 92 { 93 step = (int) (z[j] - z[j-1]); 94 if (step > -ON_EPSILON && step < ON_EPSILON) 95 continue; 96 97 if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) ) 98 return; // mixed changes 99 100 steps++; 101 dir = step; 102 } 103 104 if (!dir) 105 { 106 sv_player->u.v.idealpitch = 0; 107 return; 108 } 109 110 if (steps < 2) 111 return; 112 sv_player->u.v.idealpitch = -dir * sv_idealpitchscale.value; 113} 114 115 116/* 117================== 118SV_UserFriction 119 120================== 121*/ 122void SV_UserFriction (void) 123{ 124 float *vel; 125 float speed, newspeed, control; 126 vec3_t start, stop; 127 float friction; 128 trace_t trace; 129 130 vel = velocity; 131 132 speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]); 133 if (!speed) 134 return; 135 136// if the leading edge is over a dropoff, increase friction 137 start[0] = stop[0] = origin[0] + vel[0]/speed*16; 138 start[1] = stop[1] = origin[1] + vel[1]/speed*16; 139 start[2] = origin[2] + sv_player->u.v.mins[2]; 140 stop[2] = start[2] - 34; 141 142 trace = SV_Move (start, vec3_origin, vec3_origin, stop, true, sv_player); 143 144 if (trace.fraction == 1.0) 145 friction = sv_friction.value*sv_edgefriction.value; 146 else 147 friction = sv_friction.value; 148 149// apply friction 150 control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed; 151 newspeed = speed - host_frametime*control*friction; 152 153 if (newspeed < 0) 154 newspeed = 0; 155 newspeed /= speed; 156 157 vel[0] = vel[0] * newspeed; 158 vel[1] = vel[1] * newspeed; 159 vel[2] = vel[2] * newspeed; 160} 161 162/* 163============== 164SV_Accelerate 165============== 166*/ 167cvar_t sv_maxspeed = CVAR4("sv_maxspeed", "320", false, true); 168cvar_t sv_accelerate = CVAR2("sv_accelerate", "10"); 169#if 0 170void SV_Accelerate (vec3_t wishvel) 171{ 172 int i; 173 float addspeed, accelspeed; 174 vec3_t pushvec; 175 176 if (wishspeed == 0) 177 return; 178 179 VectorSubtract (wishvel, velocity, pushvec); 180 addspeed = VectorNormalize (pushvec); 181 182 accelspeed = sv_accelerate.value*host_frametime*addspeed; 183 if (accelspeed > addspeed) 184 accelspeed = addspeed; 185 186 for (i=0 ; i<3 ; i++) 187 velocity[i] += accelspeed*pushvec[i]; 188} 189#endif 190void SV_Accelerate (void) 191{ 192 int i; 193 float addspeed, accelspeed, currentspeed; 194 195 currentspeed = DotProduct (velocity, wishdir); 196 addspeed = wishspeed - currentspeed; 197 if (addspeed <= 0) 198 return; 199 accelspeed = sv_accelerate.value*host_frametime*wishspeed; 200 if (accelspeed > addspeed) 201 accelspeed = addspeed; 202 203 for (i=0 ; i<3 ; i++) 204 velocity[i] += accelspeed*wishdir[i]; 205} 206 207void SV_AirAccelerate (vec3_t wishveloc) 208{ 209 int i; 210 float addspeed, wishspd, accelspeed, currentspeed; 211 212 wishspd = VectorNormalize (wishveloc); 213 if (wishspd > 30) 214 wishspd = 30; 215 currentspeed = DotProduct (velocity, wishveloc); 216 addspeed = wishspd - currentspeed; 217 if (addspeed <= 0) 218 return; 219// accelspeed = sv_accelerate.value * host_frametime; 220 accelspeed = sv_accelerate.value*wishspeed * host_frametime; 221 if (accelspeed > addspeed) 222 accelspeed = addspeed; 223 224 for (i=0 ; i<3 ; i++) 225 velocity[i] += accelspeed*wishveloc[i]; 226} 227 228 229void DropPunchAngle (void) 230{ 231 float len; 232 233 len = VectorNormalize (sv_player->u.v.punchangle); 234 235 len -= 10*host_frametime; 236 if (len < 0) 237 len = 0; 238 VectorScale (sv_player->u.v.punchangle, len, sv_player->u.v.punchangle); 239} 240 241/* 242=================== 243SV_WaterMove 244 245=================== 246*/ 247void SV_WaterMove (void) 248{ 249 int i; 250 vec3_t wishvel; 251 float speed, newspeed, wishspeed, addspeed, accelspeed; 252 253// 254// user intentions 255// 256 AngleVectors (sv_player->u.v.v_angle, forward, right, up); 257 258 for (i=0 ; i<3 ; i++) 259 wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove; 260 261 if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove) 262 wishvel[2] -= 60; // drift towards bottom 263 else 264 wishvel[2] += cmd.upmove; 265 266 wishspeed = Length(wishvel); 267 if (wishspeed > sv_maxspeed.value) 268 { 269 VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); 270 wishspeed = sv_maxspeed.value; 271 } 272 wishspeed *= 0.7; 273 274// 275// water friction 276// 277 speed = Length (velocity); 278 if (speed) 279 { 280 newspeed = speed - host_frametime * speed * sv_friction.value; 281 if (newspeed < 0) 282 newspeed = 0; 283 VectorScale (velocity, newspeed/speed, velocity); 284 } 285 else 286 newspeed = 0; 287 288// 289// water acceleration 290// 291 if (!wishspeed) 292 return; 293 294 addspeed = wishspeed - newspeed; 295 if (addspeed <= 0) 296 return; 297 298 VectorNormalize (wishvel); 299 accelspeed = sv_accelerate.value * wishspeed * host_frametime; 300 if (accelspeed > addspeed) 301 accelspeed = addspeed; 302 303 for (i=0 ; i<3 ; i++) 304 velocity[i] += accelspeed * wishvel[i]; 305} 306 307void SV_WaterJump (void) 308{ 309 if (sv.time > sv_player->u.v.teleport_time 310 || !sv_player->u.v.waterlevel) 311 { 312 sv_player->u.v.flags = (int)sv_player->u.v.flags & ~FL_WATERJUMP; 313 sv_player->u.v.teleport_time = 0; 314 } 315 sv_player->u.v.velocity[0] = sv_player->u.v.movedir[0]; 316 sv_player->u.v.velocity[1] = sv_player->u.v.movedir[1]; 317} 318 319 320/* 321=================== 322SV_AirMove 323 324=================== 325*/ 326void SV_AirMove (void) 327{ 328 int i; 329 vec3_t wishvel; 330 float fmove, smove; 331 332 AngleVectors (sv_player->u.v.angles, forward, right, up); 333 334 fmove = cmd.forwardmove; 335 smove = cmd.sidemove; 336 337// hack to not let you back into teleporter 338 if (sv.time < sv_player->u.v.teleport_time && fmove < 0) 339 fmove = 0; 340 341 for (i=0 ; i<3 ; i++) 342 wishvel[i] = forward[i]*fmove + right[i]*smove; 343 344 if ( (int)sv_player->u.v.movetype != MOVETYPE_WALK) 345 wishvel[2] = cmd.upmove; 346 else 347 wishvel[2] = 0; 348 349 VectorCopy (wishvel, wishdir); 350 wishspeed = VectorNormalize(wishdir); 351 if (wishspeed > sv_maxspeed.value) 352 { 353 VectorScale (wishvel, sv_maxspeed.value/wishspeed, wishvel); 354 wishspeed = sv_maxspeed.value; 355 } 356 357 if ( sv_player->u.v.movetype == MOVETYPE_NOCLIP) 358 { // noclip 359 VectorCopy (wishvel, velocity); 360 } 361 else if ( onground ) 362 { 363 SV_UserFriction (); 364 SV_Accelerate (); 365 } 366 else 367 { // not on ground, so little effect on velocity 368 SV_AirAccelerate (wishvel); 369 } 370} 371 372/* 373=================== 374SV_ClientThink 375 376the move fields specify an intended velocity in pix/sec 377the angle fields specify an exact angular motion in degrees 378=================== 379*/ 380void SV_ClientThink (void) 381{ 382 vec3_t v_angle; 383 384 if (sv_player->u.v.movetype == MOVETYPE_NONE) 385 return; 386 387 onground = (int)sv_player->u.v.flags & FL_ONGROUND; 388 389 origin = sv_player->u.v.origin; 390 velocity = sv_player->u.v.velocity; 391 392 DropPunchAngle (); 393 394// 395// if dead, behave differently 396// 397 if (sv_player->u.v.health <= 0) 398 return; 399 400// 401// angles 402// show 1/3 the pitch angle and all the roll angle 403 cmd = host_client->cmd; 404 angles = sv_player->u.v.angles; 405 406 VectorAdd (sv_player->u.v.v_angle, sv_player->u.v.punchangle, v_angle); 407 angles[ROLL] = V_CalcRoll (sv_player->u.v.angles, sv_player->u.v.velocity)*4; 408 if (!sv_player->u.v.fixangle) 409 { 410 angles[PITCH] = -v_angle[PITCH]/3; 411 angles[YAW] = v_angle[YAW]; 412 } 413 414 if ( (int)sv_player->u.v.flags & FL_WATERJUMP ) 415 { 416 SV_WaterJump (); 417 return; 418 } 419// 420// walk 421// 422 if ( (sv_player->u.v.waterlevel >= 2) 423 && (sv_player->u.v.movetype != MOVETYPE_NOCLIP) ) 424 { 425 SV_WaterMove (); 426 return; 427 } 428 429 SV_AirMove (); 430} 431 432 433/* 434=================== 435SV_ReadClientMove 436=================== 437*/ 438void SV_ReadClientMove (usercmd_t *move) 439{ 440 int i; 441 vec3_t angle; 442 int bits; 443 444// read ping time 445 host_client->ping_times[host_client->num_pings%NUM_PING_TIMES] 446 = sv.time - MSG_ReadFloat (); 447 host_client->num_pings++; 448 449// read current angles 450 for (i=0 ; i<3 ; i++) 451 angle[i] = MSG_ReadAngle (); 452 453 VectorCopy (angle, host_client->edict->u.v.v_angle); 454 455// read movement 456 move->forwardmove = MSG_ReadShort (); 457 move->sidemove = MSG_ReadShort (); 458 move->upmove = MSG_ReadShort (); 459 460// read buttons 461 bits = MSG_ReadByte (); 462 host_client->edict->u.v.button0 = bits & 1; 463 host_client->edict->u.v.button2 = (bits & 2)>>1; 464 465 i = MSG_ReadByte (); 466 if (i) 467 host_client->edict->u.v.impulse = i; 468 469#ifdef QUAKE2 470// read light level 471 host_client->edict->u.v.light_level = MSG_ReadByte (); 472#endif 473} 474 475/* 476=================== 477SV_ReadClientMessage 478 479Returns false if the client should be killed 480=================== 481*/ 482qboolean SV_ReadClientMessage (void) 483{ 484 int ret; 485 int cmd; 486 char *s; 487 488 do 489 { 490nextmsg: 491 ret = NET_GetMessage (host_client->netconnection); 492 if (ret == -1) 493 { 494 Sys_Printf ("SV_ReadClientMessage: NET_GetMessage failed\n"); 495 return false; 496 } 497 if (!ret) 498 return true; 499 500 MSG_BeginReading (); 501 502 while (1) 503 { 504 if (!host_client->active) 505 return false; // a command caused an error 506 507 if (msg_badread) 508 { 509 Sys_Printf ("SV_ReadClientMessage: badread\n"); 510 return false; 511 } 512 513 cmd = MSG_ReadChar (); 514 515 switch (cmd) 516 { 517 case -1: 518 goto nextmsg; // end of message 519 520 default: 521 Sys_Printf ("SV_ReadClientMessage: unknown command char\n"); 522 return false; 523 524 case clc_nop: 525// Sys_Printf ("clc_nop\n"); 526 break; 527 528 case clc_stringcmd: 529 s = MSG_ReadString (); 530 if (host_client->privileged) 531 ret = 2; 532 else 533 ret = 0; 534 if (Q_strncasecmp(s, "status", 6) == 0) 535 ret = 1; 536 else if (Q_strncasecmp(s, "god", 3) == 0) 537 ret = 1; 538 else if (Q_strncasecmp(s, "notarget", 8) == 0) 539 ret = 1; 540 else if (Q_strncasecmp(s, "fly", 3) == 0) 541 ret = 1; 542 else if (Q_strncasecmp(s, "name", 4) == 0) 543 ret = 1; 544 else if (Q_strncasecmp(s, "noclip", 6) == 0) 545 ret = 1; 546 else if (Q_strncasecmp(s, "say", 3) == 0) 547 ret = 1; 548 else if (Q_strncasecmp(s, "say_team", 8) == 0) 549 ret = 1; 550 else if (Q_strncasecmp(s, "tell", 4) == 0) 551 ret = 1; 552 else if (Q_strncasecmp(s, "color", 5) == 0) 553 ret = 1; 554 else if (Q_strncasecmp(s, "kill", 4) == 0) 555 ret = 1; 556 else if (Q_strncasecmp(s, "pause", 5) == 0) 557 ret = 1; 558 else if (Q_strncasecmp(s, "spawn", 5) == 0) 559 ret = 1; 560 else if (Q_strncasecmp(s, "begin", 5) == 0) 561 ret = 1; 562 else if (Q_strncasecmp(s, "prespawn", 8) == 0) 563 ret = 1; 564 else if (Q_strncasecmp(s, "kick", 4) == 0) 565 ret = 1; 566 else if (Q_strncasecmp(s, "ping", 4) == 0) 567 ret = 1; 568 else if (Q_strncasecmp(s, "give", 4) == 0) 569 ret = 1; 570 else if (Q_strncasecmp(s, "ban", 3) == 0) 571 ret = 1; 572 if (ret == 2) 573 Cbuf_InsertText (s); 574 else if (ret == 1) 575 Cmd_ExecuteString (s, src_client); 576 else 577 Con_DPrintf("%s tried to %s\n", host_client->name, s); 578 break; 579 580 case clc_disconnect: 581// Sys_Printf ("SV_ReadClientMessage: client disconnected\n"); 582 return false; 583 584 case clc_move: 585 SV_ReadClientMove (&host_client->cmd); 586 break; 587 } 588 } 589 } while (ret == 1); 590 591 return true; 592} 593 594 595/* 596================== 597SV_RunClients 598================== 599*/ 600void SV_RunClients (void) 601{ 602 int i; 603 604 for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) 605 { 606 if (!host_client->active) 607 continue; 608 609 sv_player = host_client->edict; 610 611 if (!SV_ReadClientMessage ()) 612 { 613 SV_DropClient (false); // client misbehaved... 614 continue; 615 } 616 617 if (!host_client->spawned) 618 { 619 // clear client movement until a new packet is received 620 memset (&host_client->cmd, 0, sizeof(host_client->cmd)); 621 continue; 622 } 623 624// always pause in single player if in console or menus 625 if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) ) 626 SV_ClientThink (); 627 } 628} 629 630