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