]> icculus.org git repositories - divverent/darkplaces.git/blob - sv_user.c
disable -Werror because it makes a mess of releases if anyone has warnings (like...
[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 vec3_t forward, right, up;
33
34 vec3_t wishdir;
35 float wishspeed;
36
37 // world
38 float *angles;
39 float *origin;
40 float *velocity;
41
42 qboolean onground;
43
44 usercmd_t cmd;
45
46
47 /*
48 ===============
49 SV_SetIdealPitch
50 ===============
51 */
52 #define MAX_FORWARD     6
53 void 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->v->flags & FL_ONGROUND))
63                 return;
64
65         angleval = sv_player->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->v->origin[0] + cosval*(i+3)*12;
72                 top[1] = sv_player->v->origin[1] + sinval*(i+3)*12;
73                 top[2] = sv_player->v->origin[2] + sv_player->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, MOVE_NOMONSTERS, sv_player);
80                 // if looking at a wall, leave ideal the way is was
81                 if (tr.allsolid)
82                         return;
83
84                 // near a dropoff
85                 if (tr.fraction == 1)
86                         return;
87
88                 z[i] = top[2] + tr.fraction*(bottom[2]-top[2]);
89         }
90
91         dir = 0;
92         steps = 0;
93         for (j=1 ; j<i ; j++)
94         {
95                 step = z[j] - z[j-1];
96                 if (step > -ON_EPSILON && step < ON_EPSILON)
97                         continue;
98
99                 // mixed changes
100                 if (dir && ( step-dir > ON_EPSILON || step-dir < -ON_EPSILON ) )
101                         return;
102
103                 steps++;
104                 dir = step;
105         }
106
107         if (!dir)
108         {
109                 sv_player->v->idealpitch = 0;
110                 return;
111         }
112
113         if (steps < 2)
114                 return;
115         sv_player->v->idealpitch = -dir * sv_idealpitchscale.value;
116 }
117
118
119 /*
120 ==================
121 SV_UserFriction
122
123 ==================
124 */
125 void SV_UserFriction (void)
126 {
127         float *vel, speed, newspeed, control, friction;
128         vec3_t start, stop;
129         trace_t trace;
130
131         vel = velocity;
132
133         speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
134         if (!speed)
135                 return;
136
137         // if the leading edge is over a dropoff, increase friction
138         start[0] = stop[0] = origin[0] + vel[0]/speed*16;
139         start[1] = stop[1] = origin[1] + vel[1]/speed*16;
140         start[2] = origin[2] + sv_player->v->mins[2];
141         stop[2] = start[2] - 34;
142
143         trace = SV_Move (start, vec3_origin, vec3_origin, stop, MOVE_NOMONSTERS, sv_player);
144
145         if (trace.fraction == 1.0)
146                 friction = sv_friction.value*sv_edgefriction.value;
147         else
148                 friction = sv_friction.value;
149
150         // apply friction
151         control = speed < sv_stopspeed.value ? sv_stopspeed.value : speed;
152         newspeed = speed - sv.frametime*control*friction;
153
154         if (newspeed < 0)
155                 newspeed = 0;
156         else
157                 newspeed /= speed;
158
159         vel[0] = vel[0] * newspeed;
160         vel[1] = vel[1] * newspeed;
161         vel[2] = vel[2] * newspeed;
162 }
163
164 /*
165 ==============
166 SV_Accelerate
167 ==============
168 */
169 void SV_Accelerate (void)
170 {
171         int i;
172         float addspeed, accelspeed, currentspeed;
173
174         currentspeed = DotProduct (velocity, wishdir);
175         addspeed = wishspeed - currentspeed;
176         if (addspeed <= 0)
177                 return;
178         accelspeed = sv_accelerate.value*sv.frametime*wishspeed;
179         if (accelspeed > addspeed)
180                 accelspeed = addspeed;
181
182         for (i=0 ; i<3 ; i++)
183                 velocity[i] += accelspeed*wishdir[i];
184 }
185
186 void SV_AirAccelerate (vec3_t wishveloc)
187 {
188         int i;
189         float addspeed, wishspd, accelspeed, currentspeed;
190
191         wishspd = VectorNormalizeLength (wishveloc);
192         if (wishspd > 30)
193                 wishspd = 30;
194         currentspeed = DotProduct (velocity, wishveloc);
195         addspeed = wishspd - currentspeed;
196         if (addspeed <= 0)
197                 return;
198         accelspeed = sv_accelerate.value*wishspeed * sv.frametime;
199         if (accelspeed > addspeed)
200                 accelspeed = addspeed;
201
202         for (i=0 ; i<3 ; i++)
203                 velocity[i] += accelspeed*wishveloc[i];
204 }
205
206
207 void DropPunchAngle (void)
208 {
209         float len;
210         eval_t *val;
211
212         len = VectorNormalizeLength (sv_player->v->punchangle);
213
214         len -= 10*sv.frametime;
215         if (len < 0)
216                 len = 0;
217         VectorScale (sv_player->v->punchangle, len, sv_player->v->punchangle);
218
219         if ((val = GETEDICTFIELDVALUE(sv_player, eval_punchvector)))
220         {
221                 len = VectorNormalizeLength (val->vector);
222
223                 len -= 20*sv.frametime;
224                 if (len < 0)
225                         len = 0;
226                 VectorScale (val->vector, len, val->vector);
227         }
228 }
229
230 /*
231 ===================
232 SV_FreeMove
233 ===================
234 */
235 void SV_FreeMove (void)
236 {
237         int i;
238         float wishspeed;
239
240         AngleVectors (sv_player->v->v_angle, forward, right, up);
241
242         for (i = 0; i < 3; i++)
243                 velocity[i] = forward[i] * cmd.forwardmove + right[i] * cmd.sidemove;
244
245         velocity[2] += cmd.upmove;
246
247         wishspeed = VectorLength (velocity);
248         if (wishspeed > sv_maxspeed.value)
249         {
250                 VectorScale (velocity, sv_maxspeed.value / wishspeed, velocity);
251                 wishspeed = sv_maxspeed.value;
252         }
253 }
254
255 /*
256 ===================
257 SV_WaterMove
258
259 ===================
260 */
261 void SV_WaterMove (void)
262 {
263         int i;
264         vec3_t wishvel;
265         float speed, newspeed, wishspeed, addspeed, accelspeed, temp;
266
267         // user intentions
268         AngleVectors (sv_player->v->v_angle, forward, right, up);
269
270         for (i=0 ; i<3 ; i++)
271                 wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove;
272
273         if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove)
274                 wishvel[2] -= 60;               // drift towards bottom
275         else
276                 wishvel[2] += cmd.upmove;
277
278         wishspeed = VectorLength(wishvel);
279         if (wishspeed > sv_maxspeed.value)
280         {
281                 temp = sv_maxspeed.value/wishspeed;
282                 VectorScale (wishvel, temp, wishvel);
283                 wishspeed = sv_maxspeed.value;
284         }
285         wishspeed *= 0.7;
286
287         // water friction
288         speed = VectorLength (velocity);
289         if (speed)
290         {
291                 newspeed = speed - sv.frametime * speed * sv_friction.value;
292                 if (newspeed < 0)
293                         newspeed = 0;
294                 temp = newspeed/speed;
295                 VectorScale (velocity, temp, velocity);
296         }
297         else
298                 newspeed = 0;
299
300         // water acceleration
301         if (!wishspeed)
302                 return;
303
304         addspeed = wishspeed - newspeed;
305         if (addspeed <= 0)
306                 return;
307
308         VectorNormalize (wishvel);
309         accelspeed = sv_accelerate.value * wishspeed * sv.frametime;
310         if (accelspeed > addspeed)
311                 accelspeed = addspeed;
312
313         for (i=0 ; i<3 ; i++)
314                 velocity[i] += accelspeed * wishvel[i];
315 }
316
317 void SV_WaterJump (void)
318 {
319         if (sv.time > sv_player->v->teleport_time || !sv_player->v->waterlevel)
320         {
321                 sv_player->v->flags = (int)sv_player->v->flags & ~FL_WATERJUMP;
322                 sv_player->v->teleport_time = 0;
323         }
324         sv_player->v->velocity[0] = sv_player->v->movedir[0];
325         sv_player->v->velocity[1] = sv_player->v->movedir[1];
326 }
327
328
329 /*
330 ===================
331 SV_AirMove
332
333 ===================
334 */
335 void SV_AirMove (void)
336 {
337         int i;
338         vec3_t wishvel;
339         float fmove, smove, temp;
340
341         // LordHavoc: correct quake movement speed bug when looking up/down
342         wishvel[0] = wishvel[2] = 0;
343         wishvel[1] = sv_player->v->angles[1];
344         AngleVectors (wishvel, forward, right, up);
345
346         fmove = cmd.forwardmove;
347         smove = cmd.sidemove;
348
349 // hack to not let you back into teleporter
350         if (sv.time < sv_player->v->teleport_time && fmove < 0)
351                 fmove = 0;
352
353         for (i=0 ; i<3 ; i++)
354                 wishvel[i] = forward[i]*fmove + right[i]*smove;
355
356         if ((int)sv_player->v->movetype != MOVETYPE_WALK)
357                 wishvel[2] += cmd.upmove;
358
359         VectorCopy (wishvel, wishdir);
360         wishspeed = VectorNormalizeLength(wishdir);
361         if (wishspeed > sv_maxspeed.value)
362         {
363                 temp = sv_maxspeed.value/wishspeed;
364                 VectorScale (wishvel, temp, wishvel);
365                 wishspeed = sv_maxspeed.value;
366         }
367
368         if (sv_player->v->movetype == MOVETYPE_NOCLIP)
369         {
370                 // noclip
371                 VectorCopy (wishvel, velocity);
372         }
373         else if ( onground )
374         {
375                 SV_UserFriction ();
376                 SV_Accelerate ();
377         }
378         else
379         {
380                 // not on ground, so little effect on velocity
381                 SV_AirAccelerate (wishvel);
382         }
383 }
384
385 /*
386 ===================
387 SV_ClientThink
388
389 the move fields specify an intended velocity in pix/sec
390 the angle fields specify an exact angular motion in degrees
391 ===================
392 */
393 void SV_ClientThink (void)
394 {
395         vec3_t v_angle;
396
397         if (sv_player->v->movetype == MOVETYPE_NONE)
398                 return;
399
400         onground = (int)sv_player->v->flags & FL_ONGROUND;
401
402         origin = sv_player->v->origin;
403         velocity = sv_player->v->velocity;
404
405         DropPunchAngle ();
406
407         // if dead, behave differently
408         if (sv_player->v->health <= 0)
409                 return;
410
411         // angles
412         // show 1/3 the pitch angle and all the roll angle
413         cmd = host_client->cmd;
414         angles = sv_player->v->angles;
415
416         VectorAdd (sv_player->v->v_angle, sv_player->v->punchangle, v_angle);
417         angles[ROLL] = V_CalcRoll (sv_player->v->angles, sv_player->v->velocity)*4;
418         if (!sv_player->v->fixangle)
419         {
420                 // LordHavoc: pitch was ugly to begin with...  removed except in water
421                 if (sv_player->v->waterlevel >= 2)
422                         angles[PITCH] = -v_angle[PITCH]/3;
423                 else
424                         angles[PITCH] = 0;
425                 angles[YAW] = v_angle[YAW];
426         }
427
428         if ( (int)sv_player->v->flags & FL_WATERJUMP )
429         {
430                 SV_WaterJump ();
431                 return;
432         }
433
434         // Player is (somehow) outside of the map, or flying, or noclipping
435         if (SV_TestEntityPosition (sv_player)
436          || sv_player->v->movetype == MOVETYPE_FLY
437          || sv_player->v->movetype == MOVETYPE_NOCLIP)
438         {
439                 SV_FreeMove ();
440                 return;
441         }
442
443         // walk
444         if ((sv_player->v->waterlevel >= 2) && (sv_player->v->movetype != MOVETYPE_NOCLIP))
445         {
446                 SV_WaterMove ();
447                 return;
448         }
449
450         SV_AirMove ();
451 }
452
453
454 /*
455 ===================
456 SV_ReadClientMove
457 ===================
458 */
459 void SV_ReadClientMove (usercmd_t *move)
460 {
461         int i;
462         vec3_t angle;
463         int bits;
464         eval_t *val;
465         float total;
466
467         // read ping time
468         host_client->ping_times[host_client->num_pings % NUM_PING_TIMES] = sv.time - MSG_ReadFloat ();
469         host_client->num_pings++;
470         for (i=0, total = 0;i < NUM_PING_TIMES;i++)
471                 total += host_client->ping_times[i];
472         // can be used for prediction
473         host_client->ping = total / NUM_PING_TIMES;
474         if ((val = GETEDICTFIELDVALUE(sv_player, eval_ping)))
475                 val->_float = host_client->ping * 1000.0;
476
477         // read current angles
478         // dpprotocol version 2
479         for (i = 0;i < 3;i++)
480                 angle[i] = MSG_ReadFloat ();
481
482         VectorCopy (angle, sv_player->v->v_angle);
483
484         // read movement
485         move->forwardmove = MSG_ReadShort ();
486         move->sidemove = MSG_ReadShort ();
487         move->upmove = MSG_ReadShort ();
488         if ((val = GETEDICTFIELDVALUE(sv_player, eval_movement)))
489         {
490                 val->vector[0] = move->forwardmove;
491                 val->vector[1] = move->sidemove;
492                 val->vector[2] = move->upmove;
493         }
494
495         // read buttons
496         bits = MSG_ReadByte ();
497         sv_player->v->button0 = bits & 1;
498         sv_player->v->button2 = (bits & 2)>>1;
499         // LordHavoc: added 6 new buttons
500         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button3))) val->_float = ((bits >> 2) & 1);
501         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button4))) val->_float = ((bits >> 3) & 1);
502         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button5))) val->_float = ((bits >> 4) & 1);
503         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button6))) val->_float = ((bits >> 5) & 1);
504         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button7))) val->_float = ((bits >> 6) & 1);
505         if ((val = GETEDICTFIELDVALUE(sv_player, eval_button8))) val->_float = ((bits >> 7) & 1);
506
507         i = MSG_ReadByte ();
508         if (i)
509                 sv_player->v->impulse = i;
510 }
511
512 /*
513 ===================
514 SV_ReadClientMessage
515 ===================
516 */
517 extern void SV_SendServerinfo(client_t *client);
518 void SV_ReadClientMessage(void)
519 {
520         int cmd;
521         char *s;
522
523         //MSG_BeginReading ();
524
525         for(;;)
526         {
527                 if (!host_client->active)
528                 {
529                         // a command caused an error
530                         SV_DropClient (false);
531                         return;
532                 }
533
534                 if (msg_badread)
535                 {
536                         Con_Printf ("SV_ReadClientMessage: badread\n");
537                         SV_DropClient (false);
538                         return;
539                 }
540
541                 cmd = MSG_ReadChar ();
542                 if (cmd == -1)
543                 {
544                         // end of message
545                         break;
546                 }
547
548                 switch (cmd)
549                 {
550                 default:
551                         Con_Printf ("SV_ReadClientMessage: unknown command char %i\n", cmd);
552                         SV_DropClient (false);
553                         return;
554
555                 case clc_nop:
556                         break;
557
558                 case clc_stringcmd:
559                         s = MSG_ReadString ();
560                         if (strncasecmp(s, "spawn", 5) == 0
561                          || strncasecmp(s, "begin", 5) == 0
562                          || strncasecmp(s, "prespawn", 8) == 0)
563                                 Cmd_ExecuteString (s, src_client);
564                         else if (SV_ParseClientCommandQC)
565                         {
566                                 G_INT(OFS_PARM0) = PR_SetString(s);
567                                 pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
568                                 PR_ExecuteProgram ((func_t)(SV_ParseClientCommandQC - pr_functions), "");
569                         }
570                         else if (strncasecmp(s, "status", 6) == 0
571                          || strncasecmp(s, "name", 4) == 0
572                          || strncasecmp(s, "say", 3) == 0
573                          || strncasecmp(s, "say_team", 8) == 0
574                          || strncasecmp(s, "tell", 4) == 0
575                          || strncasecmp(s, "color", 5) == 0
576                          || strncasecmp(s, "kill", 4) == 0
577                          || strncasecmp(s, "pause", 5) == 0
578                          || strncasecmp(s, "kick", 4) == 0
579                          || strncasecmp(s, "ping", 4) == 0
580                          || strncasecmp(s, "ban", 3) == 0
581                          || strncasecmp(s, "pmodel", 6) == 0
582                          || (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))
583                          || (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)))
584                                 Cmd_ExecuteString (s, src_client);
585                         else
586                                 Con_Printf("%s tried to %s\n", host_client->name, s);
587                         break;
588
589                 case clc_disconnect:
590                         SV_DropClient (false); // client wants to disconnect
591                         return;
592
593                 case clc_move:
594                         SV_ReadClientMove (&host_client->cmd);
595                         break;
596
597                 case clc_ackentities:
598                         EntityFrame_AckFrame(&host_client->entitydatabase, MSG_ReadLong());
599                         break;
600                 }
601         }
602 }
603
604 /*
605 ==================
606 SV_RunClients
607 ==================
608 */
609 void SV_RunClients (void)
610 {
611         int i;
612
613         for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
614         {
615                 if (!host_client->active)
616                         continue;
617
618                 sv_player = host_client->edict;
619
620                 if (!host_client->spawned)
621                 {
622                         // clear client movement until a new packet is received
623                         memset (&host_client->cmd, 0, sizeof(host_client->cmd));
624                         continue;
625                 }
626
627                 if (sv.frametime)
628                 {
629                         // LordHavoc: QuakeC replacement for SV_ClientThink (player movement)
630                         if (SV_PlayerPhysicsQC)
631                         {
632                                 pr_global_struct->time = sv.time;
633                                 pr_global_struct->self = EDICT_TO_PROG(sv_player);
634                                 PR_ExecuteProgram ((func_t)(SV_PlayerPhysicsQC - pr_functions), "");
635                         }
636                         else
637                                 SV_ClientThink ();
638
639                         SV_CheckVelocity (sv_player);
640
641                         // LordHavoc: a hack to ensure that the (rather silly) id1 quakec
642                         // player_run/player_stand1 does not horribly malfunction if the
643                         // velocity becomes a number that is both == 0 and != 0
644                         // (sounds to me like NaN but to be absolutely safe...)
645                         if (DotProduct(sv_player->v->velocity, sv_player->v->velocity) < 0.0001)
646                                 VectorClear(sv_player->v->velocity);
647                 }
648         }
649 }
650