3 #include "clprogdefs.h"
5 #include "cl_collision.h"
10 //============================================================================
11 // Client prog handling
12 //[515]: omg !!! optimize it ! a lot of hacks here and there also :P
14 #define CSQC_RETURNVAL prog->globals.generic[OFS_RETURN]
15 #define CSQC_BEGIN csqc_tmpprog=prog;prog=0;PRVM_SetProg(PRVM_CLIENTPROG);
16 #define CSQC_END prog=csqc_tmpprog;
18 static prvm_prog_t *csqc_tmpprog;
20 void CL_VM_PreventInformationLeaks(void)
25 VM_ClearTraceGlobals();
26 PRVM_clientglobalfloat(trace_networkentity) = 0;
30 //[515]: these are required funcs
31 static const char *cl_required_func[] =
36 "CSQC_ConsoleCommand",
39 static int cl_numrequiredfunc = sizeof(cl_required_func) / sizeof(char*);
41 #define CL_REQFIELDS (sizeof(cl_reqfields) / sizeof(prvm_required_field_t))
43 prvm_required_field_t cl_reqfields[] =
45 {ev_entity, "aiment"}, // client field
46 {ev_entity, "enemy"}, // client field
47 {ev_entity, "groundentity"}, // client field
48 {ev_entity, "tag_entity"}, // client field
49 {ev_float, "alpha"}, // client field
50 {ev_float, "bouncefactor"}, // client field
51 {ev_float, "bouncestop"}, // client field
52 {ev_float, "dphitcontentsmask"}, // client field
53 {ev_float, "frame1time"}, // client field
54 {ev_float, "frame2time"}, // client field
55 {ev_float, "frame2"}, // client field
56 {ev_float, "frame3time"}, // client field
57 {ev_float, "frame3"}, // client field
58 {ev_float, "frame4time"}, // client field
59 {ev_float, "frame4"}, // client field
60 {ev_float, "frame"}, // client field
61 {ev_float, "gravity"}, // client field
62 {ev_float, "gravity"}, // client field
63 {ev_float, "ideal_yaw"}, // client field
64 {ev_float, "idealpitch"}, // client field
65 {ev_float, "jointtype"}, // client field
66 {ev_float, "lerpfrac3"}, // client field
67 {ev_float, "lerpfrac4"}, // client field
68 {ev_float, "lerpfrac"}, // client field
69 {ev_float, "mass"}, // client field
70 {ev_float, "modelindex"}, // client field
71 {ev_float, "movetype"}, // client field
72 {ev_float, "pitch_speed"}, // client field
73 {ev_float, "renderflags"}, // client field
74 {ev_float, "scale"}, // client field
75 {ev_float, "scale"}, // client field
76 {ev_float, "shadertime"}, // client field
77 {ev_float, "skeletonindex"}, // client field
78 {ev_float, "solid"}, // client field
79 {ev_float, "tag_index"}, // client field
80 {ev_float, "userwavefunc_param0"}, // client field
81 {ev_float, "userwavefunc_param1"}, // client field
82 {ev_float, "userwavefunc_param2"}, // client field
83 {ev_float, "userwavefunc_param3"}, // client field
84 {ev_float, "yaw_speed"}, // client field
85 {ev_function, "camera_transform"}, // client field
86 {ev_string, "classname"}, // client field
87 {ev_string, "message"}, // client field
88 {ev_vector, "angles"}, // client field
89 {ev_vector, "avelocity"}, // client field
90 {ev_vector, "colormod"}, // client field
91 {ev_vector, "glowmod"}, // client field
92 {ev_vector, "maxs"}, // client field
93 {ev_vector, "mins"}, // client field
94 {ev_vector, "movedir"}, // client field
95 {ev_vector, "origin"}, // client field
96 {ev_vector, "velocity"}, // client field
99 #define CL_REQGLOBALS (sizeof(cl_reqglobals) / sizeof(prvm_required_field_t))
101 prvm_required_field_t cl_reqglobals[] =
103 {ev_entity, "self"}, // client global
104 {ev_entity, "trace_ent"}, // client global
105 {ev_float, "coop"}, // client global
106 {ev_float, "deathmatch"}, // client global
107 {ev_float, "dmg_save"}, // client global
108 {ev_float, "dmg_take"}, // client global
109 {ev_float, "drawfont"}, // client global
110 {ev_float, "gettaginfo_parent"}, // client global
111 {ev_float, "intermission"}, // client global
112 {ev_float, "particle_airfriction"}, // client global
113 {ev_float, "particle_alpha"}, // client global
114 {ev_float, "particle_alphafade"}, // client global
115 {ev_float, "particle_angle"}, // client global
116 {ev_float, "particle_blendmode"}, // client global
117 {ev_float, "particle_bounce"}, // client global
118 {ev_float, "particle_delaycollision"}, // client global
119 {ev_float, "particle_delayspawn"}, // client global
120 {ev_float, "particle_gravity"}, // client global
121 {ev_float, "particle_liquidfriction"}, // client global
122 {ev_float, "particle_orientation"}, // client global
123 {ev_float, "particle_originjitter"}, // client global
124 {ev_float, "particle_qualityreduction"}, // client global
125 {ev_float, "particle_size"}, // client global
126 {ev_float, "particle_sizeincrease"}, // client global
127 {ev_float, "particle_spin"}, // client global
128 {ev_float, "particle_stainalpha"}, // client global
129 {ev_float, "particle_stainsize"}, // client global
130 {ev_float, "particle_staintex"}, // client global
131 {ev_float, "particle_staintex"}, // client global
132 {ev_float, "particle_stretch"}, // client global
133 {ev_float, "particle_tex"}, // client global
134 {ev_float, "particle_time"}, // client global
135 {ev_float, "particle_type"}, // client global
136 {ev_float, "particle_velocityjitter"}, // client global
137 {ev_float, "particles_alphamax"}, // client global
138 {ev_float, "particles_alphamin"}, // client global
139 {ev_float, "require_spawnfunc_prefix"}, // client global
140 {ev_float, "sb_showscores"}, // client global
141 {ev_float, "serverdeltatime"}, // client global
142 {ev_float, "serverprevtime"}, // client global
143 {ev_float, "servertime"}, // client global
144 {ev_float, "trace_allsolid"}, // client global
145 {ev_float, "trace_dphitcontents"}, // client global
146 {ev_float, "trace_dphitq3surfaceflags"}, // client global
147 {ev_float, "trace_dpstartcontents"}, // client global
148 {ev_float, "trace_fraction"}, // client global
149 {ev_float, "trace_inopen"}, // client global
150 {ev_float, "trace_inwater"}, // client global
151 {ev_float, "trace_networkentity"}, // client global
152 {ev_float, "trace_plane_dist"}, // client global
153 {ev_float, "trace_startsolid"}, // client global
154 {ev_float, "transparent_offset"}, // client global
155 {ev_string, "gettaginfo_name"}, // client global
156 {ev_string, "trace_dphittexturename"}, // client global
157 {ev_vector, "dmg_origin"}, // client global
158 {ev_vector, "drawfontscale"}, // client global
159 {ev_vector, "gettaginfo_forward"}, // client global
160 {ev_vector, "gettaginfo_offset"}, // client global
161 {ev_vector, "gettaginfo_right"}, // client global
162 {ev_vector, "gettaginfo_up"}, // client global
163 {ev_vector, "particle_color1"}, // client global
164 {ev_vector, "particle_color2"}, // client global
165 {ev_vector, "particle_staincolor1"}, // client global
166 {ev_vector, "particle_staincolor2"}, // client global
167 {ev_vector, "particles_colormax"}, // client global
168 {ev_vector, "particles_colormin"}, // client global
169 {ev_vector, "pmove_inwater"}, // client global
170 {ev_vector, "pmove_onground"}, // client global
171 {ev_vector, "trace_endpos"}, // client global
172 {ev_vector, "trace_plane_normal"}, // client global
173 {ev_vector, "v_forward"}, // client global
174 {ev_vector, "v_right"}, // client global
175 {ev_vector, "v_up"}, // client global
176 {ev_vector, "view_angles"}, // client global
177 {ev_vector, "view_punchangle"}, // client global
178 {ev_vector, "view_punchvector"}, // client global
181 void CL_VM_Error (const char *format, ...) DP_FUNC_PRINTF(1);
182 void CL_VM_Error (const char *format, ...) //[515]: hope it will be never executed =)
184 char errorstring[4096];
187 va_start (argptr, format);
188 dpvsnprintf (errorstring, sizeof(errorstring), format, argptr);
190 // Con_Printf( "CL_VM_Error: %s\n", errorstring );
193 cl.csqc_loaded = false;
195 Cvar_SetValueQuick(&csqc_progcrc, -1);
196 Cvar_SetValueQuick(&csqc_progsize, -1);
198 // Host_AbortCurrentFrame(); //[515]: hmmm... if server says it needs csqc then client MUST disconnect
199 Host_Error("CL_VM_Error: %s", errorstring);
201 void CL_VM_UpdateDmgGlobals (int dmg_take, int dmg_save, vec3_t dmg_origin)
206 PRVM_clientglobalfloat(dmg_take) = dmg_take;
207 PRVM_clientglobalfloat(dmg_save) = dmg_save;
208 VectorCopy(dmg_origin, PRVM_clientglobalvector(dmg_origin));
213 void CSQC_UpdateNetworkTimes(double newtime, double oldtime)
218 PRVM_clientglobalfloat(servertime) = newtime;
219 PRVM_clientglobalfloat(serverprevtime) = oldtime;
220 PRVM_clientglobalfloat(serverdeltatime) = newtime - oldtime;
224 //[515]: set globals before calling R_UpdateView, WEIRD CRAP
225 static void CSQC_SetGlobals (void)
228 prog->globals.client->time = cl.time;
229 prog->globals.client->frametime = max(0, cl.time - cl.oldtime);
230 prog->globals.client->servercommandframe = cls.servermovesequence;
231 prog->globals.client->clientcommandframe = cl.movecmd[0].sequence;
232 VectorCopy(cl.viewangles, prog->globals.client->input_angles);
233 // // FIXME: this actually belongs into getinputstate().. [12/17/2007 Black]
234 prog->globals.client->input_buttons = cl.movecmd[0].buttons;
235 VectorSet(prog->globals.client->input_movevalues, cl.movecmd[0].forwardmove, cl.movecmd[0].sidemove, cl.movecmd[0].upmove);
236 VectorCopy(cl.csqc_vieworiginfromengine, cl.csqc_vieworigin);
237 VectorCopy(cl.csqc_viewanglesfromengine, cl.csqc_viewangles);
239 // LordHavoc: Spike says not to do this, but without pmove_org the
240 // CSQC is useless as it can't alter the view origin without
241 // completely replacing it
242 Matrix4x4_OriginFromMatrix(&cl.entities[cl.viewentity].render.matrix, prog->globals.client->pmove_org);
243 VectorCopy(cl.movement_velocity, prog->globals.client->pmove_vel);
244 PRVM_clientglobalfloat(pmove_onground) = cl.onground;
245 PRVM_clientglobalfloat(pmove_inwater) = cl.inwater;
247 VectorCopy(cl.viewangles, PRVM_clientglobalvector(view_angles));
248 VectorCopy(cl.punchangle, PRVM_clientglobalvector(view_punchangle));
249 VectorCopy(cl.punchvector, PRVM_clientglobalvector(view_punchvector));
250 prog->globals.client->maxclients = cl.maxclients;
254 void CSQC_Predraw (prvm_edict_t *ed)
257 if(!ed->fields.client->predraw)
259 b = prog->globals.client->self;
260 prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
261 PRVM_ExecuteProgram(ed->fields.client->predraw, "CSQC_Predraw: NULL function\n");
262 prog->globals.client->self = b;
265 void CSQC_Think (prvm_edict_t *ed)
268 if(ed->fields.client->think)
269 if(ed->fields.client->nextthink && ed->fields.client->nextthink <= prog->globals.client->time)
271 ed->fields.client->nextthink = 0;
272 b = prog->globals.client->self;
273 prog->globals.client->self = PRVM_EDICT_TO_PROG(ed);
274 PRVM_ExecuteProgram(ed->fields.client->think, "CSQC_Think: NULL function\n");
275 prog->globals.client->self = b;
279 extern cvar_t cl_noplayershadow;
280 extern cvar_t r_equalize_entities_fullbright;
281 qboolean CSQC_AddRenderEdict(prvm_edict_t *ed, int edictnum)
286 entity_render_t *entrender;
289 model = CL_GetModelFromEdict(ed);
295 if (r_refdef.scene.numentities >= r_refdef.scene.maxentities)
297 entrender = cl.csqcrenderentities + edictnum;
298 r_refdef.scene.entities[r_refdef.scene.numentities++] = entrender;
299 entrender->entitynumber = edictnum + MAX_EDICTS;
300 //entrender->shadertime = 0; // shadertime was set by spawn()
301 entrender->flags = 0;
302 entrender->alpha = 1;
303 entrender->scale = 1;
304 VectorSet(entrender->colormod, 1, 1, 1);
305 VectorSet(entrender->glowmod, 1, 1, 1);
306 entrender->allowdecals = true;
310 entrender = CL_NewTempEntity(0);
315 entrender->userwavefunc_param[0] = PRVM_clientedictfloat(ed, userwavefunc_param0);
316 entrender->userwavefunc_param[1] = PRVM_clientedictfloat(ed, userwavefunc_param1);
317 entrender->userwavefunc_param[2] = PRVM_clientedictfloat(ed, userwavefunc_param2);
318 entrender->userwavefunc_param[3] = PRVM_clientedictfloat(ed, userwavefunc_param3);
320 entrender->model = model;
321 entrender->skinnum = (int)ed->fields.client->skin;
322 entrender->effects |= entrender->model->effects;
323 renderflags = (int)PRVM_clientedictfloat(ed, renderflags);
324 entrender->alpha = PRVM_clientedictfloat(ed, alpha);
325 entrender->scale = scale = PRVM_clientedictfloat(ed, scale);
326 VectorCopy(PRVM_clientedictvector(ed, colormod), entrender->colormod);
327 VectorCopy(PRVM_clientedictvector(ed, glowmod), entrender->glowmod);
328 if(ed->fields.client->effects) entrender->effects |= (int)ed->fields.client->effects;
329 if (!entrender->alpha)
330 entrender->alpha = 1.0f;
331 if (!entrender->scale)
332 entrender->scale = scale = 1.0f;
333 if (!VectorLength2(entrender->colormod))
334 VectorSet(entrender->colormod, 1, 1, 1);
335 if (!VectorLength2(entrender->glowmod))
336 VectorSet(entrender->glowmod, 1, 1, 1);
338 // LordHavoc: use the CL_GetTagMatrix function on self to ensure consistent behavior (duplicate code would be bad)
339 CL_GetTagMatrix(&entrender->matrix, ed, 0);
341 // set up the animation data
342 VM_GenerateFrameGroupBlend(ed->priv.server->framegroupblend, ed);
343 VM_FrameBlendFromFrameGroupBlend(ed->priv.server->frameblend, ed->priv.server->framegroupblend, model);
344 VM_UpdateEdictSkeleton(ed, model, ed->priv.server->frameblend);
345 entrender->shadertime = PRVM_clientedictfloat(ed, shadertime);
347 // transparent offset
348 if (renderflags & RF_USETRANSPARENTOFFSET)
349 entrender->transparent_offset = PRVM_clientglobalfloat(transparent_offset);
353 if(renderflags & RF_VIEWMODEL) entrender->flags |= RENDER_VIEWMODEL | RENDER_NODEPTHTEST;
354 if(renderflags & RF_EXTERNALMODEL)entrender->flags |= RENDER_EXTERIORMODEL;
355 if(renderflags & RF_NOCULL) entrender->flags |= RENDER_NOCULL;
356 if(renderflags & RF_DEPTHHACK) entrender->flags |= RENDER_NODEPTHTEST;
357 if(renderflags & RF_ADDITIVE) entrender->flags |= RENDER_ADDITIVE;
360 c = (int)ed->fields.client->colormap;
362 CL_SetEntityColormapColors(entrender, -1);
363 else if (c <= cl.maxclients && cl.scores != NULL)
364 CL_SetEntityColormapColors(entrender, cl.scores[c-1].colors);
366 CL_SetEntityColormapColors(entrender, c);
368 entrender->flags &= ~(RENDER_SHADOW | RENDER_LIGHT | RENDER_NOSELFSHADOW);
369 // either fullbright or lit
370 if(!r_fullbright.integer)
372 if (!(entrender->effects & EF_FULLBRIGHT) && !(renderflags & RF_FULLBRIGHT))
373 entrender->flags |= RENDER_LIGHT;
374 else if(r_equalize_entities_fullbright.integer)
375 entrender->flags |= RENDER_LIGHT | RENDER_EQUALIZE;
377 // hide player shadow during intermission or nehahra movie
378 if (!(entrender->effects & (EF_NOSHADOW | EF_ADDITIVE | EF_NODEPTHTEST))
379 && (entrender->alpha >= 1)
380 && !(renderflags & RF_NOSHADOW)
381 && !(entrender->flags & RENDER_VIEWMODEL)
382 && (!(entrender->flags & RENDER_EXTERIORMODEL) || (!cl.intermission && cls.protocol != PROTOCOL_NEHAHRAMOVIE && !cl_noplayershadow.integer)))
383 entrender->flags |= RENDER_SHADOW;
384 if (entrender->flags & RENDER_VIEWMODEL)
385 entrender->flags |= RENDER_NOSELFSHADOW;
386 if (entrender->effects & EF_NOSELFSHADOW)
387 entrender->flags |= RENDER_NOSELFSHADOW;
388 if (entrender->effects & EF_NODEPTHTEST)
389 entrender->flags |= RENDER_NODEPTHTEST;
390 if (entrender->effects & EF_ADDITIVE)
391 entrender->flags |= RENDER_ADDITIVE;
392 if (entrender->effects & EF_DOUBLESIDED)
393 entrender->flags |= RENDER_DOUBLESIDED;
395 // make the other useful stuff
396 memcpy(entrender->framegroupblend, ed->priv.server->framegroupblend, sizeof(ed->priv.server->framegroupblend));
397 CL_UpdateRenderEntity(entrender);
398 // override animation data with full control
399 memcpy(entrender->frameblend, ed->priv.server->frameblend, sizeof(ed->priv.server->frameblend));
400 if (ed->priv.server->skeleton.relativetransforms)
401 entrender->skeleton = &ed->priv.server->skeleton;
403 entrender->skeleton = NULL;
408 qboolean CL_VM_InputEvent (qboolean down, int key, int ascii)
416 if (!prog->funcoffsets.CSQC_InputEvent)
420 prog->globals.client->time = cl.time;
421 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
422 PRVM_G_FLOAT(OFS_PARM0) = !down; // 0 is down, 1 is up
423 PRVM_G_FLOAT(OFS_PARM1) = key;
424 PRVM_G_FLOAT(OFS_PARM2) = ascii;
425 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_InputEvent, "QC function CSQC_InputEvent is missing");
426 r = CSQC_RETURNVAL != 0;
432 qboolean CL_VM_UpdateView (void)
441 R_TimeReport("pre-UpdateView");
443 //VectorCopy(cl.viewangles, oldangles);
444 prog->globals.client->time = cl.time;
445 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
447 // clear renderable entity and light lists to prevent crashes if the
448 // CSQC_UpdateView function does not call R_ClearScene as it should
449 r_refdef.scene.numentities = 0;
450 r_refdef.scene.numlights = 0;
451 // pass in width and height as parameters (EXT_CSQC_1)
452 PRVM_G_FLOAT(OFS_PARM0) = vid.width;
453 PRVM_G_FLOAT(OFS_PARM1) = vid.height;
454 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_UpdateView, "QC function CSQC_UpdateView is missing");
455 //VectorCopy(oldangles, cl.viewangles);
456 // Dresk : Reset Dmg Globals Here
457 CL_VM_UpdateDmgGlobals(0, 0, emptyvector);
459 R_TimeReport("UpdateView");
463 extern sizebuf_t vm_tempstringsbuf;
464 qboolean CL_VM_ConsoleCommand (const char *cmd)
466 int restorevm_tempstringsbuf_cursize;
471 if (prog->funcoffsets.CSQC_ConsoleCommand)
473 prog->globals.client->time = cl.time;
474 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
475 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
476 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(cmd);
477 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_ConsoleCommand, "QC function CSQC_ConsoleCommand is missing");
478 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
479 r = CSQC_RETURNVAL != 0;
485 qboolean CL_VM_Parse_TempEntity (void)
492 if(prog->funcoffsets.CSQC_Parse_TempEntity)
495 prog->globals.client->time = cl.time;
496 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
497 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_TempEntity, "QC function CSQC_Parse_TempEntity is missing");
498 r = CSQC_RETURNVAL != 0;
509 void CL_VM_Parse_StuffCmd (const char *msg)
511 int restorevm_tempstringsbuf_cursize;
517 // if this is setting a csqc variable, deprotect csqc_progcrc
518 // temporarily so that it can be set by the cvar command,
519 // and then reprotect it afterwards
520 int crcflags = csqc_progcrc.flags;
521 int sizeflags = csqc_progcrc.flags;
522 csqc_progcrc.flags &= ~CVAR_READONLY;
523 csqc_progsize.flags &= ~CVAR_READONLY;
524 Cmd_ExecuteString (msg, src_command);
525 csqc_progcrc.flags = crcflags;
526 csqc_progsize.flags = sizeflags;
531 if(!strncmp(msg, "curl --clear_autodownload\ncurl --pak --forthismap --as ", 55))
533 // special handling for map download commands
534 // run these commands IMMEDIATELY, instead of waiting for a client frame
535 // that way, there is no black screen when playing back demos
536 // I know this is a really ugly hack, but I can't think of any better way
537 // FIXME find the actual CAUSE of this, and make demo playback WAIT
538 // until all maps are loaded, then remove this hack
540 char buf[MAX_INPUTLINE];
553 if(l > sizeof(buf) - 1)
555 strlcpy(buf, p, l + 1); // strlcpy needs a + 1 as it includes the newline!
557 Cmd_ExecuteString(buf, src_command);
561 ++p; // skip the newline and continue
563 break; // end of string or overflow
565 Cmd_ExecuteString("curl --clear_autodownload", src_command); // don't inhibit CSQC loading
575 if(prog->funcoffsets.CSQC_Parse_StuffCmd)
577 prog->globals.client->time = cl.time;
578 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
579 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
580 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
581 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_StuffCmd, "QC function CSQC_Parse_StuffCmd is missing");
582 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
589 static void CL_VM_Parse_Print (const char *msg)
591 int restorevm_tempstringsbuf_cursize;
592 prog->globals.client->time = cl.time;
593 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
594 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
595 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
596 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_Print, "QC function CSQC_Parse_Print is missing");
597 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
600 void CSQC_AddPrintText (const char *msg)
609 if(prog->funcoffsets.CSQC_Parse_Print)
611 // FIXME: is this bugged?
613 if(msg[i] != '\n' && msg[i] != '\r')
615 if(strlen(cl.csqc_printtextbuf)+i >= MAX_INPUTLINE)
617 CL_VM_Parse_Print(cl.csqc_printtextbuf);
618 cl.csqc_printtextbuf[0] = 0;
621 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
624 strlcat(cl.csqc_printtextbuf, msg, MAX_INPUTLINE);
625 CL_VM_Parse_Print(cl.csqc_printtextbuf);
626 cl.csqc_printtextbuf[0] = 0;
633 void CL_VM_Parse_CenterPrint (const char *msg)
635 int restorevm_tempstringsbuf_cursize;
638 SCR_CenterPrint(msg);
642 if(prog->funcoffsets.CSQC_Parse_CenterPrint)
644 prog->globals.client->time = cl.time;
645 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
646 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
647 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(msg);
648 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Parse_CenterPrint, "QC function CSQC_Parse_CenterPrint is missing");
649 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
652 SCR_CenterPrint(msg);
656 void CL_VM_UpdateIntermissionState (int intermission)
661 PRVM_clientglobalfloat(intermission) = intermission;
665 void CL_VM_UpdateShowingScoresState (int showingscores)
670 PRVM_clientglobalfloat(sb_showscores) = showingscores;
674 qboolean CL_VM_Event_Sound(int sound_num, float volume, int channel, float attenuation, int ent, vec3_t pos)
680 if(prog->funcoffsets.CSQC_Event_Sound)
682 prog->globals.client->time = cl.time;
683 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
684 PRVM_G_FLOAT(OFS_PARM0) = ent;
685 PRVM_G_FLOAT(OFS_PARM1) = channel;
686 PRVM_G_INT(OFS_PARM2) = PRVM_SetTempString(cl.sound_name[sound_num] );
687 PRVM_G_FLOAT(OFS_PARM3) = volume;
688 PRVM_G_FLOAT(OFS_PARM4) = attenuation;
689 VectorCopy(pos, PRVM_G_VECTOR(OFS_PARM5) );
690 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event_Sound, "QC function CSQC_Event_Sound is missing");
691 r = CSQC_RETURNVAL != 0;
698 void CL_VM_UpdateCoopDeathmatchGlobals (int gametype)
700 // Avoid global names for clean(er) coding
706 if(gametype == GAME_COOP)
712 if(gametype == GAME_DEATHMATCH)
719 // How did the ServerInfo send an unknown gametype?
720 // Better just assign the globals as 0...
725 PRVM_clientglobalfloat(coop) = localcoop;
726 PRVM_clientglobalfloat(deathmatch) = localdeathmatch;
730 float CL_VM_Event (float event) //[515]: needed ? I'd say "YES", but don't know for what :D
736 if(prog->funcoffsets.CSQC_Event)
738 prog->globals.client->time = cl.time;
739 prog->globals.client->self = cl.csqc_server2csqcentitynumber[cl.playerentity];
740 PRVM_G_FLOAT(OFS_PARM0) = event;
741 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Event, "QC function CSQC_Event is missing");
748 void CSQC_ReadEntities (void)
750 unsigned short entnum, oldself, realentnum;
753 Host_Error ("CSQC_ReadEntities: CSQC is not loaded");
758 prog->globals.client->time = cl.time;
759 oldself = prog->globals.client->self;
762 entnum = MSG_ReadShort();
763 if(!entnum || msg_badread)
765 realentnum = entnum & 0x7FFF;
766 prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum];
769 if(prog->globals.client->self)
771 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Remove, "QC function CSQC_Ent_Remove is missing");
772 cl.csqc_server2csqcentitynumber[realentnum] = 0;
776 // LordHavoc: removing an entity that is already gone on
777 // the csqc side is possible for legitimate reasons (such
778 // as a repeat of the remove message), so no warning is
780 //Con_Printf("Bad csqc_server2csqcentitynumber map\n"); //[515]: never happens ?
785 if(!prog->globals.client->self)
787 if(!prog->funcoffsets.CSQC_Ent_Spawn)
790 ed = PRVM_ED_Alloc();
791 ed->fields.client->entnum = realentnum;
792 prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT_TO_PROG(ed);
796 // entity( float entnum ) CSQC_Ent_Spawn;
797 // the qc function should set entnum, too (this way it also can return world [2/1/2008 Andreas]
798 PRVM_G_FLOAT(OFS_PARM0) = (float) realentnum;
799 // make sure no one gets wrong ideas
800 prog->globals.client->self = 0;
801 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Spawn, "QC function CSQC_Ent_Spawn is missing");
802 prog->globals.client->self = cl.csqc_server2csqcentitynumber[realentnum] = PRVM_EDICT( PRVM_G_INT( OFS_RETURN ) );
804 PRVM_G_FLOAT(OFS_PARM0) = 1;
805 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Update, "QC function CSQC_Ent_Update is missing");
808 PRVM_G_FLOAT(OFS_PARM0) = 0;
809 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Ent_Update, "QC function CSQC_Ent_Update is missing");
813 prog->globals.client->self = oldself;
817 void CL_VM_CB_BeginIncreaseEdicts(void)
819 // links don't survive the transition, so unlink everything
820 World_UnlinkAll(&cl.world);
823 void CL_VM_CB_EndIncreaseEdicts(void)
828 // link every entity except world
829 for (i = 1, ent = prog->edicts;i < prog->num_edicts;i++, ent++)
830 if (!ent->priv.server->free)
834 void CL_VM_CB_InitEdict(prvm_edict_t *e)
836 int edictnum = PRVM_NUM_FOR_EDICT(e);
837 entity_render_t *entrender;
838 CL_ExpandCSQCRenderEntities(edictnum);
839 entrender = cl.csqcrenderentities + edictnum;
840 e->priv.server->move = false; // don't move on first frame
841 memset(entrender, 0, sizeof(*entrender));
842 entrender->shadertime = cl.time;
845 extern void R_DecalSystem_Reset(decalsystem_t *decalsystem);
847 void CL_VM_CB_FreeEdict(prvm_edict_t *ed)
849 entity_render_t *entrender = cl.csqcrenderentities + PRVM_NUM_FOR_EDICT(ed);
850 R_DecalSystem_Reset(&entrender->decalsystem);
851 memset(entrender, 0, sizeof(*entrender));
852 World_UnlinkEdict(ed);
853 memset(ed->fields.client, 0, sizeof(*ed->fields.client));
854 VM_RemoveEdictSkeleton(ed);
855 World_Physics_RemoveFromEntity(&cl.world, ed);
856 World_Physics_RemoveJointFromEntity(&cl.world, ed);
859 void CL_VM_CB_CountEdicts(void)
863 int active = 0, models = 0, solid = 0;
865 for (i=0 ; i<prog->num_edicts ; i++)
867 ent = PRVM_EDICT_NUM(i);
868 if (ent->priv.server->free)
871 if (ent->fields.client->solid)
873 if (ent->fields.client->model)
877 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
878 Con_Printf("active :%3i\n", active);
879 Con_Printf("view :%3i\n", models);
880 Con_Printf("touch :%3i\n", solid);
883 qboolean CL_VM_CB_LoadEdict(prvm_edict_t *ent)
888 void Cmd_ClearCsqcFuncs (void);
890 // returns true if the packet is valid, false if end of file is reached
891 // used for dumping the CSQC download into demo files
892 qboolean MakeDownloadPacket(const char *filename, unsigned char *data, size_t len, int crc, int cnt, sizebuf_t *buf, int protocol)
894 int packetsize = buf->maxsize - 7; // byte short long
895 int npackets = (len + packetsize - 1) / (packetsize);
897 if(protocol == PROTOCOL_QUAKEWORLD)
898 return false; // CSQC can't run in QW anyway
903 MSG_WriteByte(buf, svc_stufftext);
904 MSG_WriteString(buf, va("\ncl_downloadbegin %lu %s\n", (unsigned long)len, filename));
907 else if(cnt >= 1 && cnt <= npackets)
909 unsigned long thispacketoffset = (cnt - 1) * packetsize;
910 int thispacketsize = len - thispacketoffset;
911 if(thispacketsize > packetsize)
912 thispacketsize = packetsize;
914 MSG_WriteByte(buf, svc_downloaddata);
915 MSG_WriteLong(buf, thispacketoffset);
916 MSG_WriteShort(buf, thispacketsize);
917 SZ_Write(buf, data + thispacketoffset, thispacketsize);
921 else if(cnt == npackets + 1)
923 MSG_WriteByte(buf, svc_stufftext);
924 MSG_WriteString(buf, va("\ncl_downloadfinished %lu %d\n", (unsigned long)len, crc));
930 void CL_VM_Init (void)
932 const char* csprogsfn;
933 unsigned char *csprogsdata;
934 fs_offset_t csprogsdatasize;
935 int csprogsdatacrc, requiredcrc;
938 // reset csqc_progcrc after reading it, so that changing servers doesn't
939 // expect csqc on the next server
940 requiredcrc = csqc_progcrc.integer;
941 requiredsize = csqc_progsize.integer;
942 Cvar_SetValueQuick(&csqc_progcrc, -1);
943 Cvar_SetValueQuick(&csqc_progsize, -1);
945 // if the server is not requesting a csprogs, then we're done here
949 // see if the requested csprogs.dat file matches the requested crc
951 csprogsfn = va("dlcache/%s.%i.%i", csqc_progname.string, requiredsize, requiredcrc);
952 csprogsdata = FS_LoadFile(csprogsfn, tempmempool, true, &csprogsdatasize);
955 csprogsfn = csqc_progname.string;
956 csprogsdata = FS_LoadFile(csprogsfn, tempmempool, true, &csprogsdatasize);
960 csprogsdatacrc = CRC_Block(csprogsdata, (size_t)csprogsdatasize);
961 if (csprogsdatacrc != requiredcrc || csprogsdatasize != requiredsize)
963 if (cls.demoplayback)
965 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);
966 // Mem_Free(csprogsdata);
968 // We WANT to continue here, and play the demo with different csprogs!
969 // After all, this is just a warning. Sure things may go wrong from here.
973 Mem_Free(csprogsdata);
974 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);
982 if (requiredcrc >= 0)
984 if (cls.demoplayback)
985 Con_Printf("CL_VM_Init: demo requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
987 Con_Printf("CL_VM_Init: server requires CSQC, but \"%s\" wasn't found\n", csqc_progname.string);
994 PRVM_InitProg(PRVM_CLIENTPROG);
996 // allocate the mempools
997 prog->progs_mempool = Mem_AllocPool(csqc_progname.string, 0, NULL);
998 prog->headercrc = CL_PROGHEADER_CRC;
999 prog->edictprivate_size = 0; // no private struct used
1000 prog->name = CL_NAME;
1001 prog->num_edicts = 1;
1002 prog->max_edicts = 512;
1003 prog->limit_edicts = CL_MAX_EDICTS;
1004 prog->reserved_edicts = 0;
1005 prog->edictprivate_size = sizeof(edict_engineprivate_t);
1006 // TODO: add a shared extension string #define and add real support for csqc extension strings [12/5/2007 Black]
1007 prog->extensionstring = vm_sv_extensions;
1008 prog->builtins = vm_cl_builtins;
1009 prog->numbuiltins = vm_cl_numbuiltins;
1010 prog->begin_increase_edicts = CL_VM_CB_BeginIncreaseEdicts;
1011 prog->end_increase_edicts = CL_VM_CB_EndIncreaseEdicts;
1012 prog->init_edict = CL_VM_CB_InitEdict;
1013 prog->free_edict = CL_VM_CB_FreeEdict;
1014 prog->count_edicts = CL_VM_CB_CountEdicts;
1015 prog->load_edict = CL_VM_CB_LoadEdict;
1016 prog->init_cmd = VM_CL_Cmd_Init;
1017 prog->reset_cmd = VM_CL_Cmd_Reset;
1018 prog->error_cmd = CL_VM_Error;
1019 prog->ExecuteProgram = CLVM_ExecuteProgram;
1021 PRVM_LoadProgs(csprogsfn, cl_numrequiredfunc, cl_required_func, CL_REQFIELDS, cl_reqfields, CL_REQGLOBALS, cl_reqglobals);
1025 CL_VM_Error("CSQC %s ^2failed to load\n", csprogsfn);
1028 Mem_Free(csprogsdata);
1032 Con_DPrintf("CSQC %s ^5loaded (crc=%i, size=%i)\n", csprogsfn, csprogsdatacrc, (int)csprogsdatasize);
1034 if(cls.demorecording)
1036 if(cls.demo_lastcsprogssize != csprogsdatasize || cls.demo_lastcsprogscrc != csprogsdatacrc)
1039 static char buf[NET_MAXMESSAGE];
1041 unsigned char *demobuf; fs_offset_t demofilesize;
1043 sb.data = (unsigned char *) buf;
1044 sb.maxsize = sizeof(buf);
1047 CL_CutDemo(&demobuf, &demofilesize);
1048 while(MakeDownloadPacket(csqc_progname.string, csprogsdata, (size_t)csprogsdatasize, csprogsdatacrc, i++, &sb, cls.protocol))
1049 CL_WriteDemoMessage(&sb);
1050 CL_PasteDemo(&demobuf, &demofilesize);
1052 cls.demo_lastcsprogssize = csprogsdatasize;
1053 cls.demo_lastcsprogscrc = csprogsdatacrc;
1056 Mem_Free(csprogsdata);
1058 // check if OP_STATE animation is possible in this dat file
1059 if (prog->fieldoffsets.nextthink >= 0 && prog->fieldoffsets.frame >= 0 && prog->fieldoffsets.think >= 0 && prog->globaloffsets.self >= 0)
1060 prog->flag |= PRVM_OP_STATE;
1063 prog->globals.client->time = cl.time;
1064 prog->globals.client->self = 0;
1066 prog->globals.client->mapname = PRVM_SetEngineString(cl.worldname);
1067 prog->globals.client->player_localentnum = cl.playerentity;
1069 // set map description (use world entity 0)
1070 PRVM_clientedictstring(prog->edicts, message) = PRVM_SetEngineString(cl.worldmessage);
1071 VectorCopy(cl.world.mins, prog->edicts->fields.client->mins);
1072 VectorCopy(cl.world.maxs, prog->edicts->fields.client->maxs);
1074 // call the prog init
1075 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Init, "QC function CSQC_Init is missing");
1078 cl.csqc_loaded = true;
1080 cl.csqc_vidvars.drawcrosshair = false;
1081 cl.csqc_vidvars.drawenginesbar = false;
1083 // Update Coop and Deathmatch Globals (at this point the client knows them from ServerInfo)
1084 CL_VM_UpdateCoopDeathmatchGlobals(cl.gametype);
1087 void CL_VM_ShutDown (void)
1089 Cmd_ClearCsqcFuncs();
1090 //Cvar_SetValueQuick(&csqc_progcrc, -1);
1091 //Cvar_SetValueQuick(&csqc_progsize, -1);
1095 prog->globals.client->time = cl.time;
1096 prog->globals.client->self = 0;
1097 if (prog->funcoffsets.CSQC_Shutdown)
1098 PRVM_ExecuteProgram(prog->funcoffsets.CSQC_Shutdown, "QC function CSQC_Shutdown is missing");
1101 Con_DPrint("CSQC ^1unloaded\n");
1102 cl.csqc_loaded = false;
1105 qboolean CL_VM_GetEntitySoundOrigin(int entnum, vec3_t out)
1114 // FIXME consider attachments here!
1116 ed = PRVM_EDICT_NUM(entnum - MAX_EDICTS);
1118 if(!ed->priv.required->free)
1120 mod = CL_GetModelFromEdict(ed);
1121 VectorCopy(ed->fields.client->origin, out);
1122 if(CL_GetTagMatrix (&matrix, ed, 0) == 0)
1123 Matrix4x4_OriginFromMatrix(&matrix, out);
1124 if (mod && mod->soundfromcenter)
1125 VectorMAMAM(1.0f, out, 0.5f, mod->normalmins, 0.5f, mod->normalmaxs, out);
1134 qboolean CL_VM_TransformView(int entnum, matrix4x4_t *viewmatrix, mplane_t *clipplane, vec3_t visorigin)
1136 qboolean ret = false;
1138 vec3_t forward, left, up, origin, ang;
1139 matrix4x4_t mat, matq;
1142 ed = PRVM_EDICT_NUM(entnum);
1145 if(PRVM_clientedictfunction(ed, camera_transform))
1148 if(viewmatrix || clipplane || visorigin)
1150 Matrix4x4_ToVectors(viewmatrix, forward, left, up, origin);
1151 AnglesFromVectors(ang, forward, up, false);
1152 prog->globals.client->time = cl.time;
1153 prog->globals.client->self = entnum;
1154 VectorCopy(origin, PRVM_G_VECTOR(OFS_PARM0));
1155 VectorCopy(ang, PRVM_G_VECTOR(OFS_PARM1));
1156 VectorCopy(forward, PRVM_clientglobalvector(v_forward));
1157 VectorScale(left, -1, PRVM_clientglobalvector(v_right));
1158 VectorCopy(up, PRVM_clientglobalvector(v_up));
1159 VectorCopy(origin, PRVM_clientglobalvector(trace_endpos));
1160 PRVM_ExecuteProgram(PRVM_clientedictfunction(ed, camera_transform), "QC function e.camera_transform is missing");
1161 VectorCopy(PRVM_G_VECTOR(OFS_RETURN), origin);
1162 VectorCopy(PRVM_clientglobalvector(v_forward), forward);
1163 VectorScale(PRVM_clientglobalvector(v_right), -1, left);
1164 VectorCopy(PRVM_clientglobalvector(v_up), up);
1165 VectorCopy(PRVM_clientglobalvector(trace_endpos), visorigin);
1166 Matrix4x4_Invert_Full(&mat, viewmatrix);
1167 Matrix4x4_FromVectors(viewmatrix, forward, left, up, origin);
1168 Matrix4x4_Concat(&matq, viewmatrix, &mat);
1169 Matrix4x4_TransformPositivePlane(&matq, clipplane->normal[0], clipplane->normal[1], clipplane->normal[2], clipplane->dist, &clipplane->normal[0]);