]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_user.c
*** empty log message ***
[divverent/darkplaces.git] / sv_user.c
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 // sv_user.c -- server code for moving users
21
22 #include "quakedef.h"
23
24 edict_t *sv_player;
25
26 cvar_t sv_edgefriction = {0, "edgefriction", "2"};
27 cvar_t sv_deltacompress = {0, "sv_deltacompress", "1"};
28 cvar_t sv_idealpitchscale = {0, "sv_idealpitchscale","0.8"};
29 cvar_t sv_maxspeed = {CVAR_NOTIFY, "sv_maxspeed", "320"};
30 cvar_t sv_accelerate = {0, "sv_accelerate", "10"};
31
32 static usercmd_t cmd;
33
34
35 /*
36 ===============
37 SV_SetIdealPitch
38 ===============
39 */
40 #define MAX_FORWARD     6
41 void SV_SetIdealPitch (void)
42 {
43         float   angleval, sinval, cosval;
44         trace_t tr;
45         vec3_t  top, bottom;
46         float   z[MAX_FORWARD];
47         int             i, j;
48         int             step, dir, steps;
49
50         if (!((int)sv_player->v->flags & FL_ONGROUND))
51                 return;
52
53         angleval = sv_player->v->angles[YAW] * M_PI*2 / 360;
54         sinval = sin(angleval);
55         cosval = cos(angleval);
56
57         for (i=0 ; i<MAX_FORWARD ; i++)
58         {
59                 top[0] = sv_player->v->origin[0] + cosval*(i+3)*12;
60                 top[1] = sv_player->v->origin[1] + sinval*(i+3)*12;
61                 top[2] = sv_player->v->origin[2] + sv_player->v->view_ofs[2];
62
63                 bottom[0] = top[0];
64                 bottom[1] = top[1];
65                 bottom[2] = top[2] - 160;
66
67                 tr = SV_Move (top, vec3_origin, vec3_origin, bottom, MOVE_NOMONSTERS, sv_player);
68                 // if looking at a wall, leave ideal the way is was
69                 if (tr.startsolid)
70                         return;
71
72                 // near a dropoff
73                 if (tr.fraction == 1)
74                         return;
75
76                 z[i] = top[2] + tr.fraction*(bottom[2]-top[2]);
77         }
78
79         dir = 0;
80         steps = 0;
81         for (j=1 ; j<i ; j++)
82         {
83                 step = z[j] - z[j-1];
84                 if (step > -ON_EPSILON && step < ON_EPSILON)
85                         continue;
86
87                 // mixed changes
88                 if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) )
89                         return;
90
91                 steps++;
92                 dir = step;
93         }
94
95         if (!dir)
96         {
97                 sv_player->v->idealpitch = 0;
98                 return;
99         }
100
101         if (steps < 2)
102                 return;
103         sv_player->v->idealpitch = -dir * sv_idealpitchscale.value;
104 }
105
106 #if 1
107 static vec3_t wishdir, forward, right, up;
108 static float wishspeed;
109
110 static qboolean onground;
111
112 /*
113 ==================
114 SV_UserFriction
115
116 ==================
117 */
118 void SV_UserFriction (void)
119 {
120         float speed, newspeed, control, friction;
121         vec3_t start, stop;
122         trace_t trace;
123
124         speed = sqrt(sv_player->v->velocity[0]*sv_player->v->velocity[0]+sv_player->v->velocity[1]*sv_player->v->velocity[1]);
125         if (!speed)
126                 return;
127
128         // if the leading edge is over a dropoff, increase friction
129         start[0] = stop[0] = sv_player->v->origin[0] + sv_player->v->velocity[0]/speed*16;
130         start[1] = stop[1] = sv_player->v->origin[1] + sv_player->v->velocity[1]/speed*16;
131         start[2] = sv_player->v->origin[2] + sv_player->v->mins[2];
132         stop[2] = start[2] - 34;
133
134         trace = SV_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, sv_player);
135
136         if (trace.fraction == 1.0)
137                 friction = sv_friction.value*sv_edgefriction.value;
138         else
139                 friction = sv_friction.value;
140
141         // apply friction
142         control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed;
143         newspeed = speed - sv.frametime*control*friction;
144
145         if (newspeed < 0)
146                 newspeed = 0;
147         else
148                 newspeed /= speed;
149
150         VectorScale(sv_player->v->velocity, newspeed, sv_player->v->velocity);
151 }
152
153 /*
154 ==============
155 SV_Accelerate
156 ==============
157 */
158 void SV_Accelerate (void)
159 {
160         int i;
161         float addspeed, accelspeed, currentspeed;
162
163         currentspeed = DotProduct (sv_player->v->velocity, wishdir);
164         addspeed = wishspeed - currentspeed;
165         if (addspeed <= 0)
166                 return;
167         accelspeed = sv_accelerate.value*sv.frametime*wishspeed;
168         if (accelspeed > addspeed)
169                 accelspeed = addspeed;
170
171         for (i=0 ; i<3 ; i++)
172                 sv_player->v->velocity[i] += accelspeed*wishdir[i];
173 }
174
175 void SV_AirAccelerate (vec3_t wishveloc)
176 {
177         int i;
178         float addspeed, wishspd, accelspeed, currentspeed;
179
180         wishspd = VectorNormalizeLength (wishveloc);
181         if (wishspd > 30)
182                 wishspd = 30;
183         currentspeed = DotProduct (sv_player->v->velocity, wishveloc);
184         addspeed = wishspd - currentspeed;
185         if (addspeed <= 0)
186                 return;
187         accelspeed = sv_accelerate.value*wishspeed * sv.frametime;
188         if (accelspeed > addspeed)
189                 accelspeed = addspeed;
190
191         for (i=0 ; i<3 ; i++)
192                 sv_player->v->velocity[i] += accelspeed*wishveloc[i];
193 }
194
195
196 void DropPunchAngle (void)
197 {
198         float len;
199         eval_t *val;
200
201         len = VectorNormalizeLength (sv_player->v->punchangle);
202
203         len -= 10*sv.frametime;
204         if (len < 0)
205                 len = 0;
206         VectorScale (sv_player->v->punchangle, len, sv_player->v->punchangle);
207
208         if ((val = GETEDICTFIELDVALUE(sv_player, eval_punchvector)))
209         {
210                 len = VectorNormalizeLength (val->vector);
211
212                 len -= 20*sv.frametime;
213                 if (len < 0)
214                         len = 0;
215                 VectorScale (val->vector, len, val->vector);
216         }
217 }
218
219 /*
220 ===================
221 SV_FreeMove
222 ===================
223 */
224 void SV_FreeMove (void)
225 {
226         int i;
227         float wishspeed;
228
229         AngleVectors (sv_player->v->v_angle, forward, right, up);
230
231         for (i = 0; i < 3; i++)
232                 sv_player->v->velocity[i] = forward[i] * cmd.forwardmove + right[i] * cmd.sidemove;
233
234         sv_player->v->velocity[2] += cmd.upmove;
235
236         wishspeed = VectorLength(sv_player->v->velocity);
237         if (wishspeed > sv_maxspeed.value)
238                 VectorScale(sv_player->v->velocity, sv_maxspeed.value / wishspeed, sv_player->v->velocity);
239 }
240
241 /*
242 ===================
243 SV_WaterMove
244
245 ===================
246 */
247 void SV_WaterMove (void)
248 {
249         int i;
250         vec3_t wishvel;
251         float speed, newspeed, wishspeed, addspeed, accelspeed, temp;
252
253         // user intentions
254         AngleVectors (sv_player->v->v_angle, forward, right, up);
255
256         for (i=0 ; i<3 ; i++)
257                 wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove;
258
259         if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove)
260                 wishvel[2] -= 60;               // drift towards bottom
261         else
262                 wishvel[2] += cmd.upmove;
263
264         wishspeed = VectorLength(wishvel);
265         if (wishspeed > sv_maxspeed.value)
266         {
267                 temp = sv_maxspeed.value/wishspeed;
268                 VectorScale (wishvel, temp, wishvel);
269                 wishspeed = sv_maxspeed.value;
270         }
271         wishspeed *= 0.7;
272
273         // water friction
274         speed = VectorLength(sv_player->v->velocity);
275         if (speed)
276         {
277                 newspeed = speed - sv.frametime * speed * sv_friction.value;
278                 if (newspeed < 0)
279                         newspeed = 0;
280                 temp = newspeed/speed;
281                 VectorScale(sv_player->v->velocity, temp, sv_player->v->velocity);
282         }
283         else
284                 newspeed = 0;
285
286         // water acceleration
287         if (!wishspeed)
288                 return;
289
290         addspeed = wishspeed - newspeed;
291         if (addspeed <= 0)
292                 return;
293
294         VectorNormalize (wishvel);
295         accelspeed = sv_accelerate.value * wishspeed * sv.frametime;
296         if (accelspeed > addspeed)
297                 accelspeed = addspeed;
298
299         for (i=0 ; i<3 ; i++)
300                 sv_player->v->velocity[i] += accelspeed * wishvel[i];
301 }
302
303 void SV_WaterJump (void)
304 {
305         if (sv.time > sv_player->v->teleport_time || !sv_player->v->waterlevel)
306         {
307                 sv_player->v->flags = (int)sv_player->v->flags & ~FL_WATERJUMP;
308                 sv_player->v->teleport_time = 0;
309         }
310         sv_player->v->velocity[0] = sv_player->v->movedir[0];
311         sv_player->v->velocity[1] = sv_player->v->movedir[1];
312 }
313
314
315 /*
316 ===================
317 SV_AirMove
318
319 ===================
320 */
321 void SV_AirMove (void)
322 {
323         int i;
324         vec3_t wishvel;
325         float fmove, smove, temp;
326
327         // LordHavoc: correct quake movement speed bug when looking up/down
328         wishvel[0] = wishvel[2] = 0;
329         wishvel[1] = sv_player->v->angles[1];
330         AngleVectors (wishvel, forward, right, up);
331
332         fmove = cmd.forwardmove;
333         smove = cmd.sidemove;
334
335 // hack to not let you back into teleporter
336         if (sv.time < sv_player->v->teleport_time && fmove < 0)
337                 fmove = 0;
338
339         for (i=0 ; i<3 ; i++)
340                 wishvel[i] = forward[i]*fmove + right[i]*smove;
341
342         if ((int)sv_player->v->movetype != MOVETYPE_WALK)
343                 wishvel[2] += cmd.upmove;
344
345         VectorCopy (wishvel, wishdir);
346         wishspeed = VectorNormalizeLength(wishdir);
347         if (wishspeed > sv_maxspeed.value)
348         {
349                 temp = sv_maxspeed.value/wishspeed;
350                 VectorScale (wishvel, temp, wishvel);
351                 wishspeed = sv_maxspeed.value;
352         }
353
354         if (sv_player->v->movetype == MOVETYPE_NOCLIP)
355         {
356                 // noclip
357                 VectorCopy (wishvel, sv_player->v->velocity);
358         }
359         else if ( onground )
360         {
361                 SV_UserFriction ();
362                 SV_Accelerate ();
363         }
364         else
365         {
366                 // not on ground, so little effect on velocity
367                 SV_AirAccelerate (wishvel);
368         }
369 }
370
371 /*
372 ===================
373 SV_ClientThink
374
375 the move fields specify an intended velocity in pix/sec
376 the angle fields specify an exact angular motion in degrees
377 ===================
378 */
379 void SV_ClientThink (void)
380 {
381         vec3_t v_angle;
382
383         if (sv_player->v->movetype == MOVETYPE_NONE)
384                 return;
385
386         onground = (int)sv_player->v->flags & FL_ONGROUND;
387
388         DropPunchAngle ();
389
390         // if dead, behave differently
391         if (sv_player->v->health <= 0)
392                 return;
393
394         cmd = host_client->cmd;
395
396         // angles
397         // show 1/3 the pitch angle and all the roll angle
398         VectorAdd (sv_player->v->v_angle, sv_player->v->punchangle, v_angle);
399         sv_player->v->angles[ROLL] = V_CalcRoll (sv_player->v->angles, sv_player->v->velocity)*4;
400         if (!sv_player->v->fixangle)
401         {
402                 sv_player->v->angles[PITCH] = -v_angle[PITCH]/3;
403                 sv_player->v->angles[YAW] = v_angle[YAW];
404         }
405
406         if ( (int)sv_player->v->flags & FL_WATERJUMP )
407         {
408                 SV_WaterJump ();
409                 return;
410         }
411
412         /*
413         // Player is (somehow) outside of the map, or flying, or noclipping
414         if (sv_player->v->movetype != MOVETYPE_NOCLIP && (sv_player->v->movetype == MOVETYPE_FLY || SV_TestEntityPosition (sv_player)))
415         //if (sv_player->v->movetype == MOVETYPE_NOCLIP || sv_player->v->movetype == MOVETYPE_FLY || SV_TestEntityPosition (sv_player))
416         {
417                 SV_FreeMove ();
418                 return;
419         }
420         */
421
422         // walk
423         if ((sv_player->v->waterlevel >= 2) && (sv_player->v->movetype != MOVETYPE_NOCLIP))
424         {
425                 SV_WaterMove ();
426                 return;
427         }
428
429         SV_AirMove ();
430 }
431
432 #else
433
434 extern cvar_t cl_rollspeed;
435 extern cvar_t cl_rollangle;
436 void SV_ClientThink(void)
437 {
438         int j;
439         vec3_t wishvel, wishdir, v, v_forward, v_right, v_up, start, stop;
440         float wishspeed, f, limit;
441         trace_t trace;
442
443         if (sv_player->v->movetype == MOVETYPE_NONE)
444                 return;
445
446         f = DotProduct(sv_player->v->punchangle, sv_player->v->punchangle);
447         if (f)
448         {
449                 limit = sqrt(f);
450                 f = (limit - 10 * sv.frametime);
451                 f /= limit;
452                 f = max(0, f);
453                 VectorScale(sv_player->v->punchangle, f, sv_player->v->punchangle);
454         }
455
456         // if dead, behave differently
457         if (sv_player->v->health <= 0)
458                 return;
459
460         AngleVectors(sv_player->v->v_angle, v_forward, v_right, v_up);
461         // show 1/3 the pitch angle and all the roll angle
462         f = DotProduct(sv_player->v->velocity, v_right) * (1.0 / cl_rollspeed.value);
463         sv_player->v->angles[2] = bound(-1, f, 1) * cl_rollangle.value * 4;
464         if (!sv_player->v->fixangle)
465         {
466                 sv_player->v->angles[0] = (sv_player->v->v_angle[0] + sv_player->v->punchangle[0]) * -0.333;
467                 sv_player->v->angles[1] = sv_player->v->v_angle[1] + sv_player->v->punchangle[1];
468         }
469
470         if ((int)sv_player->v->flags & FL_WATERJUMP)
471         {
472                 sv_player->v->velocity[0] = sv_player->v->movedir[0];
473                 sv_player->v->velocity[1] = sv_player->v->movedir[1];
474                 if (sv.time > sv_player->v->teleport_time || sv_player->v->waterlevel == 0)
475                 {
476                         sv_player->v->flags = (int)sv_player->v->flags - ((int)sv_player->v->flags & FL_WATERJUMP);
477                         sv_player->v->teleport_time = 0;
478                 }
479                 return;
480         }
481
482         // swim
483         if (sv_player->v->waterlevel >= 2)
484         if (sv_player->v->movetype != MOVETYPE_NOCLIP)
485         {
486                 if (host_client->cmd.forwardmove == 0 && host_client->cmd.sidemove == 0 && host_client->cmd.upmove == 0)
487                 {
488                         // drift towards bottom
489                         wishvel[0] = 0;
490                         wishvel[1] = 0;
491                         wishvel[2] = -60;
492                 }
493                 else
494                 {
495                         for (j = 0;j < 3;j++)
496                                 wishvel[j] = v_forward[j] * host_client->cmd.forwardmove + v_right[j] * host_client->cmd.sidemove;
497                         wishvel[2] += host_client->cmd.upmove;
498                 }
499
500                 wishspeed = VectorLength(wishvel);
501                 wishspeed = min(wishspeed, sv_maxspeed.value) * 0.7;
502
503                 // water friction
504                 f = VectorLength(sv_player->v->velocity) * (1 - sv.frametime * sv_friction.value);
505                 if (f > 0)
506                         f /= VectorLength(sv_player->v->velocity);
507                 else
508                         f = 0;
509                 VectorScale(sv_player->v->velocity, f, sv_player->v->velocity);
510
511                 // water acceleration
512                 if (wishspeed <= f)
513                         return;
514
515                 f = wishspeed - f;
516                 limit = sv_accelerate.value * wishspeed * sv.frametime;
517                 if (f > limit)
518                         f = limit;
519                 limit = VectorLength(wishvel);
520                 if (limit)
521                         f /= limit;
522                 VectorMA(sv_player->v->velocity, f, wishvel, sv_player->v->velocity);
523                 return;
524         }
525
526         // if not flying, move horizontally only
527         if (sv_player->v->movetype != MOVETYPE_FLY)
528         {
529                 VectorClear(wishvel);
530                 wishvel[1] = sv_player->v->v_angle[1];
531                 AngleVectors(wishvel, v_forward, v_right, v_up);
532         }
533
534         // hack to not let you back into teleporter
535         VectorScale(v_right, host_client->cmd.sidemove, wishvel);
536         if (sv.time >= sv_player->v->teleport_time || host_client->cmd.forwardmove > 0)
537                 VectorMA(wishvel, host_client->cmd.forwardmove, v_forward, wishvel);
538         if (sv_player->v->movetype != MOVETYPE_WALK)
539                 wishvel[2] += cmd.upmove;
540
541         VectorCopy(wishvel, wishdir);
542         VectorNormalize(wishdir);
543         wishspeed = VectorLength(wishvel);
544         if (wishspeed > sv_maxspeed.value)
545                 wishspeed = sv_maxspeed.value;
546
547         if (sv_player->v->movetype == MOVETYPE_NOCLIP || sv_player->v->movetype == MOVETYPE_FLY)
548         {
549                 VectorScale(wishdir, wishspeed, sv_player->v->velocity);
550                 return;
551         }
552
553         if ((int)sv_player->v->flags & FL_ONGROUND) // walking
554         {
555                 // friction
556                 f = sv_player->v->velocity[0] * sv_player->v->velocity[0] + sv_player->v->velocity[1] * sv_player->v->velocity[1];
557                 if (f)
558                 {
559                         f = sqrt(f);
560                         VectorCopy(sv_player->v->velocity, v);
561                         v[2] = 0;
562
563                         // if the leading edge is over a dropoff, increase friction
564                         limit = 16.0f / f;
565                         VectorMA(sv_player->v->origin, limit, v, v);
566                         v[2] += sv_player->v->mins[2];
567
568                         VectorCopy(v, start);
569                         VectorCopy(v, stop);
570                         stop[2] -= 34;
571                         trace = SV_Move(start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, sv_player);
572
573                         // apply friction
574                         if (f < sv_stopspeed.value)
575                                 f = sv_stopspeed.value / f;
576                         else
577                                 f = 1;
578                         if (trace.fraction == 1)
579                                 f *= sv_edgefriction.value;
580                         f = 1 - sv.frametime * f * sv_friction.value;
581
582                         if (f < 0)
583                                 f = 0;
584                         VectorScale(sv_player->v->velocity, f, sv_player->v->velocity);
585                 }
586         }
587         else // airborn
588                 wishspeed = min(wishspeed, 30);
589
590         // ground or air acceleration
591         f = wishspeed - DotProduct(sv_player->v->velocity, wishdir);
592         if (f > 0)
593         {
594                 limit = sv_accelerate.value * sv.frametime * wishspeed;
595                 if (f > limit)
596                         f = limit;
597                 VectorMA(sv_player->v->velocity, f, wishdir, sv_player->v->velocity);
598         }
599 }
600 #endif
601
602 /*
603 ===================
604 SV_ReadClientMove
605 ===================
606 */
607 void SV_ReadClientMove (usercmd_t *move)
608 {
609         int i;
610         vec3_t angle;
611         int bits;
612         eval_t *val;
613         float total;
614
615         // read ping time
616         host_client->ping_times[host_client->num_pings % NUM_PING_TIMES] = sv.time - MSG_ReadFloat ();
617         host_client->num_pings++;
618         for (i=0, total = 0;i < NUM_PING_TIMES;i++)
619                 total += host_client->ping_times[i];
620         // can be used for prediction
621         host_client->ping = total / NUM_PING_TIMES;
622         if ((val = GETEDICTFIELDVALUE(sv_player, eval_ping)))
623                 val->_float = host_client->ping * 1000.0;
624
625         // read current angles
626         // PROTOCOL_DARKPLACES4
627         for (i = 0;i < 3;i++)
628                 angle[i] = MSG_ReadPreciseAngle();
629
630         VectorCopy (angle, sv_player->v->v_angle);
631
632         // read movement
633         move->forwardmove = MSG_ReadShort ();
634         move->sidemove = MSG_ReadShort ();
635         move->upmove = MSG_ReadShort ();
636         if ((val = GETEDICTFIELDVALUE(sv_player, eval_movement)))
637         {
638                 val->vector[0] = move->forwardmove;
639                 val->vector[1] = move->sidemove;
640                 val->vector[2] = move->upmove;
641         }
642
643         // read buttons
644         bits = MSG_ReadByte ();
645         sv_player->v->button0 = bits & 1;
646         sv_player->v->button2 = (bits & 2)>>1;
647         // LordHavoc: added 6 new buttons
648         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button3))) val->_float = ((bits >> 2) & 1);
649         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button4))) val->_float = ((bits >> 3) & 1);
650         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button5))) val->_float = ((bits >> 4) & 1);
651         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button6))) val->_float = ((bits >> 5) & 1);
652         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button7))) val->_float = ((bits >> 6) & 1);
653         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button8))) val->_float = ((bits >> 7) & 1);
654
655         i = MSG_ReadByte ();
656         if (i)
657                 sv_player->v->impulse = i;
658 }
659
660 /*
661 ===================
662 SV_ReadClientMessage
663 ===================
664 */
665 extern void SV_SendServerinfo(client_t *client);
666 void SV_ReadClientMessage(void)
667 {
668         int cmd;
669         char *s;
670
671         //MSG_BeginReading ();
672
673         for(;;)
674         {
675                 if (!host_client->active)
676                 {
677                         // a command caused an error
678                         SV_DropClient (false);
679                         return;
680                 }
681
682                 if (msg_badread)
683                 {
684                         Con_Print("SV_ReadClientMessage: badread\n");
685                         SV_DropClient (false);
686                         return;
687                 }
688
689                 cmd = MSG_ReadChar ();
690                 if (cmd == -1)
691                 {
692                         // end of message
693                         break;
694                 }
695
696                 switch (cmd)
697                 {
698                 default:
699                         Con_Printf("SV_ReadClientMessage: unknown command char %i\n", cmd);
700                         SV_DropClient (false);
701                         return;
702
703                 case clc_nop:
704                         break;
705
706                 case clc_stringcmd:
707                         s = MSG_ReadString ();
708                         if (strncasecmp(s, "spawn", 5) == 0
709                          || strncasecmp(s, "begin", 5) == 0
710                          || strncasecmp(s, "prespawn", 8) == 0)
711                                 Cmd_ExecuteString (s, src_client);
712                         else if (SV_ParseClientCommandQC)
713                         {
714                                 G_INT(OFS_PARM0) = PR_SetString(s);
715                                 pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
716                                 PR_ExecuteProgram ((func_t)(SV_ParseClientCommandQC - pr_functions), "");
717                         }
718                         else if (strncasecmp(s, "status", 6) == 0
719                          || strncasecmp(s, "name", 4) == 0
720                          || strncasecmp(s, "say", 3) == 0
721                          || strncasecmp(s, "say_team", 8) == 0
722                          || strncasecmp(s, "tell", 4) == 0
723                          || strncasecmp(s, "color", 5) == 0
724                          || strncasecmp(s, "kill", 4) == 0
725                          || strncasecmp(s, "pause", 5) == 0
726                          || strncasecmp(s, "kick", 4) == 0
727                          || strncasecmp(s, "ping", 4) == 0
728                          || strncasecmp(s, "ban", 3) == 0
729                          || strncasecmp(s, "pmodel", 6) == 0
730                          || strncasecmp(s, "rate", 4) == 0
731                          || (gamemode == GAME_NEHAHRA && (strncasecmp(s, "max", 3) == 0 || strncasecmp(s, "monster", 7) == 0 || strncasecmp(s, "scrag", 5) == 0 || strncasecmp(s, "gimme", 5) == 0 || strncasecmp(s, "wraith", 6) == 0))
732                          || (gamemode != GAME_NEHAHRA && (strncasecmp(s, "god", 3) == 0 || strncasecmp(s, "notarget", 8) == 0 || strncasecmp(s, "fly", 3) == 0 || strncasecmp(s, "give", 4) == 0 || strncasecmp(s, "noclip", 6) == 0)))
733                                 Cmd_ExecuteString (s, src_client);
734                         else
735                                 Con_Printf("%s tried to %s\n", host_client->name, s);
736                         break;
737
738                 case clc_disconnect:
739                         SV_DropClient (false); // client wants to disconnect
740                         return;
741
742                 case clc_move:
743                         SV_ReadClientMove (&host_client->cmd);
744                         break;
745
746                 case clc_ackentities:
747                         host_client->entitydatabase4->ackframenum = MSG_ReadLong();
748                         if (developer_networkentities.integer >= 1)
749                                 Con_Printf("recv clc_ackentities %i\n", host_client->entitydatabase4->ackframenum);
750                         EntityFrame4_AckFrame(host_client->entitydatabase4, host_client->entitydatabase4->ackframenum);
751                         break;
752                 }
753         }
754 }
755
756 /*
757 ==================
758 SV_RunClients
759 ==================
760 */
761 void SV_RunClients (void)
762 {
763         int i;
764
765         for (i = 0, host_client = svs.clients;i < svs.maxclients;i++, host_client++)
766         {
767                 if (!host_client->active)
768                         continue;
769
770                 if (!host_client->spawned)
771                 {
772                         // clear client movement until a new packet is received
773                         memset (&host_client->cmd, 0, sizeof(host_client->cmd));
774                         continue;
775                 }
776
777                 if (sv.frametime)
778                 {
779                         sv_player = host_client->edict;
780
781                         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
782                         if (SV_PlayerPhysicsQC)
783                         {
784                                 pr_global_struct->time = sv.time;
785                                 pr_global_struct->self = EDICT_TO_PROG(sv_player);
786                                 PR_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - pr_functions), "");
787                         }
788                         else
789                                 SV_ClientThink ();
790
791                         SV_CheckVelocity(sv_player);
792
793                         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
794                         // player_run/player_stand1 does not horribly malfunction if the
795                         // velocity becomes a number that is both == 0 and != 0
796                         // (sounds to me like NaN but to be absolutely safe...)
797                         if (DotProduct(sv_player->v->velocity, sv_player->v->velocity) < 0.0001)
798                                 VectorClear(sv_player->v->velocity);
799                 }
800         }
801 }
802