]> icculus.org git repositories - divverent/darkplaces.git/blob - csprogs.c
3961e6bf85f38f4cb072b47afd9a8cc079d45ff2
[divverent/darkplaces.git] / csprogs.c
1 #include "quakedef.h"
2 #include "progsvm.h"
3 #include "clprogdefs.h"
4 #include "csprogs.h"
5 #include "cl_collision.h"
6 #include "snd_main.h"
7 #include "clvm_cmds.h"
8
9 //============================================================================
10 // Client prog handling
11 //[515]: omg !!! optimize it ! a lot of hacks here and there also :P
12
13 #define CSQC_RETURNVAL  prog->globals.generic[OFS_RETURN]
14 #define CSQC_BEGIN              csqc_tmpprog=prog;prog=0;PRVM_SetProg(PRVM_CLIENTPROG);
15 #define CSQC_END                prog=csqc_tmpprog;
16
17 static prvm_prog_t *csqc_tmpprog;
18
19 //[515]: these are required funcs
20 static char *cl_required_func[] =
21 {
22         "CSQC_Init",
23         "CSQC_InputEvent",
24         "CSQC_UpdateView",
25         "CSQC_ConsoleCommand",
26 };
27
28 static int cl_numrequiredfunc = sizeof(cl_required_func) / sizeof(char*);
29
30 void CL_VM_Error (const char *format, ...) DP_FUNC_PRINTF(1);
31 void CL_VM_Error (const char *format, ...)      //[515]: hope it will be never executed =)
32 {
33         char errorstring[4096];
34         va_list argptr;
35
36         va_start (argptr, format);
37         dpvsnprintf (errorstring, sizeof(errorstring), format, argptr);
38         va_end (argptr);
39 //      Con_Printf( "CL_VM_Error: %s\n", errorstring );
40
41         PRVM_Crash();
42         cl.csqc_loaded = false;
43
44         Cvar_SetValueQuick(&csqc_progcrc, -1);
45         Cvar_SetValueQuick(&csqc_progsize, -1);
46
47 //      Host_AbortCurrentFrame();       //[515]: hmmm... if server says it needs csqc then client MUST disconnect
48         Host_Error("CL_VM_Error: %s", errorstring);
49 }
50 void CL_VM_UpdateDmgGlobals (int dmg_take, int dmg_save, vec3_t dmg_origin)
51 {
52         prvm_eval_t *val;
53         if(cl.csqc_loaded)
54         {
55                 CSQC_BEGIN
56                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_take);
57                 if(val)
58                         val->_float = dmg_take;
59                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_save);
60                 if(val)
61                         val->_float = dmg_save;
62                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_origin);
63                 if(val)
64                 {
65                         val->vector[0] = dmg_origin[0];
66                         val->vector[1] = dmg_origin[1];
67                         val->vector[2] = dmg_origin[2];
68                 }
69                 CSQC_END
70         }
71 }
72 //[515]: set globals before calling R_UpdateView, WEIRD CRAP
73 static void CSQC_SetGlobals (void)
74 {
75         prvm_eval_t *val;
76         CSQC_BEGIN
77                 prog->globals.client->time = cl.time;
78                 prog->globals.client->frametime = max(0, cl.time - cl.oldtime);
79                 prog->globals.client->servercommandframe = cls.servermovesequence;
80                 prog->globals.client->clientcommandframe = cl.movecmd[0].sequence;
81                 VectorCopy(cl.viewangles, prog->globals.client->input_angles);
82                 VectorCopy(cl.viewangles, cl.csqc_angles);
83                 // // FIXME: this actually belongs into getinputstate().. [12/17/2007 Black]
84                 prog->globals.client->input_buttons = cl.movecmd[0].buttons;
85                 VectorSet(prog->globals.client->input_movevalues, cl.movecmd[0].forwardmove, cl.movecmd[0].sidemove, cl.movecmd[0].upmove);
86                 //VectorCopy(cl.movement_origin, cl.csqc_origin);
87                 Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, cl.csqc_origin);
88
89                 // LordHavoc: Spike says not to do this, but without pmove_org the
90                 // CSQC is useless as it can't alter the view origin without
91                 // completely replacing it
92                 VectorCopy(cl.csqc_origin, prog->globals.client->pmove_org);
93                 VectorCopy(cl.velocity, prog->globals.client->pmove_vel);
94
95                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.view_angles)))
96                         VectorCopy(cl.viewangles, val->vector);
97                 prog->globals.client->maxclients = cl.maxclients;
98         CSQC_END
99 }
100
101 void CSQC_Predraw (prvm_edict_t *ed)
102 {
103         int b;
104         if(!ed->fields.client->predraw)
105                 return;
106         b = prog->globals.client->self;
107         prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
108         PRVM_ExecuteProgram(ed->fields.client->predraw, "CSQC_Predraw: NULL function\n");
109         prog->globals.client->self = b;
110 }
111
112 void CSQC_Think (prvm_edict_t *ed)
113 {
114         int b;
115         if(ed->fields.client->think)
116         if(ed->fields.client->nextthink && ed->fields.client->nextthink <= prog->globals.client->time)
117         {
118                 ed->fields.client->nextthink = 0;
119                 b = prog->globals.client->self;
120                 prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
121                 PRVM_ExecuteProgram(ed->fields.client->think, "CSQC_Think: NULL function\n");
122                 prog->globals.client->self = b;
123         }
124 }
125
126 extern cvar_t cl_noplayershadow;
127 qboolean CSQC_AddRenderEdict(prvm_edict_t *ed)
128 {
129         int renderflags;
130         int c;
131         float scale;
132         prvm_eval_t *val;
133         entity_render_t *entrender;
134         dp_model_t *model;
135         matrix4x4_t tagmatrix, matrix2;
136
137         model = CL_GetModelFromEdict(ed);
138         if (!model)
139                 return false;
140
141         entrender = CL_NewTempEntity();
142         if (!entrender)
143                 return false;
144
145         entrender->model = model;
146         entrender->skinnum = (int)ed->fields.client->skin;
147         entrender->effects |= entrender->model->effects;
148         scale = 1;
149         renderflags = 0;
150         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.renderflags)) && val->_float)     renderflags = (int)val->_float;
151         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.alpha)) && val->_float)           entrender->alpha = val->_float;
152         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.scale)) && val->_float)           entrender->scale = scale = val->_float;
153         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.colormod)) && VectorLength2(val->vector)) VectorCopy(val->vector, entrender->colormod);
154         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.effects)) && val->_float) entrender->effects |= (int)val->_float;
155         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.tag_entity)) && val->edict)
156         {
157                 int tagentity;
158                 int tagindex = 0;
159                 tagentity = val->edict;
160                 if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.tag_index)) && val->_float)
161                         tagindex = (int)val->_float;
162                 CL_GetTagMatrix (&tagmatrix, PRVM_PROG_TO_EDICT(tagentity), tagindex);
163         }
164         else
165                 Matrix4x4_CreateIdentity(&tagmatrix);
166
167         if (renderflags & RF_USEAXIS)
168         {
169                 vec3_t left;
170                 VectorNegate(prog->globals.client->v_right, left);
171                 Matrix4x4_FromVectors(&matrix2, prog->globals.client->v_forward, left, prog->globals.client->v_up, ed->fields.client->origin);
172                 Matrix4x4_Scale(&matrix2, scale, 1);
173         }
174         else
175         {
176                 vec3_t angles;
177                 VectorCopy(ed->fields.client->angles, angles);
178                 // if model is alias, reverse pitch direction
179                 if (entrender->model->type == mod_alias)
180                         angles[0] = -angles[0];
181
182                 // set up the render matrix
183                 Matrix4x4_CreateFromQuakeEntity(&matrix2, ed->fields.client->origin[0], ed->fields.client->origin[1], ed->fields.client->origin[2], angles[0], angles[1], angles[2], scale);
184         }
185
186         // set up the animation data
187         // self.frame is the interpolation target (new frame)
188         // self.frame1time is the animation base time for the interpolation target
189         // self.frame2 is the interpolation start (previous frame)
190         // self.frame2time is the animation base time for the interpolation start
191         entrender->frame1 = entrender->frame2 = ed->fields.client->frame;
192         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2))) entrender->frame2 = val->_float;
193         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame1time))) entrender->frame2time = val->_float;
194         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2time))) entrender->frame1time = val->_float;
195         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac))) entrender->framelerp = val->_float;
196
197         // concat the matrices to make the entity relative to its tag
198         Matrix4x4_Concat(&entrender->matrix, &tagmatrix, &matrix2);
199
200         if(renderflags)
201         {
202                 if(renderflags & RF_VIEWMODEL)  entrender->flags |= RENDER_VIEWMODEL;
203                 if(renderflags & RF_EXTERNALMODEL)entrender->flags |= RENDER_EXTERIORMODEL;
204                 if(renderflags & RF_DEPTHHACK)  entrender->effects |= EF_NODEPTHTEST;
205                 if(renderflags & RF_ADDITIVE)           entrender->effects |= EF_ADDITIVE;
206         }
207
208         c = (int)ed->fields.client->colormap;
209         if (c <= 0)
210                 CL_SetEntityColormapColors(entrender, -1);
211         else if (c <= cl.maxclients && cl.scores != NULL)
212                 CL_SetEntityColormapColors(entrender, cl.scores[c-1].colors);
213         else
214                 CL_SetEntityColormapColors(entrender, c);
215
216         entrender->flags &= ~(RENDER_SHADOW | RENDER_LIGHT | RENDER_NOSELFSHADOW);
217         // either fullbright or lit
218         if (!(entrender->effects & EF_FULLBRIGHT) && !r_fullbright.integer)
219                 entrender->flags |= RENDER_LIGHT;
220         // hide player shadow during intermission or nehahra movie
221         if (!(entrender->effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST))
222          &&  (entrender->alpha >= 1)
223          && !(entrender->flags & RENDER_VIEWMODEL)
224          && (!(entrender->flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cls.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer)))
225                 entrender->flags |= RENDER_SHADOW;
226         if (entrender->flags & RENDER_VIEWMODEL)
227                 entrender->flags |= RENDER_NOSELFSHADOW;
228         if (entrender->effects & EF_NOSELFSHADOW)
229                 entrender->flags |= RENDER_NOSELFSHADOW;
230
231         // make the other useful stuff
232         CL_UpdateRenderEntity(entrender);
233
234         return true;
235 }
236
237 qboolean CL_VM_InputEvent (qboolean down, int key, int ascii)
238 {
239         qboolean r;
240
241         if(!cl.csqc_loaded)
242                 return false;
243
244         CSQC_BEGIN
245                 if (!prog->funcoffsets.CSQC_InputEvent)
246                         r = false;
247                 else
248                 {
249                         prog->globals.client->time = cl.time;
250                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
251                         PRVM_G_FLOAT(OFS_PARM0) = !down; // 0 is down, 1 is up
252                         PRVM_G_FLOAT(OFS_PARM1) = key;
253                         PRVM_G_FLOAT(OFS_PARM2) = ascii;
254                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_InputEvent, "QC function CSQC_InputEvent is missing");
255                         r = CSQC_RETURNVAL;
256                 }
257         CSQC_END
258         return r;
259 }
260
261 qboolean CL_VM_UpdateView (void)
262 {
263         vec3_t emptyvector;
264         emptyvector[0] = 0;
265         emptyvector[1] = 0;
266         emptyvector[2] = 0;
267 //      vec3_t oldangles;
268         if(!cl.csqc_loaded)
269                 return false;
270         CSQC_BEGIN
271                 //VectorCopy(cl.viewangles, oldangles);
272                 prog->globals.client->time = cl.time;
273                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
274                 CSQC_SetGlobals();
275                 // clear renderable entity and light lists to prevent crashes if the
276                 // CSQC_UpdateView function does not call R_ClearScene as it should
277                 r_refdef.scene.numentities = 0;
278                 r_refdef.scene.numlights = 0;
279                 // pass in width and height as parameters (EXT_CSQC_1)
280                 PRVM_G_FLOAT(OFS_PARM0) = vid.width;
281                 PRVM_G_FLOAT(OFS_PARM1) = vid.height;
282                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_UpdateView, "QC function CSQC_UpdateView is missing");
283                 //VectorCopy(oldangles, cl.viewangles);
284                 // Dresk : Reset Dmg Globals Here
285                 CL_VM_UpdateDmgGlobals(0, 0, emptyvector);
286         CSQC_END
287         return true;
288 }
289
290 extern sizebuf_t vm_tempstringsbuf;
291 qboolean CL_VM_ConsoleCommand (const char *cmd)
292 {
293         int restorevm_tempstringsbuf_cursize;
294         qboolean r = false;
295         if(!cl.csqc_loaded)
296                 return false;
297         CSQC_BEGIN
298         if (prog->funcoffsets.CSQC_ConsoleCommand)
299         {
300                 prog->globals.client->time = cl.time;
301                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
302                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
303                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(cmd);
304                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_ConsoleCommand, "QC function CSQC_ConsoleCommand is missing");
305                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
306                 r = CSQC_RETURNVAL;
307         }
308         CSQC_END
309         return r;
310 }
311
312 qboolean CL_VM_Parse_TempEntity (void)
313 {
314         int                     t;
315         qboolean        r = false;
316         if(!cl.csqc_loaded)
317                 return false;
318         CSQC_BEGIN
319         if(prog->funcoffsets.CSQC_Parse_TempEntity)
320         {
321                 t = msg_readcount;
322                 prog->globals.client->time = cl.time;
323                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
324                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_TempEntity, "QC function CSQC_Parse_TempEntity is missing");
325                 r = CSQC_RETURNVAL;
326                 if(!r)
327                 {
328                         msg_readcount = t;
329                         msg_badread = false;
330                 }
331         }
332         CSQC_END
333         return r;
334 }
335
336 void CL_VM_Parse_StuffCmd (const char *msg)
337 {
338         int restorevm_tempstringsbuf_cursize;
339         if(msg[0] == 'c')
340         if(msg[1] == 's')
341         if(msg[2] == 'q')
342         if(msg[3] == 'c')
343         {
344                 // if this is setting a csqc variable, deprotect csqc_progcrc
345                 // temporarily so that it can be set by the cvar command,
346                 // and then reprotect it afterwards
347                 int crcflags = csqc_progcrc.flags;
348                 int sizeflags = csqc_progcrc.flags;
349                 csqc_progcrc.flags &= ~CVAR_READONLY;
350                 csqc_progsize.flags &= ~CVAR_READONLY;
351                 Cmd_ExecuteString (msg, src_command);
352                 csqc_progcrc.flags = crcflags;
353                 csqc_progsize.flags = sizeflags;
354                 return;
355         }
356
357         if(cls.demoplayback)
358         if(!strncmp(msg, "curl --clear_autodownload\ncurl --pak --forthismap --as ", 55))
359         {
360                 // special handling for map download commands
361                 // run these commands IMMEDIATELY, instead of waiting for a client frame
362                 // that way, there is no black screen when playing back demos
363                 // I know this is a really ugly hack, but I can't think of any better way
364                 // FIXME find the actual CAUSE of this, and make demo playback WAIT
365                 // until all maps are loaded, then remove this hack
366
367                 char buf[MAX_INPUTLINE];
368                 const char *p, *q;
369                 size_t l;
370
371                 p = msg;
372
373                 for(;;)
374                 {
375                         q = strchr(p, '\n');
376                         if(q)
377                                 l = q - p;
378                         else
379                                 l = strlen(p);
380                         if(l > sizeof(buf) - 1)
381                                 l = sizeof(buf) - 1;
382                         strlcpy(buf, p, l + 1); // strlcpy needs a + 1 as it includes the newline!
383
384                         Cmd_ExecuteString(buf, src_command);
385
386                         p += l;
387                         if(*p == '\n')
388                                 ++p; // skip the newline and continue
389                         else
390                                 break; // end of string or overflow
391                 }
392                 Cmd_ExecuteString("curl --clear_autodownload", src_command); // don't inhibit CSQC loading
393                 return;
394         }
395
396         if(!cl.csqc_loaded)
397         {
398                 Cbuf_AddText(msg);
399                 return;
400         }
401         CSQC_BEGIN
402         if(prog->funcoffsets.CSQC_Parse_StuffCmd)
403         {
404                 prog->globals.client->time = cl.time;
405                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
406                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
407                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
408                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_StuffCmd, "QC function CSQC_Parse_StuffCmd is missing");
409                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
410         }
411         else
412                 Cbuf_AddText(msg);
413         CSQC_END
414 }
415
416 static void CL_VM_Parse_Print (const char *msg)
417 {
418         int restorevm_tempstringsbuf_cursize;
419         prog->globals.client->time = cl.time;
420         prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
421         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
422         PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
423         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_Print, "QC function CSQC_Parse_Print is missing");
424         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
425 }
426
427 void CSQC_AddPrintText (const char *msg)
428 {
429         size_t i;
430         if(!cl.csqc_loaded)
431         {
432                 Con_Print(msg);
433                 return;
434         }
435         CSQC_BEGIN
436         if(prog->funcoffsets.CSQC_Parse_Print)
437         {
438                 // FIXME: is this bugged?
439                 i = strlen(msg)-1;
440                 if(msg[i] != '\n' && msg[i] != '\r')
441                 {
442                         if(strlen(cl.csqc_printtextbuf)+i >= MAX_INPUTLINE)
443                         {
444                                 CL_VM_Parse_Print(cl.csqc_printtextbuf);
445                                 cl.csqc_printtextbuf[0] = 0;
446                         }
447                         else
448                                 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
449                         return;
450                 }
451                 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
452                 CL_VM_Parse_Print(cl.csqc_printtextbuf);
453                 cl.csqc_printtextbuf[0] = 0;
454         }
455         else
456                 Con_Print(msg);
457         CSQC_END
458 }
459
460 void CL_VM_Parse_CenterPrint (const char *msg)
461 {
462         int restorevm_tempstringsbuf_cursize;
463         if(!cl.csqc_loaded)
464         {
465                 SCR_CenterPrint(msg);
466                 return;
467         }
468         CSQC_BEGIN
469         if(prog->funcoffsets.CSQC_Parse_CenterPrint)
470         {
471                 prog->globals.client->time = cl.time;
472                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
473                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
474                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
475                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_CenterPrint, "QC function CSQC_Parse_CenterPrint is missing");
476                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
477         }
478         else
479                 SCR_CenterPrint(msg);
480         CSQC_END
481 }
482
483 void CL_VM_UpdateIntermissionState (int intermission)
484 {
485         prvm_eval_t *val;
486         if(cl.csqc_loaded)
487         {
488                 CSQC_BEGIN
489                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.intermission);
490                 if(val)
491                         val->_float = intermission;
492                 CSQC_END
493         }
494 }
495 void CL_VM_UpdateShowingScoresState (int showingscores)
496 {
497         prvm_eval_t *val;
498         if(cl.csqc_loaded)
499         {
500                 CSQC_BEGIN
501                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.sb_showscores);
502                 if(val)
503                         val->_float = showingscores;
504                 CSQC_END
505         }
506 }
507 qboolean CL_VM_Event_Sound(int sound_num, int volume, int channel, float attenuation, int ent, vec3_t pos)
508 {
509         qboolean r = false;
510         if(cl.csqc_loaded)
511         {
512                 CSQC_BEGIN
513                 if(prog->funcoffsets.CSQC_Event_Sound)
514                 {
515                         prog->globals.client->time = cl.time;
516                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
517                         PRVM_G_FLOAT(OFS_PARM0) = ent;
518                         PRVM_G_FLOAT(OFS_PARM1) = channel;
519                         PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(cl.sound_name[sound_num] );
520                         PRVM_G_FLOAT(OFS_PARM3) = volume;
521                         PRVM_G_FLOAT(OFS_PARM4) = attenuation;
522                         VectorCopy(pos, PRVM_G_VECTOR(OFS_PARM5) );
523                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event_Sound, "QC function CSQC_Event_Sound is missing");
524                         r = CSQC_RETURNVAL;
525                 }
526                 CSQC_END
527         }
528
529         return r;
530 }
531 void CL_VM_UpdateCoopDeathmatchGlobals (int gametype)
532 {
533         // Avoid global names for clean(er) coding
534         int localcoop;
535         int localdeathmatch;
536
537         prvm_eval_t *val;
538         if(cl.csqc_loaded)
539         {
540                 if(gametype == GAME_COOP)
541                 {
542                         localcoop = 1;
543                         localdeathmatch = 0;
544                 }
545                 else
546                 if(gametype == GAME_DEATHMATCH)
547                 {
548                         localcoop = 0;
549                         localdeathmatch = 1;
550                 }
551                 else
552                 {
553                         // How did the ServerInfo send an unknown gametype?
554                         // Better just assign the globals as 0...
555                         localcoop = 0;
556                         localdeathmatch = 0;
557                 }
558                 CSQC_BEGIN
559                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.coop);
560                 if(val)
561                         val->_float = localcoop;
562                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.deathmatch);
563                 if(val)
564                         val->_float = localdeathmatch;
565                 CSQC_END
566         }
567 }
568 float CL_VM_Event (float event)         //[515]: needed ? I'd say "YES", but don't know for what :D
569 {
570         float r = 0;
571         if(!cl.csqc_loaded)
572                 return 0;
573         CSQC_BEGIN
574         if(prog->funcoffsets.CSQC_Event)
575         {
576                 prog->globals.client->time = cl.time;
577                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
578                 PRVM_G_FLOAT(OFS_PARM0) = event;
579                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event, "QC function CSQC_Event is missing");
580                 r = CSQC_RETURNVAL;
581         }
582         CSQC_END
583         return r;
584 }
585
586 void CSQC_ReadEntities (void)
587 {
588         unsigned short entnum, oldself, realentnum;
589         if(!cl.csqc_loaded)
590         {
591                 Host_Error ("CSQC_ReadEntities: CSQC is not loaded");
592                 return;
593         }
594
595         CSQC_BEGIN
596                 prog->globals.client->time = cl.time;
597                 oldself = prog->globals.client->self;
598                 while(1)
599                 {
600                         entnum = MSG_ReadShort();
601                         if(!entnum || msg_badread)
602                                 return;
603                         realentnum = entnum & 0x7FFF;
604                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum];
605                         if(entnum & 0x8000)
606                         {
607                                 if(prog->globals.client->self)
608                                 {
609                                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Remove, "QC function CSQC_Ent_Remove is missing");
610                                         cl.csqc_server2csqcentitynumber[realentnum] = 0;
611                                 }
612                                 else
613                                 {
614                                         // LordHavoc: removing an entity that is already gone on
615                                         // the csqc side is possible for legitimate reasons (such
616                                         // as a repeat of the remove message), so no warning is
617                                         // needed
618                                         //Con_Printf("Bad csqc_server2csqcentitynumber map\n"); //[515]: never happens ?
619                                 }
620                         }
621                         else
622                         {
623                                 if(!prog->globals.client->self)
624                                 {
625                                         if(!prog->funcoffsets.CSQC_Ent_Spawn)
626                                         {
627                                                 prvm_edict_t    *ed;
628                                                 ed = PRVM_ED_Alloc();
629                                                 ed->fields.client->entnum = realentnum;
630                                                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT_TO_PROG(ed);
631                                         }
632                                         else
633                                         {
634                                                 // entity( float entnum ) CSQC_Ent_Spawn;
635                                                 // the qc function should set entnum, too (this way it also can return world [2/1/2008 Andreas]
636                                                 PRVM_G_FLOAT(OFS_PARM0) = (float) realentnum;
637                                                 // make sure no one gets wrong ideas
638                                                 prog->globals.client->self = 0;
639                                                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Spawn, "QC function CSQC_Ent_Spawn is missing");
640                                                 prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT( PRVM_G_INT( OFS_RETURN ) );
641                                         }
642                                         PRVM_G_FLOAT(OFS_PARM0) = 1;
643                                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Update, "QC function CSQC_Ent_Update is missing");
644                                 }
645                                 else {
646                                         PRVM_G_FLOAT(OFS_PARM0) = 0;
647                                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Update, "QC function CSQC_Ent_Update is missing");
648                                 }
649                         }
650                 }
651                 prog->globals.client->self = oldself;
652         CSQC_END
653 }
654
655 void CL_VM_CB_BeginIncreaseEdicts(void)
656 {
657         // links don't survive the transition, so unlink everything
658         World_UnlinkAll(&cl.world);
659 }
660
661 void CL_VM_CB_EndIncreaseEdicts(void)
662 {
663         int i;
664         prvm_edict_t *ent;
665
666         // link every entity except world
667         for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
668                 if (!ent->priv.server->free)
669                         CL_LinkEdict(ent);
670 }
671
672 void CL_VM_CB_InitEdict(prvm_edict_t *e)
673 {
674         e->priv.server->move = false; // don't move on first frame
675 }
676
677 void CL_VM_CB_FreeEdict(prvm_edict_t *ed)
678 {
679         World_UnlinkEdict(ed);
680         memset(ed->fields.client, 0, sizeof(*ed->fields.client));
681 }
682
683 void CL_VM_CB_CountEdicts(void)
684 {
685         int             i;
686         prvm_edict_t    *ent;
687         int             active = 0, models = 0, solid = 0;
688
689         for (i=0 ; i<prog->num_edicts ; i++)
690         {
691                 ent = PRVM_EDICT_NUM(i);
692                 if (ent->priv.server->free)
693                         continue;
694                 active++;
695                 if (ent->fields.client->solid)
696                         solid++;
697                 if (ent->fields.client->model)
698                         models++;
699         }
700
701         Con_Printf("num_edicts:%3i\n", prog->num_edicts);
702         Con_Printf("active    :%3i\n", active);
703         Con_Printf("view      :%3i\n", models);
704         Con_Printf("touch     :%3i\n", solid);
705 }
706
707 qboolean CL_VM_CB_LoadEdict(prvm_edict_t *ent)
708 {
709         return true;
710 }
711
712 void Cmd_ClearCsqcFuncs (void);
713
714 // returns true if the packet is valid, false if end of file is reached
715 // used for dumping the CSQC download into demo files
716 qboolean MakeDownloadPacket(const char *filename, unsigned char *data, unsigned long len, int crc, int cnt, sizebuf_t *buf, int protocol)
717 {
718         int packetsize = buf->maxsize - 7; // byte short long
719         int npackets = (len + packetsize - 1) / (packetsize);
720
721         if(protocol == PROTOCOL_QUAKEWORLD)
722                 return false; // CSQC can't run in QW anyway
723
724         SZ_Clear(buf);
725         if(cnt == 0)
726         {
727                 MSG_WriteByte(buf, svc_stufftext);
728                 MSG_WriteString(buf, va("\ncl_downloadbegin %lu %s\n", len, filename));
729                 return true;
730         }
731         else if(cnt >= 1 && cnt <= npackets)
732         {
733                 unsigned long thispacketoffset = (cnt - 1) * packetsize;
734                 int thispacketsize = len - thispacketoffset;
735                 if(thispacketsize > packetsize)
736                         thispacketsize = packetsize;
737
738                 MSG_WriteByte(buf, svc_downloaddata);
739                 MSG_WriteLong(buf, thispacketoffset);
740                 MSG_WriteShort(buf, thispacketsize);
741                 SZ_Write(buf, data + thispacketoffset, thispacketsize);
742
743                 return true;
744         }
745         else if(cnt == npackets + 1)
746         {
747                 MSG_WriteByte(buf, svc_stufftext);
748                 MSG_WriteString(buf, va("\ncl_downloadfinished %lu %d\n", len, crc));
749                 return true;
750         }
751         return false;
752 }
753
754 void CL_VM_Init (void)
755 {
756         const char* csprogsfn;
757         unsigned char *csprogsdata;
758         fs_offset_t csprogsdatasize;
759         int csprogsdatacrc, requiredcrc;
760         int requiredsize;
761         prvm_eval_t *val;
762
763         // reset csqc_progcrc after reading it, so that changing servers doesn't
764         // expect csqc on the next server
765         requiredcrc = csqc_progcrc.integer;
766         requiredsize = csqc_progsize.integer;
767         Cvar_SetValueQuick(&csqc_progcrc, -1);
768         Cvar_SetValueQuick(&csqc_progsize, -1);
769
770         // if the server is not requesting a csprogs, then we're done here
771         if (requiredcrc < 0)
772                 return;
773
774         // see if the requested csprogs.dat file matches the requested crc
775         csprogsdatacrc = -1;
776         csprogsfn = va("dlcache/%s.%i.%i", csqc_progname.string, requiredsize, requiredcrc);
777         csprogsdata = FS_LoadFile(csprogsfn, tempmempool, true, &csprogsdatasize);
778         if (!csprogsdata)
779         {
780                 csprogsfn = csqc_progname.string;
781                 csprogsdata = FS_LoadFile(csprogsfn, tempmempool, true, &csprogsdatasize);
782         }
783         if (csprogsdata)
784         {
785                 csprogsdatacrc = CRC_Block(csprogsdata, csprogsdatasize);
786                 if (csprogsdatacrc != requiredcrc || csprogsdatasize != requiredsize)
787                 {
788                         if (cls.demoplayback)
789                         {
790                                 Con_Printf("^1Warning: Your %s is not the same version as the demo was recorded with (CRC/size are %i/%i but should be %i/%i)\n", csqc_progname.string, csprogsdatacrc, (int)csprogsdatasize, requiredcrc, requiredsize);
791                                 // Mem_Free(csprogsdata);
792                                 // return;
793                                 // We WANT to continue here, and play the demo with different csprogs!
794                                 // After all, this is just a warning. Sure things may go wrong from here.
795                         }
796                         else
797                         {
798                                 Mem_Free(csprogsdata);
799                                 Con_Printf("^1Your %s is not the same version as the server (CRC is %i/%i but should be %i/%i)\n", csqc_progname.string, csprogsdatacrc, (int)csprogsdatasize, requiredcrc, requiredsize);
800                                 CL_Disconnect();
801                                 return;
802                         }
803                 }
804         }
805         else
806         {
807                 if (requiredcrc >= 0)
808                 {
809                         if (cls.demoplayback)
810                                 Con_Printf("CL_VM_Init: demo requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
811                         else
812                                 Con_Printf("CL_VM_Init: server requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
813                         CL_Disconnect();
814                 }
815                 return;
816         }
817
818         PRVM_Begin;
819         PRVM_InitProg(PRVM_CLIENTPROG);
820
821         // allocate the mempools
822         prog->progs_mempool = Mem_AllocPool(csqc_progname.string, 0, NULL);
823         prog->headercrc = CL_PROGHEADER_CRC;
824         prog->edictprivate_size = 0; // no private struct used
825         prog->name = CL_NAME;
826         prog->num_edicts = 1;
827         prog->max_edicts = 512;
828         prog->limit_edicts = CL_MAX_EDICTS;
829         prog->reserved_edicts = 0;
830         prog->edictprivate_size = sizeof(edict_engineprivate_t);
831         // TODO: add a shared extension string #define and add real support for csqc extension strings [12/5/2007 Black]
832         prog->extensionstring = vm_sv_extensions;
833         prog->builtins = vm_cl_builtins;
834         prog->numbuiltins = vm_cl_numbuiltins;
835         prog->begin_increase_edicts = CL_VM_CB_BeginIncreaseEdicts;
836         prog->end_increase_edicts = CL_VM_CB_EndIncreaseEdicts;
837         prog->init_edict = CL_VM_CB_InitEdict;
838         prog->free_edict = CL_VM_CB_FreeEdict;
839         prog->count_edicts = CL_VM_CB_CountEdicts;
840         prog->load_edict = CL_VM_CB_LoadEdict;
841         prog->init_cmd = VM_CL_Cmd_Init;
842         prog->reset_cmd = VM_CL_Cmd_Reset;
843         prog->error_cmd = CL_VM_Error;
844
845         PRVM_LoadProgs(csprogsfn, cl_numrequiredfunc, cl_required_func, 0, NULL, 0, NULL);
846
847         if (!prog->loaded)
848         {
849                 CL_VM_Error("CSQC %s ^2failed to load\n", csprogsfn);
850                 if(!sv.active)
851                         CL_Disconnect();
852                 Mem_Free(csprogsdata);
853                 return;
854         }
855
856         Con_Printf("CSQC %s ^5loaded (crc=%i, size=%i)\n", csprogsfn, csprogsdatacrc, (int)csprogsdatasize);
857
858         if(cls.demorecording)
859         {
860                 if(cls.demo_lastcsprogssize != csprogsdatasize || cls.demo_lastcsprogscrc != csprogsdatacrc)
861                 {
862                         int i;
863                         char buf[NET_MAXMESSAGE];
864                         sizebuf_t sb;
865                         unsigned char *demobuf; fs_offset_t demofilesize;
866
867                         sb.data = (void *) buf;
868                         sb.maxsize = sizeof(buf);
869                         i = 0;
870
871                         CL_CutDemo(&demobuf, &demofilesize);
872                         while(MakeDownloadPacket(csqc_progname.string, csprogsdata, csprogsdatasize, csprogsdatacrc, i++, &sb, cls.protocol))
873                                 CL_WriteDemoMessage(&sb);
874                         CL_PasteDemo(&demobuf, &demofilesize);
875
876                         cls.demo_lastcsprogssize = csprogsdatasize;
877                         cls.demo_lastcsprogscrc = csprogsdatacrc;
878                 }
879         }
880         Mem_Free(csprogsdata);
881
882         // check if OP_STATE animation is possible in this dat file
883         if (prog->fieldoffsets.nextthink >= 0 && prog->fieldoffsets.frame >= 0 && prog->fieldoffsets.think >= 0 && prog->globaloffsets.self >= 0)
884                 prog->flag |= PRVM_OP_STATE;
885
886         // set time
887         prog->globals.client->time = cl.time;
888         prog->globals.client->self = 0;
889
890         prog->globals.client->mapname = cl.worldmodel ? PRVM_SetEngineString(cl.worldmodel->name) : PRVM_SetEngineString("");
891         prog->globals.client->player_localentnum = cl.playerentity;
892
893         // set map description (use world entity 0)
894         val = PRVM_EDICTFIELDVALUE(prog->edicts, prog->fieldoffsets.message);
895         if(val)
896                 val->string = PRVM_SetEngineString(cl.levelname);
897         VectorCopy(cl.world.mins, prog->edicts->fields.client->mins);
898         VectorCopy(cl.world.maxs, prog->edicts->fields.client->maxs);
899
900         // call the prog init
901         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Init, "QC function CSQC_Init is missing");
902
903         PRVM_End;
904         cl.csqc_loaded = true;
905
906         cl.csqc_vidvars.drawcrosshair = false;
907         cl.csqc_vidvars.drawenginesbar = false;
908
909         // Update Coop and Deathmatch Globals (at this point the client knows them from ServerInfo)
910         CL_VM_UpdateCoopDeathmatchGlobals(cl.gametype);
911 }
912
913 void CL_VM_ShutDown (void)
914 {
915         Cmd_ClearCsqcFuncs();
916         //Cvar_SetValueQuick(&csqc_progcrc, -1);
917         //Cvar_SetValueQuick(&csqc_progsize, -1);
918         if(!cl.csqc_loaded)
919                 return;
920         CSQC_BEGIN
921                 prog->globals.client->time = cl.time;
922                 prog->globals.client->self = 0;
923                 if (prog->funcoffsets.CSQC_Shutdown)
924                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Shutdown, "QC function CSQC_Shutdown is missing");
925                 PRVM_ResetProg();
926         CSQC_END
927         Con_Print("CSQC ^1unloaded\n");
928         cl.csqc_loaded = false;
929 }
930
931 qboolean CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out)
932 {
933         prvm_edict_t *ed;
934         dp_model_t *mod;
935         qboolean r = 0;
936
937         CSQC_BEGIN;
938
939         ed = PRVM_EDICT_NUM(entnum - 32768);
940
941         if(!ed->priv.required->free)
942         {
943                 mod = CL_GetModelFromEdict(ed);
944                 VectorCopy(ed->fields.client->origin, out);
945                 if (mod && mod->soundfromcenter)
946                         VectorMAMAM(1.0f, out, 0.5f, mod->normalmins, 0.5f, mod->normalmaxs, out);
947                 r = 1;
948         }
949
950         CSQC_END;
951
952         return r;
953 }