]> git.xonotic.org Git - xonotic/darkplaces.git/blob - csprogs.c
set pmove_org and pmove_vel again in csqc because it's useless without
[xonotic/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
8 //============================================================================
9 // Client prog handling
10 //[515]: omg !!! optimize it ! a lot of hacks here and there also :P
11
12 #define CSQC_RETURNVAL  prog->globals.generic[OFS_RETURN]
13 #define CSQC_BEGIN              csqc_tmpprog=prog;prog=0;PRVM_SetProg(PRVM_CLIENTPROG);
14 #define CSQC_END                prog=csqc_tmpprog;
15 static prvm_prog_t *csqc_tmpprog;
16
17 //[515]: these are required funcs
18 static char *cl_required_func[] =
19 {
20         "CSQC_Init",
21         "CSQC_InputEvent",
22         "CSQC_UpdateView",
23         "CSQC_ConsoleCommand",
24 };
25
26 static int cl_numrequiredfunc = sizeof(cl_required_func) / sizeof(char*);
27
28 void CL_VM_Error (const char *format, ...) DP_FUNC_PRINTF(1);
29 void CL_VM_Error (const char *format, ...)      //[515]: hope it will be never executed =)
30 {
31         char errorstring[4096];
32         va_list argptr;
33
34         va_start (argptr, format);
35         dpvsnprintf (errorstring, sizeof(errorstring), format, argptr);
36         va_end (argptr);
37 //      Con_Printf( "CL_VM_Error: %s\n", errorstring );
38
39         PRVM_Crash();
40         cl.csqc_loaded = false;
41
42         Cvar_SetValueQuick(&csqc_progcrc, -1);
43         Cvar_SetValueQuick(&csqc_progsize, -1);
44
45 //      Host_AbortCurrentFrame();       //[515]: hmmm... if server says it needs csqc then client MUST disconnect
46         Host_Error(va("CL_VM_Error: %s", errorstring));
47 }
48 void CL_VM_UpdateDmgGlobals (int dmg_take, int dmg_save, vec3_t dmg_origin)
49 {
50         prvm_eval_t *val;
51         if(cl.csqc_loaded)
52         {
53                 CSQC_BEGIN
54                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_take);
55                 if(val)
56                         val->_float = dmg_take;
57                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_save);
58                 if(val)
59                         val->_float = dmg_save;
60                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.dmg_origin);
61                 if(val)
62                 {
63                         val->vector[0] = dmg_origin[0];
64                         val->vector[1] = dmg_origin[1];
65                         val->vector[2] = dmg_origin[2];
66                 }
67                 CSQC_END
68         }
69 }
70 //[515]: set globals before calling R_UpdateView, WEIRD CRAP
71 static void CSQC_SetGlobals (void)
72 {
73         prvm_eval_t *val;
74         CSQC_BEGIN
75                 prog->globals.client->time = cl.time;
76                 prog->globals.client->frametime = max(0, cl.time - cl.oldtime);
77                 prog->globals.client->servercommandframe = cls.servermovesequence;
78                 prog->globals.client->clientcommandframe = cl.movecmd[0].sequence;
79                 VectorCopy(cl.viewangles, prog->globals.client->input_angles);
80                 VectorCopy(cl.viewangles, cl.csqc_angles);
81                 prog->globals.client->input_buttons = cl.movecmd[0].buttons;
82                 VectorSet(prog->globals.client->input_movevalues, cl.movecmd[0].forwardmove, cl.movecmd[0].sidemove, cl.movecmd[0].upmove);
83                 //VectorCopy(cl.movement_origin, cl.csqc_origin);
84                 Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, cl.csqc_origin);
85
86                 // LordHavoc: Spike says not to do this, but without pmove_org the
87                 // CSQC is useless as it can't alter the view origin without
88                 // completely replacing it
89                 VectorCopy(cl.csqc_origin, prog->globals.client->pmove_org);
90                 VectorCopy(cl.velocity, prog->globals.client->pmove_vel);
91
92                 if ((val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.view_angles)))
93                         VectorCopy(cl.viewangles, val->vector);
94                 prog->globals.client->maxclients = cl.maxclients;
95         CSQC_END
96 }
97
98 void CSQC_Predraw (prvm_edict_t *ed)
99 {
100         int b;
101         if(!ed->fields.client->predraw)
102                 return;
103         b = prog->globals.client->self;
104         prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
105         PRVM_ExecuteProgram(ed->fields.client->predraw, "CSQC_Predraw: NULL function\n");
106         prog->globals.client->self = b;
107 }
108
109 void CSQC_Think (prvm_edict_t *ed)
110 {
111         int b;
112         if(ed->fields.client->think)
113         if(ed->fields.client->nextthink && ed->fields.client->nextthink <= prog->globals.client->time)
114         {
115                 ed->fields.client->nextthink = 0;
116                 b = prog->globals.client->self;
117                 prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
118                 PRVM_ExecuteProgram(ed->fields.client->think, "CSQC_Think: NULL function\n");
119                 prog->globals.client->self = b;
120         }
121 }
122
123 extern cvar_t cl_noplayershadow;
124 qboolean CSQC_AddRenderEdict(prvm_edict_t *ed)
125 {
126         int renderflags;
127         float scale;
128         prvm_eval_t *val;
129         entity_t *e;
130         model_t *model;
131         matrix4x4_t tagmatrix, matrix2;
132
133         model = CL_GetModelFromEdict(ed);
134         if (!model)
135                 return false;
136
137         e = CL_NewTempEntity();
138         if (!e)
139                 return false;
140
141         e->render.model = model;
142         e->render.colormap = (int)ed->fields.client->colormap;
143         e->render.skinnum = (int)ed->fields.client->skin;
144         e->render.effects |= e->render.model->effects;
145         scale = 1;
146         renderflags = 0;
147         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.renderflags)) && val->_float)     renderflags = (int)val->_float;
148         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.alpha)) && val->_float)           e->render.alpha = val->_float;
149         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.scale)) && val->_float)           e->render.scale = scale = val->_float;
150         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.colormod)) && VectorLength2(val->vector)) VectorCopy(val->vector, e->render.colormod);
151         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.effects)) && val->_float) e->render.effects |= (int)val->_float;
152         if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.tag_entity)) && val->edict)
153         {
154                 int tagentity;
155                 int tagindex = 0;
156                 tagentity = val->edict;
157                 if((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.tag_index)) && val->_float)
158                         tagindex = (int)val->_float;
159                 // FIXME: calculate tag matrix
160                 Matrix4x4_CreateIdentity(&tagmatrix);
161         }
162         else
163                 Matrix4x4_CreateIdentity(&tagmatrix);
164
165         if (renderflags & RF_USEAXIS)
166         {
167                 vec3_t left;
168                 VectorNegate(prog->globals.client->v_right, left);
169                 Matrix4x4_FromVectors(&matrix2, prog->globals.client->v_forward, left, prog->globals.client->v_up, ed->fields.client->origin);
170                 Matrix4x4_Scale(&matrix2, scale, 1);
171         }
172         else
173         {
174                 vec3_t angles;
175                 VectorCopy(ed->fields.client->angles, angles);
176                 // if model is alias, reverse pitch direction
177                 if (e->render.model->type == mod_alias)
178                         angles[0] = -angles[0];
179
180                 // set up the render matrix
181                 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);
182         }
183
184         // set up the animation data
185         // self.frame is the interpolation target (new frame)
186         // self.frame1time is the animation base time for the interpolation target
187         // self.frame2 is the interpolation start (previous frame)
188         // self.frame2time is the animation base time for the interpolation start
189         e->render.frame1 = e->render.frame2 = ed->fields.client->frame;
190         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2))) e->render.frame2 = val->_float;
191         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame1time))) e->render.frame2time = val->_float;
192         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.frame2time))) e->render.frame1time = val->_float;
193         if ((val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.lerpfrac))) e->render.framelerp = val->_float;
194
195         // concat the matrices to make the entity relative to its tag
196         Matrix4x4_Concat(&e->render.matrix, &tagmatrix, &matrix2);
197         // make the other useful stuff
198         CL_UpdateRenderEntity(&e->render);
199
200         if(renderflags)
201         {
202                 if(renderflags & RF_VIEWMODEL)  e->render.flags |= RENDER_VIEWMODEL;
203                 if(renderflags & RF_EXTERNALMODEL)e->render.flags |= RENDER_EXTERIORMODEL;
204                 if(renderflags & RF_DEPTHHACK)  e->render.effects |= EF_NODEPTHTEST;
205                 if(renderflags & RF_ADDITIVE)           e->render.effects |= EF_ADDITIVE;
206         }
207
208         if ((e->render.colormap > 0 && e->render.colormap <= cl.maxclients) || e->render.colormap >= 1024)
209         {
210                 unsigned char *cbcolor;
211                 int palcol;
212                 if (e->render.colormap >= 1024)
213                         palcol = (unsigned char)(e->render.colormap-1024);
214                 else
215                         palcol = cl.scores[e->render.colormap-1].colors;
216
217                 cbcolor = (unsigned char *) (&palette_pantscolormap[palcol & 0xF]);
218                 e->render.colormap_pantscolor[0] = cbcolor[0] * (1.0f / 255.0f);
219                 e->render.colormap_pantscolor[1] = cbcolor[1] * (1.0f / 255.0f);
220                 e->render.colormap_pantscolor[2] = cbcolor[2] * (1.0f / 255.0f);
221                 cbcolor = (unsigned char *) (&palette_shirtcolormap[(palcol & 0xF0) >> 4]);
222                 e->render.colormap_shirtcolor[0] = cbcolor[0] * (1.0f / 255.0f);
223                 e->render.colormap_shirtcolor[1] = cbcolor[1] * (1.0f / 255.0f);
224                 e->render.colormap_shirtcolor[2] = cbcolor[2] * (1.0f / 255.0f);
225         }
226
227         // either fullbright or lit
228         if (!(e->render.effects & EF_FULLBRIGHT) && !r_fullbright.integer)
229                 e->render.flags |= RENDER_LIGHT;
230         // hide player shadow during intermission or nehahra movie
231         if (!(e->render.effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST))
232          &&  (e->render.alpha >= 1)
233          && !(e->render.flags & RENDER_VIEWMODEL)
234          && (!(e->render.flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cls.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer)))
235                 e->render.flags |= RENDER_SHADOW;
236         if (e->render.flags & RENDER_VIEWMODEL)
237                 e->render.flags |= RENDER_NOSELFSHADOW;
238
239         return true;
240 }
241
242 qboolean CL_VM_InputEvent (qboolean pressed, int key)
243 {
244         qboolean r;
245         if(!cl.csqc_loaded)
246                 return false;
247         CSQC_BEGIN
248                 if (!prog->funcoffsets.CSQC_InputEvent)
249                         r = false;
250                 else
251                 {
252                         prog->globals.client->time = cl.time;
253                         PRVM_G_FLOAT(OFS_PARM0) = pressed;
254                         PRVM_G_FLOAT(OFS_PARM1) = key;
255                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_InputEvent, "QC function CSQC_InputEvent is missing");
256                         r = CSQC_RETURNVAL;
257                 }
258         CSQC_END
259         return r;
260 }
261
262 qboolean CL_VM_UpdateView (void)
263 {
264         vec3_t emptyvector;
265         emptyvector[0] = 0;
266         emptyvector[1] = 0;
267         emptyvector[2] = 0;
268 //      vec3_t oldangles;
269         if(!cl.csqc_loaded)
270                 return false;
271         CSQC_BEGIN
272                 //VectorCopy(cl.viewangles, oldangles);
273                 prog->globals.client->time = cl.time;
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.numentities = 0;
278                 r_refdef.numlights = 0;
279                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_UpdateView, "QC function CSQC_UpdateView is missing");
280                 //VectorCopy(oldangles, cl.viewangles);
281                 // Dresk : Reset Dmg Globals Here
282                 CL_VM_UpdateDmgGlobals(0, 0, emptyvector);
283         CSQC_END
284         return true;
285 }
286
287 extern sizebuf_t vm_tempstringsbuf;
288 qboolean CL_VM_ConsoleCommand (const char *cmd)
289 {
290         int restorevm_tempstringsbuf_cursize;
291         qboolean r = false;
292         if(!cl.csqc_loaded)
293                 return false;
294         CSQC_BEGIN
295         if (prog->funcoffsets.CSQC_ConsoleCommand)
296         {
297                 prog->globals.client->time = cl.time;
298                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
299                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(cmd);
300                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_ConsoleCommand, "QC function CSQC_ConsoleCommand is missing");
301                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
302                 r = CSQC_RETURNVAL;
303         }
304         CSQC_END
305         return r;
306 }
307
308 qboolean CL_VM_Parse_TempEntity (void)
309 {
310         int                     t;
311         qboolean        r = false;
312         if(!cl.csqc_loaded)
313                 return false;
314         CSQC_BEGIN
315         if(prog->funcoffsets.CSQC_Parse_TempEntity)
316         {
317                 t = msg_readcount;
318                 prog->globals.client->time = cl.time;
319                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_TempEntity, "QC function CSQC_Parse_TempEntity is missing");
320                 r = CSQC_RETURNVAL;
321                 if(!r)
322                 {
323                         msg_readcount = t;
324                         msg_badread = false;
325                 }
326         }
327         CSQC_END
328         return r;
329 }
330
331 void CL_VM_Parse_StuffCmd (const char *msg)
332 {
333         int restorevm_tempstringsbuf_cursize;
334         if(msg[0] == 'c')
335         if(msg[1] == 's')
336         if(msg[2] == 'q')
337         if(msg[3] == 'c')
338         {
339                 // if this is setting a csqc variable, deprotect csqc_progcrc
340                 // temporarily so that it can be set by the cvar command,
341                 // and then reprotect it afterwards
342                 int crcflags = csqc_progcrc.flags;
343                 int sizeflags = csqc_progcrc.flags;
344                 csqc_progcrc.flags &= ~CVAR_READONLY;
345                 csqc_progsize.flags &= ~CVAR_READONLY;
346                 Cmd_ExecuteString (msg, src_command);
347                 csqc_progcrc.flags = crcflags;
348                 csqc_progsize.flags = sizeflags;
349                 return;
350         }
351         if(!cl.csqc_loaded)
352         {
353                 Cbuf_AddText(msg);
354                 return;
355         }
356         CSQC_BEGIN
357         if(prog->funcoffsets.CSQC_Parse_StuffCmd)
358         {
359                 prog->globals.client->time = cl.time;
360                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
361                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
362                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_StuffCmd, "QC function CSQC_Parse_StuffCmd is missing");
363                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
364         }
365         else
366                 Cbuf_AddText(msg);
367         CSQC_END
368 }
369
370 static void CL_VM_Parse_Print (const char *msg)
371 {
372         int restorevm_tempstringsbuf_cursize;
373         prog->globals.client->time = cl.time;
374         restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
375         PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
376         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_Print, "QC function CSQC_Parse_Print is missing");
377         vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
378 }
379
380 void CSQC_AddPrintText (const char *msg)
381 {
382         size_t i;
383         if(!cl.csqc_loaded)
384         {
385                 Con_Print(msg);
386                 return;
387         }
388         CSQC_BEGIN
389         if(prog->funcoffsets.CSQC_Parse_Print)
390         {
391                 // FIXME: is this bugged?
392                 i = strlen(msg)-1;
393                 if(msg[i] != '\n' && msg[i] != '\r')
394                 {
395                         if(strlen(cl.csqc_printtextbuf)+i >= MAX_INPUTLINE)
396                         {
397                                 CL_VM_Parse_Print(cl.csqc_printtextbuf);
398                                 cl.csqc_printtextbuf[0] = 0;
399                         }
400                         else
401                                 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
402                         return;
403                 }
404                 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
405                 CL_VM_Parse_Print(cl.csqc_printtextbuf);
406                 cl.csqc_printtextbuf[0] = 0;
407         }
408         else
409                 Con_Print(msg);
410         CSQC_END
411 }
412
413 void CL_VM_Parse_CenterPrint (const char *msg)
414 {
415         int restorevm_tempstringsbuf_cursize;
416         if(!cl.csqc_loaded)
417         {
418                 SCR_CenterPrint((char*)msg);
419                 return;
420         }
421         CSQC_BEGIN
422         if(prog->funcoffsets.CSQC_Parse_CenterPrint)
423         {
424                 prog->globals.client->time = cl.time;
425                 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
426                 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
427                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_CenterPrint, "QC function CSQC_Parse_CenterPrint is missing");
428                 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
429         }
430         else
431                 SCR_CenterPrint((char*)msg);
432         CSQC_END
433 }
434
435 void CL_VM_UpdateIntermissionState (int intermission)
436 {
437         prvm_eval_t *val;
438         if(cl.csqc_loaded)
439         {
440                 CSQC_BEGIN
441                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.intermission);
442                 if(val)
443                         val->_float = intermission;
444                 CSQC_END
445         }
446 }
447 void CL_VM_UpdateShowingScoresState (int showingscores)
448 {
449         prvm_eval_t *val;
450         if(cl.csqc_loaded)
451         {
452                 CSQC_BEGIN
453                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.sb_showscores);
454                 if(val)
455                         val->_float = showingscores;
456                 CSQC_END
457         }
458 }
459 qboolean CL_VM_Event_Sound(int sound_num, int volume, int channel, float attenuation, int ent, vec3_t pos)
460 {
461         qboolean r = false;
462         if(cl.csqc_loaded)
463         {
464                 CSQC_BEGIN
465                 if(prog->funcoffsets.CSQC_Event_Sound)
466                 {
467                         prog->globals.client->time = cl.time;
468                         PRVM_G_FLOAT(OFS_PARM0) = ent;
469                         PRVM_G_FLOAT(OFS_PARM1) = channel;
470                         PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(cl.sound_name[sound_num] );
471                         PRVM_G_FLOAT(OFS_PARM3) = volume;
472                         PRVM_G_FLOAT(OFS_PARM4) = attenuation;
473                         VectorCopy(pos, PRVM_G_VECTOR(OFS_PARM5) );
474                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event_Sound, "QC function CSQC_Event_Sound is missing");
475                         r = CSQC_RETURNVAL;
476                 }
477                 CSQC_END
478         }
479
480         return r;
481 }
482 void CL_VM_UpdateCoopDeathmatchGlobals (int gametype)
483 {
484         // Avoid global names for clean(er) coding
485         int localcoop;
486         int localdeathmatch;
487
488         prvm_eval_t *val;
489         if(cl.csqc_loaded)
490         {
491                 if(gametype == GAME_COOP)
492                 {
493                         localcoop = 1;
494                         localdeathmatch = 0;
495                 }
496                 else
497                 if(gametype == GAME_DEATHMATCH)
498                 {
499                         localcoop = 0;
500                         localdeathmatch = 1;
501                 }
502                 else
503                 {
504                         // How did the ServerInfo send an unknown gametype?
505                         // Better just assign the globals as 0...
506                         localcoop = 0;
507                         localdeathmatch = 0;
508                 }
509                 CSQC_BEGIN
510                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.coop);
511                 if(val)
512                         val->_float = localcoop;
513                 val = PRVM_GLOBALFIELDVALUE(prog->globaloffsets.deathmatch);
514                 if(val)
515                         val->_float = localdeathmatch;
516                 CSQC_END
517         }
518 }
519 float CL_VM_Event (float event)         //[515]: needed ? I'd say "YES", but don't know for what :D
520 {
521         float r = 0;
522         if(!cl.csqc_loaded)
523                 return 0;
524         CSQC_BEGIN
525         if(prog->funcoffsets.CSQC_Event)
526         {
527                 prog->globals.client->time = cl.time;
528                 PRVM_G_FLOAT(OFS_PARM0) = event;
529                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event, "QC function CSQC_Event is missing");
530                 r = CSQC_RETURNVAL;
531         }
532         CSQC_END
533         return r;
534 }
535
536 void CSQC_ReadEntities (void)
537 {
538         unsigned short entnum, oldself, realentnum;
539         if(!cl.csqc_loaded)
540         {
541                 Host_Error ("CSQC_ReadEntities: CSQC is not loaded");
542                 return;
543         }
544
545         CSQC_BEGIN
546                 prog->globals.client->time = cl.time;
547                 oldself = prog->globals.client->self;
548                 while(1)
549                 {
550                         entnum = MSG_ReadShort();
551                         if(!entnum || msg_badread)
552                                 return;
553                         realentnum = entnum & 0x7FFF;
554                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum];
555                         if(entnum & 0x8000)
556                         {
557                                 if(prog->globals.client->self)
558                                 {
559                                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Remove, "QC function CSQC_Ent_Remove is missing");
560                                         cl.csqc_server2csqcentitynumber[realentnum] = 0;
561                                 }
562                                 else
563                                         Con_Printf("Smth bad happens in csqc...\n");    //[515]: never happens ?
564                         }
565                         else
566                         {
567                                 if(!prog->globals.client->self)
568                                 {
569                                         prvm_edict_t    *ed;
570                                         ed = PRVM_ED_Alloc();
571                                         ed->fields.client->entnum = realentnum;
572                                         prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT_TO_PROG(ed);
573                                         PRVM_G_FLOAT(OFS_PARM0) = 1;
574                                 }
575                                 else
576                                         PRVM_G_FLOAT(OFS_PARM0) = 0;
577                                 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Update, "QC function CSQC_Ent_Update is missing");
578                         }
579                 }
580                 prog->globals.client->self = oldself;
581         CSQC_END
582 }
583
584 void CL_VM_CB_BeginIncreaseEdicts(void)
585 {
586         int i;
587         prvm_edict_t *ent;
588
589         // links don't survive the transition, so unlink everything
590         for (i = 0, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
591         {
592                 if (!ent->priv.server->free)
593                         World_UnlinkEdict(prog->edicts + i);
594                 memset(&ent->priv.server->areagrid, 0, sizeof(ent->priv.server->areagrid));
595         }
596         World_Clear(&cl.world);
597 }
598
599 void CL_VM_CB_EndIncreaseEdicts(void)
600 {
601         int i;
602         prvm_edict_t *ent;
603
604         // link every entity except world
605         for (i = 1, ent = prog->edicts;i < prog->max_edicts;i++, ent++)
606                 if (!ent->priv.server->free)
607                         CL_LinkEdict(ent);
608 }
609
610 void CL_VM_CB_InitEdict(prvm_edict_t *e)
611 {
612         e->priv.server->move = false; // don't move on first frame
613 }
614
615 void CL_VM_CB_FreeEdict(prvm_edict_t *ed)
616 {
617         World_UnlinkEdict(ed);
618         memset(ed->fields.client, 0, sizeof(*ed->fields.client));
619 }
620
621 void CL_VM_CB_CountEdicts(void)
622 {
623         int             i;
624         prvm_edict_t    *ent;
625         int             active = 0, models = 0, solid = 0;
626
627         for (i=0 ; i<prog->num_edicts ; i++)
628         {
629                 ent = PRVM_EDICT_NUM(i);
630                 if (ent->priv.server->free)
631                         continue;
632                 active++;
633                 if (ent->fields.client->solid)
634                         solid++;
635                 if (ent->fields.client->model)
636                         models++;
637         }
638
639         Con_Printf("num_edicts:%3i\n", prog->num_edicts);
640         Con_Printf("active    :%3i\n", active);
641         Con_Printf("view      :%3i\n", models);
642         Con_Printf("touch     :%3i\n", solid);
643 }
644
645 qboolean CL_VM_CB_LoadEdict(prvm_edict_t *ent)
646 {
647         return true;
648 }
649
650 void Cmd_ClearCsqcFuncs (void);
651
652 void CL_VM_Init (void)
653 {
654         unsigned char *csprogsdata;
655         fs_offset_t csprogsdatasize;
656         int csprogsdatacrc, requiredcrc;
657         int requiredsize;
658         prvm_eval_t *val;
659
660         // reset csqc_progcrc after reading it, so that changing servers doesn't
661         // expect csqc on the next server
662         requiredcrc = csqc_progcrc.integer;
663         requiredsize = csqc_progsize.integer;
664         Cvar_SetValueQuick(&csqc_progcrc, -1);
665         Cvar_SetValueQuick(&csqc_progsize, -1);
666
667         // if the server is not requesting a csprogs, then we're done here
668         if (requiredcrc < 0)
669                 return;
670
671         // see if the requested csprogs.dat file matches the requested crc
672         csprogsdatacrc = -1;
673         csprogsdata = FS_LoadFile(va("dlcache/%s.%i.%i", csqc_progname.string, requiredsize, requiredcrc), tempmempool, true, &csprogsdatasize);
674         if (!csprogsdata)
675                 csprogsdata = FS_LoadFile(csqc_progname.string, tempmempool, true, &csprogsdatasize);
676         if (csprogsdata)
677         {
678                 csprogsdatacrc = CRC_Block(csprogsdata, csprogsdatasize);
679                 Mem_Free(csprogsdata);
680                 if (csprogsdatacrc != requiredcrc || csprogsdatasize != requiredsize)
681                 {
682                         if (cls.demoplayback)
683                         {
684                                 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);
685                                 return;
686                         }
687                         else
688                         {
689                                 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);
690                                 CL_Disconnect();
691                                 return;
692                         }
693                 }
694         }
695         else
696         {
697                 if (requiredcrc >= 0)
698                 {
699                         if (cls.demoplayback)
700                                 Con_Printf("CL_VM_Init: demo requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
701                         else
702                                 Con_Printf("CL_VM_Init: server requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
703                         CL_Disconnect();
704                 }
705                 return;
706         }
707
708         PRVM_Begin;
709         PRVM_InitProg(PRVM_CLIENTPROG);
710
711         // allocate the mempools
712         prog->progs_mempool = Mem_AllocPool(csqc_progname.string, 0, NULL);
713         prog->headercrc = CL_PROGHEADER_CRC;
714         prog->edictprivate_size = 0; // no private struct used
715         prog->name = CL_NAME;
716         prog->num_edicts = 1;
717         prog->max_edicts = 512;
718         prog->limit_edicts = CL_MAX_EDICTS;
719         prog->reserved_edicts = 0;
720         prog->edictprivate_size = sizeof(edict_engineprivate_t);
721         prog->extensionstring = vm_sv_extensions;
722         prog->builtins = vm_cl_builtins;
723         prog->numbuiltins = vm_cl_numbuiltins;
724         prog->begin_increase_edicts = CL_VM_CB_BeginIncreaseEdicts;
725         prog->end_increase_edicts = CL_VM_CB_EndIncreaseEdicts;
726         prog->init_edict = CL_VM_CB_InitEdict;
727         prog->free_edict = CL_VM_CB_FreeEdict;
728         prog->count_edicts = CL_VM_CB_CountEdicts;
729         prog->load_edict = CL_VM_CB_LoadEdict;
730         prog->init_cmd = VM_CL_Cmd_Init;
731         prog->reset_cmd = VM_CL_Cmd_Reset;
732         prog->error_cmd = CL_VM_Error;
733
734         PRVM_LoadProgs(csqc_progname.string, cl_numrequiredfunc, cl_required_func, 0, NULL, 0, NULL);
735
736         if (!prog->loaded)
737         {
738                 CL_VM_Error("CSQC ^2failed to load\n");
739                 if(!sv.active)
740                         CL_Disconnect();
741                 return;
742         }
743
744         Con_Printf("CSQC ^5loaded (crc=%i, size=%i)\n", csprogsdatacrc, (int)csprogsdatasize);
745
746         // check if OP_STATE animation is possible in this dat file
747         if (prog->fieldoffsets.nextthink >= 0 && prog->fieldoffsets.frame >= 0 && prog->fieldoffsets.think >= 0 && prog->globaloffsets.self >= 0)
748                 prog->flag |= PRVM_OP_STATE;
749
750         // set time
751         prog->globals.client->time = cl.time;
752
753         prog->globals.client->mapname = PRVM_SetEngineString(cl.worldmodel->name);
754         prog->globals.client->player_localentnum = cl.playerentity;
755
756         // set map description (use world entity 0)
757         val = PRVM_EDICTFIELDVALUE(prog->edicts, prog->fieldoffsets.message);
758         if(val)
759                 val->string = PRVM_SetEngineString(cl.levelname);
760
761         // call the prog init
762         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Init, "QC function CSQC_Init is missing");
763
764         PRVM_End;
765         cl.csqc_loaded = true;
766
767         cl.csqc_vidvars.drawcrosshair = false;
768         cl.csqc_vidvars.drawenginesbar = false;
769
770         // Update Coop and Deathmatch Globals (at this point the client knows them from ServerInfo)
771         CL_VM_UpdateCoopDeathmatchGlobals(cl.gametype);
772 }
773
774 void CL_VM_ShutDown (void)
775 {
776         Cmd_ClearCsqcFuncs();
777         Cvar_SetValueQuick(&csqc_progcrc, -1);
778         Cvar_SetValueQuick(&csqc_progsize, -1);
779         if(!cl.csqc_loaded)
780                 return;
781         CSQC_BEGIN
782                 prog->globals.client->time = cl.time;
783                 if (prog->funcoffsets.CSQC_Shutdown)
784                         PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Shutdown, "QC function CSQC_Shutdown is missing");
785                 PRVM_ResetProg();
786         CSQC_END
787         Con_Print("CSQC ^1unloaded\n");
788         cl.csqc_loaded = false;
789 }