2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 static prvm_prog_t prog_list[PRVM_MAXPROGS];
29 int prvm_type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4};
31 ddef_t *PRVM_ED_FieldAtOfs(int ofs);
32 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash);
34 // LordHavoc: prints every opcode as it executes - warning: this is significant spew
35 cvar_t prvm_traceqc = {0, "prvm_traceqc", "0", "prints every QuakeC statement as it is executed (only for really thorough debugging!)"};
36 // LordHavoc: counts usage of each QuakeC statement
37 cvar_t prvm_statementprofiling = {0, "prvm_statementprofiling", "0", "counts how many times each QuakeC statement has been executed, these counts are displayed in prvm_printfunction output (if enabled)"};
38 cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print a backtrace for warnings too"};
39 cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"};
40 cvar_t prvm_leaktest_ignore_classnames = {0, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"};
41 cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"};
43 qboolean prvm_runawaycheck = true;
45 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
46 // enables detection of out of bounds memory access in the QuakeC code being run (in other words, prevents really exceedingly bad QuakeC code from doing nasty things to your computer)
47 qboolean prvm_boundscheck = true;
49 extern sizebuf_t vm_tempstringsbuf;
51 //============================================================================
59 void PRVM_MEM_Alloc(void)
63 // reserve space for the null entity aka world
64 // check bound of max_edicts
65 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
66 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
68 // edictprivate_size has to be min as big prvm_edict_private_t
69 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
72 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
74 // alloc edict private space
75 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
78 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
81 for(i = 0; i < prog->max_edicts; i++)
83 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
84 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
90 PRVM_MEM_IncreaseEdicts
93 void PRVM_MEM_IncreaseEdicts(void)
96 int oldmaxedicts = prog->max_edicts;
97 void *oldedictsfields = prog->edictsfields;
98 void *oldedictprivate = prog->edictprivate;
100 if(prog->max_edicts >= prog->limit_edicts)
103 PRVM_GCALL(begin_increase_edicts)();
106 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
108 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
109 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
111 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
112 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
114 //set e and v pointers
115 for(i = 0; i < prog->max_edicts; i++)
117 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
118 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
121 PRVM_GCALL(end_increase_edicts)();
123 Mem_Free(oldedictsfields);
124 Mem_Free(oldedictprivate);
127 //============================================================================
130 int PRVM_ED_FindFieldOffset(const char *field)
133 d = PRVM_ED_FindField(field);
139 int PRVM_ED_FindGlobalOffset(const char *global)
142 d = PRVM_ED_FindGlobal(global);
148 func_t PRVM_ED_FindFunctionOffset(const char *function)
151 f = PRVM_ED_FindFunction(function);
154 return (func_t)(f - prog->functions);
157 qboolean PRVM_ProgLoaded(int prognr)
159 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
162 return (prog_list[prognr].loaded ? TRUE : FALSE);
167 PRVM_SetProgFromString
170 // perhaps add a return value when the str doesnt exist
171 qboolean PRVM_SetProgFromString(const char *str)
174 for(; i < PRVM_MAXPROGS ; i++)
175 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
177 if(prog_list[i].loaded)
179 prog = &prog_list[i];
184 Con_Printf("%s not loaded !\n",PRVM_NAME);
189 Con_Printf("Invalid program name %s !\n", str);
198 void PRVM_SetProg(int prognr)
200 if(0 <= prognr && prognr < PRVM_MAXPROGS)
202 if(prog_list[prognr].loaded)
203 prog = &prog_list[prognr];
205 PRVM_ERROR("%i not loaded !", prognr);
208 PRVM_ERROR("Invalid program number %i", prognr);
215 Sets everything to NULL
218 void PRVM_ED_ClearEdict (prvm_edict_t *e)
220 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
221 e->priv.required->free = false;
223 // AK: Let the init_edict function determine if something needs to be initialized
224 PRVM_GCALL(init_edict)(e);
227 const char *PRVM_AllocationOrigin()
230 if(prog->leaktest_active)
231 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
233 buf = (char *)PRVM_Alloc(128);
234 PRVM_ShortStackTrace(buf, 128);
243 Either finds a free edict, or allocates a new one.
244 Try to avoid reusing an entity that was recently freed, because it
245 can cause the client to think the entity morphed into something else
246 instead of being removed and recreated, which can cause interpolated
247 angles and bad trails.
250 prvm_edict_t *PRVM_ED_Alloc (void)
255 // the client qc dont need maxclients
256 // thus it doesnt need to use svs.maxclients
257 // AK: changed i=svs.maxclients+1
258 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
259 // although the menu/client has no world
260 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
262 e = PRVM_EDICT_NUM(i);
263 // the first couple seconds of server time can involve a lot of
264 // freeing and allocating, so relax the replacement policy
265 if (e->priv.required->free && ( e->priv.required->freetime < prog->starttime + 2 || (realtime - e->priv.required->freetime) > 1 ) )
267 PRVM_ED_ClearEdict (e);
268 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
273 if (i == prog->limit_edicts)
274 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
277 if (prog->num_edicts >= prog->max_edicts)
278 PRVM_MEM_IncreaseEdicts();
280 e = PRVM_EDICT_NUM(i);
281 PRVM_ED_ClearEdict (e);
283 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
292 Marks the edict as free
293 FIXME: walk all entities and NULL out references to this entity
296 void PRVM_ED_Free (prvm_edict_t *ed)
298 // dont delete the null entity (world) or reserved edicts
299 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
302 PRVM_GCALL(free_edict)(ed);
304 ed->priv.required->free = true;
305 ed->priv.required->freetime = realtime;
306 if(ed->priv.required->allocation_origin)
308 PRVM_Free((char *)ed->priv.required->allocation_origin);
309 ed->priv.required->allocation_origin = NULL;
313 //===========================================================================
320 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
325 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
327 def = &prog->globaldefs[i];
339 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
344 for (i=0 ; i<prog->progs->numfielddefs ; i++)
346 def = &prog->fielddefs[i];
358 ddef_t *PRVM_ED_FindField (const char *name)
363 for (i=0 ; i<prog->progs->numfielddefs ; i++)
365 def = &prog->fielddefs[i];
366 if (!strcmp(PRVM_GetString(def->s_name), name))
377 ddef_t *PRVM_ED_FindGlobal (const char *name)
382 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
384 def = &prog->globaldefs[i];
385 if (!strcmp(PRVM_GetString(def->s_name), name))
397 mfunction_t *PRVM_ED_FindFunction (const char *name)
402 for (i=0 ; i<prog->progs->numfunctions ; i++)
404 func = &prog->functions[i];
405 if (!strcmp(PRVM_GetString(func->s_name), name))
416 Returns a string describing *data in a type specific manner
419 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
421 static char line[MAX_INPUTLINE];
426 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
431 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
435 if (n < 0 || n >= prog->limit_edicts)
436 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
438 dpsnprintf (line, sizeof(line), "entity %i", n);
441 f = prog->functions + val->function;
442 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
445 def = PRVM_ED_FieldAtOfs ( val->_int );
446 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
449 dpsnprintf (line, sizeof(line), "void");
452 // LordHavoc: changed from %5.1f to %10.4f
453 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
456 // LordHavoc: changed from %5.1f to %10.4f
457 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
460 dpsnprintf (line, sizeof(line), "pointer");
463 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
474 Returns a string describing *data in a type specific manner
475 Easier to parse than PR_ValueString
478 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
480 static char line[MAX_INPUTLINE];
486 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
491 // Parse the string a bit to turn special characters
492 // (like newline, specifically) into escape codes,
493 // this fixes saving games from various mods
494 s = PRVM_GetString (val->string);
495 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
524 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
527 f = prog->functions + val->function;
528 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
531 def = PRVM_ED_FieldAtOfs ( val->_int );
532 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
535 dpsnprintf (line, sizeof (line), "void");
538 dpsnprintf (line, sizeof (line), "%f", val->_float);
541 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
544 dpsnprintf (line, sizeof (line), "bad type %i", type);
555 Returns a string with a description and the contents of a global,
556 padded to 20 field width
559 char *PRVM_GlobalString (int ofs)
565 static char line[128];
567 val = (void *)&prog->globals.generic[ofs];
568 def = PRVM_ED_GlobalAtOfs(ofs);
570 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
573 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
574 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
578 //for ( ; i<20 ; i++)
579 // strcat (line," ");
585 char *PRVM_GlobalStringNoContents (int ofs)
589 static char line[128];
591 def = PRVM_ED_GlobalAtOfs(ofs);
593 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
595 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
598 //for ( ; i<20 ; i++)
599 // strcat (line," ");
613 // LordHavoc: optimized this to print out much more quickly (tempstring)
614 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
615 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
623 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
625 if (ed->priv.required->free)
627 Con_Printf("%s: FREE\n",PRVM_NAME);
632 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
633 for (i=1 ; i<prog->progs->numfielddefs ; i++)
635 d = &prog->fielddefs[i];
636 name = PRVM_GetString(d->s_name);
637 if (name[strlen(name)-2] == '_')
638 continue; // skip _x, _y, _z vars
640 // Check Field Name Wildcard
641 if(wildcard_fieldname)
642 if( !matchpattern(name, wildcard_fieldname, 1) )
643 // Didn't match; skip
646 v = (int *)((char *)ed->fields.vp + d->ofs*4);
648 // if the value is still all 0, skip the field
649 type = d->type & ~DEF_SAVEGLOBAL;
651 for (j=0 ; j<prvm_type_size[type] ; j++)
654 if (j == prvm_type_size[type])
657 if (strlen(name) > sizeof(tempstring2)-4)
659 memcpy (tempstring2, name, sizeof(tempstring2)-4);
660 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
661 tempstring2[sizeof(tempstring2)-1] = 0;
664 strlcat(tempstring, name, sizeof(tempstring));
665 for (l = strlen(name);l < 14;l++)
666 strlcat(tempstring, " ", sizeof(tempstring));
667 strlcat(tempstring, " ", sizeof(tempstring));
669 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
670 if (strlen(name) > sizeof(tempstring2)-4)
672 memcpy (tempstring2, name, sizeof(tempstring2)-4);
673 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
674 tempstring2[sizeof(tempstring2)-1] = 0;
677 strlcat(tempstring, name, sizeof(tempstring));
678 strlcat(tempstring, "\n", sizeof(tempstring));
679 if (strlen(tempstring) >= sizeof(tempstring)/2)
681 Con_Print(tempstring);
686 Con_Print(tempstring);
696 extern cvar_t developer_entityparsing;
697 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
707 if (ed->priv.required->free)
713 for (i=1 ; i<prog->progs->numfielddefs ; i++)
715 d = &prog->fielddefs[i];
716 name = PRVM_GetString(d->s_name);
718 if(developer_entityparsing.integer)
719 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
721 if (name[strlen(name)-2] == '_')
722 continue; // skip _x, _y, _z vars
724 v = (int *)((char *)ed->fields.vp + d->ofs*4);
726 // if the value is still all 0, skip the field
727 type = d->type & ~DEF_SAVEGLOBAL;
728 for (j=0 ; j<prvm_type_size[type] ; j++)
731 if (j == prvm_type_size[type])
734 FS_Printf(f,"\"%s\" ",name);
735 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
736 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
737 prog->statestring = NULL;
743 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
745 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
750 PRVM_ED_PrintEdicts_f
752 For debugging, prints all the entities in the current server
755 void PRVM_ED_PrintEdicts_f (void)
758 const char *wildcard_fieldname;
760 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
762 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
767 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
771 wildcard_fieldname = Cmd_Argv(2);
773 wildcard_fieldname = NULL;
775 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
776 for (i=0 ; i<prog->num_edicts ; i++)
777 PRVM_ED_PrintNum (i, wildcard_fieldname);
786 For debugging, prints a single edict
789 void PRVM_ED_PrintEdict_f (void)
792 const char *wildcard_fieldname;
794 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
796 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
801 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
804 i = atoi (Cmd_Argv(2));
805 if (i >= prog->num_edicts)
807 Con_Print("Bad edict number\n");
812 // Optional Wildcard Provided
813 wildcard_fieldname = Cmd_Argv(3);
816 wildcard_fieldname = NULL;
817 PRVM_ED_PrintNum (i, wildcard_fieldname);
829 // 2 possibilities : 1. just displaying the active edict count
830 // 2. making a function pointer [x]
831 void PRVM_ED_Count_f (void)
839 Con_Print("prvm_count <program name>\n");
844 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
847 if(prog->count_edicts)
848 prog->count_edicts();
852 for (i=0 ; i<prog->num_edicts ; i++)
854 ent = PRVM_EDICT_NUM(i);
855 if (ent->priv.required->free)
860 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
861 Con_Printf("active :%3i\n", active);
868 ==============================================================================
872 FIXME: need to tag constants, doesn't really work
873 ==============================================================================
881 void PRVM_ED_WriteGlobals (qfile_t *f)
889 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
891 def = &prog->globaldefs[i];
893 if ( !(def->type & DEF_SAVEGLOBAL) )
895 type &= ~DEF_SAVEGLOBAL;
897 if (type != ev_string && type != ev_float && type != ev_entity)
900 name = PRVM_GetString(def->s_name);
902 if(developer_entityparsing.integer)
903 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
905 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
906 FS_Printf(f,"\"%s\" ", name);
907 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
908 prog->statestring = NULL;
918 void PRVM_ED_ParseGlobals (const char *data)
920 char keyname[MAX_INPUTLINE];
926 if (!COM_ParseToken_Simple(&data, false, false))
927 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
928 if (com_token[0] == '}')
931 if (developer_entityparsing.integer)
932 Con_Printf("Key: \"%s\"", com_token);
934 strlcpy (keyname, com_token, sizeof(keyname));
937 if (!COM_ParseToken_Simple(&data, false, true))
938 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
940 if (developer_entityparsing.integer)
941 Con_Printf(" \"%s\"\n", com_token);
943 if (com_token[0] == '}')
944 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
946 key = PRVM_ED_FindGlobal (keyname);
949 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
953 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
954 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
958 //============================================================================
965 Can parse either fields or globals
966 returns false if error
969 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
978 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
980 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
981 switch (key->type & ~DEF_SAVEGLOBAL)
984 l = (int)strlen(s) + 1;
985 val->string = PRVM_AllocString(l, &new_p);
986 for (i = 0;i < l;i++)
988 if (s[i] == '\\' && s[i+1] && parsebackslash)
993 else if (s[i] == 'r')
1004 while (*s && ISWHITESPACE(*s))
1006 val->_float = atof(s);
1010 for (i = 0;i < 3;i++)
1012 while (*s && ISWHITESPACE(*s))
1016 val->vector[i] = atof(s);
1017 while (!ISWHITESPACE(*s))
1025 while (*s && ISWHITESPACE(*s))
1028 if (i >= prog->limit_edicts)
1029 Con_Printf("PRVM_ED_ParseEpair: ev_entity reference too large (edict %u >= MAX_EDICTS %u) on %s\n", (unsigned int)i, (unsigned int)MAX_EDICTS, PRVM_NAME);
1030 while (i >= prog->max_edicts)
1031 PRVM_MEM_IncreaseEdicts();
1032 // if IncreaseEdicts was called the base pointer needs to be updated
1034 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1035 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1041 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1044 def = PRVM_ED_FindField(s + 1);
1047 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1050 val->_int = def->ofs;
1054 func = PRVM_ED_FindFunction(s);
1057 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1060 val->function = func - prog->functions;
1064 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1074 Console command to send a string to QC function GameCommand of the
1078 sv_cmd adminmsg 3 "do not teamkill"
1079 cl_cmd someclientcommand
1080 menu_cmd somemenucommand
1082 All progs can support this extension; sg calls it in server QC, cg in client
1086 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1090 Con_Printf("%s text...\n", whichcmd);
1095 if(!PRVM_SetProgFromString(whichprogs))
1096 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1097 // also, it makes printing error messages easier!
1099 Con_Printf("%s program not loaded.\n", whichprogs);
1103 if(!prog->funcoffsets.GameCommand)
1105 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1109 int restorevm_tempstringsbuf_cursize;
1114 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1115 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1116 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1117 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1122 void PRVM_GameCommand_Server_f(void)
1124 PRVM_GameCommand("server", "sv_cmd");
1126 void PRVM_GameCommand_Client_f(void)
1128 PRVM_GameCommand("client", "cl_cmd");
1130 void PRVM_GameCommand_Menu_f(void)
1132 PRVM_GameCommand("menu", "menu_cmd");
1139 Console command to set a field of a specified edict
1142 void PRVM_ED_EdictSet_f(void)
1149 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1154 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1156 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1160 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1162 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1163 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1165 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1171 ====================
1174 Parses an edict out of the given string, returning the new position
1175 ed should be a properly initialized empty edict.
1176 Used for initial level load and for savegames.
1177 ====================
1179 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1189 // go through all the dictionary pairs
1193 if (!COM_ParseToken_Simple(&data, false, false))
1194 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1195 if (developer_entityparsing.integer)
1196 Con_Printf("Key: \"%s\"", com_token);
1197 if (com_token[0] == '}')
1200 // anglehack is to allow QuakeEd to write single scalar angles
1201 // and allow them to be turned into vectors. (FIXME...)
1202 if (!strcmp(com_token, "angle"))
1204 strlcpy (com_token, "angles", sizeof(com_token));
1210 // FIXME: change light to _light to get rid of this hack
1211 if (!strcmp(com_token, "light"))
1212 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1214 strlcpy (keyname, com_token, sizeof(keyname));
1216 // another hack to fix keynames with trailing spaces
1217 n = strlen(keyname);
1218 while (n && keyname[n-1] == ' ')
1225 if (!COM_ParseToken_Simple(&data, false, false))
1226 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1227 if (developer_entityparsing.integer)
1228 Con_Printf(" \"%s\"\n", com_token);
1230 if (com_token[0] == '}')
1231 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1235 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1239 // keynames with a leading underscore are used for utility comments,
1240 // and are immediately discarded by quake
1241 if (keyname[0] == '_')
1244 key = PRVM_ED_FindField (keyname);
1247 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1254 strlcpy (temp, com_token, sizeof(temp));
1255 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1258 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1259 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1263 ent->priv.required->free = true;
1271 PRVM_ED_LoadFromFile
1273 The entities are directly placed in the array, rather than allocated with
1274 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1275 number references out of order.
1277 Creates a server's entity / program execution context by
1278 parsing textual entity definitions out of an ent file.
1280 Used for both fresh maps and savegame loads. A fresh map would also need
1281 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1284 void PRVM_ED_LoadFromFile (const char *data)
1287 int parsed, inhibited, spawned, died;
1288 const char *funcname;
1300 // parse the opening brace
1301 if (!COM_ParseToken_Simple(&data, false, false))
1303 if (com_token[0] != '{')
1304 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1306 // CHANGED: this is not conform to PR_LoadFromFile
1307 if(prog->loadintoworld)
1309 prog->loadintoworld = false;
1310 ent = PRVM_EDICT_NUM(0);
1313 ent = PRVM_ED_Alloc();
1316 if (ent != prog->edicts) // hack
1317 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1319 data = PRVM_ED_ParseEdict (data, ent);
1322 // remove the entity ?
1323 if(prog->load_edict && !prog->load_edict(ent))
1330 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1333 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1334 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1337 if(ent->priv.required->free)
1344 // immediately call spawn function, but only if there is a self global and a classname
1346 if(!ent->priv.required->free)
1347 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1349 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1352 Con_Print("No classname for:\n");
1353 PRVM_ED_Print(ent, NULL);
1358 // look for the spawn function
1359 funcname = PRVM_GetString(handle);
1360 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1362 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1363 func = PRVM_ED_FindFunction (funcname);
1367 // check for OnEntityNoSpawnFunction
1368 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1371 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1372 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1376 if (developer.integer) // don't confuse non-developers with errors
1378 Con_Print("No spawn function for:\n");
1379 PRVM_ED_Print(ent, NULL);
1382 continue; // not included in "inhibited" count
1388 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1389 PRVM_ExecuteProgram (func - prog->functions, "");
1393 if(!ent->priv.required->free)
1394 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1397 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1398 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1402 if (ent->priv.required->free)
1406 Con_DPrintf("%s: %i new entities parsed, %i new inhibited, %i (%i new) spawned (whereas %i removed self, %i stayed)\n", PRVM_NAME, parsed, inhibited, prog->num_edicts, spawned, died, spawned - died);
1409 void PRVM_FindOffsets(void)
1411 // field and global searches use -1 for NULL
1412 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1413 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1414 // functions use 0 for NULL
1415 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1417 // server and client qc use a lot of similar fields, so this is combined
1418 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1419 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1420 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1421 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1422 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1423 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1424 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1425 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1426 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1427 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1428 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1429 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1430 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1431 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1432 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1433 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1434 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1435 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1436 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1437 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1438 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1439 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1440 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1441 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1442 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1443 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1444 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1445 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1446 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1447 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1448 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1449 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1450 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1451 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1452 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1453 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1454 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1455 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1456 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1457 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1458 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1459 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1460 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1461 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1462 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1463 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1464 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1465 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1466 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1467 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1468 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1469 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1470 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1471 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1472 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1473 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1474 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1475 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1476 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1477 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1478 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1479 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1480 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1481 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1482 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1483 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1484 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1485 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1486 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1487 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1488 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1489 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1490 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1491 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1492 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1493 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1494 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1495 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1496 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1497 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1498 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1499 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1500 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1501 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1502 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1503 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1504 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1505 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1506 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1507 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1508 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1509 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1510 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1511 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1512 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1513 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1514 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1515 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1516 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1517 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1518 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1519 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1520 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1521 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1522 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1523 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1524 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1525 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1526 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1527 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1528 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1529 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1530 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1531 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1532 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1533 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1534 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1535 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1536 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1537 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1538 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1539 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1540 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1541 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1542 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1543 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1544 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1545 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1546 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1547 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1548 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1549 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1550 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1551 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1552 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1553 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1554 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1555 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1556 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1557 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1558 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1559 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1560 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1561 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1562 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1563 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1564 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1565 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1566 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1567 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1568 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1569 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1570 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1571 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1572 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1573 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1574 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1575 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1576 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1577 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1578 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1579 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1580 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1581 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1582 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1584 // menu qc only uses some functions, nothing else
1585 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1586 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1587 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1588 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1589 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1590 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1595 typedef struct dpfield_s
1602 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1604 dpfield_t dpfields[] =
1615 void PRVM_LeakTest();
1616 void PRVM_ResetProg()
1619 PRVM_GCALL(reset_cmd)();
1620 Mem_FreePool(&prog->progs_mempool);
1621 memset(prog,0,sizeof(prvm_prog_t));
1622 prog->starttime = Sys_DoubleTime();
1630 void PRVM_LoadLNO( const char *progname ) {
1631 fs_offset_t filesize;
1633 unsigned int *header;
1636 FS_StripExtension( progname, filename, sizeof( filename ) );
1637 strlcat( filename, ".lno", sizeof( filename ) );
1639 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1645 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1646 <Spike> SafeWrite (h, &version, sizeof(int));
1647 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1648 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1649 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1650 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1651 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1653 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1658 header = (unsigned int *) lno;
1659 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1660 LittleLong( header[ 1 ] ) == 1 &&
1661 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1662 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1663 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1664 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1666 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1667 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1677 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1681 ddef_t *infielddefs;
1682 dfunction_t *dfunctions;
1683 fs_offset_t filesize;
1685 if( prog->loaded ) {
1686 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1689 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1690 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1691 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1693 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1695 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1697 // byte swap the header
1698 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1699 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1701 if (prog->progs->version != PROG_VERSION)
1702 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1703 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1704 PRVM_ERROR ("%s: %s system vars have been modified (CRC of progs.dat systemvars %i != engine %i), progdefs.h is out of date", PRVM_NAME, filename, prog->progs->crc, prog->headercrc);
1706 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1707 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1709 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1710 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1711 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1712 prog->stringssize = prog->progs->numstrings;
1714 prog->numknownstrings = 0;
1715 prog->maxknownstrings = 0;
1716 prog->knownstrings = NULL;
1717 prog->knownstrings_freeable = NULL;
1719 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1721 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1723 // we need to expand the fielddefs list to include all the engine fields,
1724 // so allocate a new place for it
1725 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1727 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1729 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1731 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1733 // moved edict_size calculation down below field adding code
1735 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1736 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1738 // byte swap the lumps
1739 for (i=0 ; i<prog->progs->numstatements ; i++)
1741 prog->statements[i].op = LittleShort(prog->statements[i].op);
1742 prog->statements[i].a = LittleShort(prog->statements[i].a);
1743 prog->statements[i].b = LittleShort(prog->statements[i].b);
1744 prog->statements[i].c = LittleShort(prog->statements[i].c);
1747 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1748 for (i = 0;i < prog->progs->numfunctions;i++)
1750 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1751 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1752 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1753 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1754 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1755 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1756 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1759 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1761 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1762 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1763 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1766 // copy the progs fields to the new fields list
1767 for (i = 0;i < prog->progs->numfielddefs;i++)
1769 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1770 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1771 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1772 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1773 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1776 // append the required fields
1777 for (i = 0;i < (int) numrequiredfields;i++)
1779 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1780 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1781 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1782 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1783 prog->progs->entityfields += 3;
1785 prog->progs->entityfields++;
1786 prog->progs->numfielddefs++;
1789 // check required functions
1790 for(i=0 ; i < numrequiredfunc ; i++)
1791 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1792 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1794 // check required globals
1795 for(i=0 ; i < numrequiredglobals ; i++)
1796 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1797 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1799 for (i=0 ; i<prog->progs->numglobals ; i++)
1800 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1802 // moved edict_size calculation down here, below field adding code
1803 // LordHavoc: this no longer includes the prvm_edict_t header
1804 prog->edict_size = prog->progs->entityfields * 4;
1805 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1807 // LordHavoc: bounds check anything static
1808 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1814 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1815 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1818 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1819 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1821 // global global global
1856 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1857 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1859 // global none global
1865 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1866 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1882 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
1883 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1897 if ((unsigned short) st->a >= prog->progs->numglobals)
1898 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
1901 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
1906 PRVM_LoadLNO(filename);
1910 prog->loaded = TRUE;
1912 // set flags & ddef_ts in prog
1918 PRVM_GCALL(init_cmd)();
1925 void PRVM_Fields_f (void)
1927 int i, j, ednum, used, usedamount;
1929 char tempstring[MAX_INPUTLINE], tempstring2[260];
1939 Con_Print("no progs loaded\n");
1946 Con_Print("prvm_fields <program name>\n");
1951 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1954 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
1955 for (ednum = 0;ednum < prog->max_edicts;ednum++)
1957 ed = PRVM_EDICT_NUM(ednum);
1958 if (ed->priv.required->free)
1960 for (i = 1;i < prog->progs->numfielddefs;i++)
1962 d = &prog->fielddefs[i];
1963 name = PRVM_GetString(d->s_name);
1964 if (name[strlen(name)-2] == '_')
1965 continue; // skip _x, _y, _z vars
1966 v = (int *)((char *)ed->fields.vp + d->ofs*4);
1967 // if the value is still all 0, skip the field
1968 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
1981 for (i = 0;i < prog->progs->numfielddefs;i++)
1983 d = &prog->fielddefs[i];
1984 name = PRVM_GetString(d->s_name);
1985 if (name[strlen(name)-2] == '_')
1986 continue; // skip _x, _y, _z vars
1987 switch(d->type & ~DEF_SAVEGLOBAL)
1990 strlcat(tempstring, "string ", sizeof(tempstring));
1993 strlcat(tempstring, "entity ", sizeof(tempstring));
1996 strlcat(tempstring, "function ", sizeof(tempstring));
1999 strlcat(tempstring, "field ", sizeof(tempstring));
2002 strlcat(tempstring, "void ", sizeof(tempstring));
2005 strlcat(tempstring, "float ", sizeof(tempstring));
2008 strlcat(tempstring, "vector ", sizeof(tempstring));
2011 strlcat(tempstring, "pointer ", sizeof(tempstring));
2014 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2015 strlcat(tempstring, tempstring2, sizeof(tempstring));
2018 if (strlen(name) > sizeof(tempstring2)-4)
2020 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2021 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2022 tempstring2[sizeof(tempstring2)-1] = 0;
2025 strlcat(tempstring, name, sizeof(tempstring));
2026 for (j = (int)strlen(name);j < 25;j++)
2027 strlcat(tempstring, " ", sizeof(tempstring));
2028 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2029 strlcat(tempstring, tempstring2, sizeof(tempstring));
2030 strlcat(tempstring, "\n", sizeof(tempstring));
2031 if (strlen(tempstring) >= sizeof(tempstring)/2)
2033 Con_Print(tempstring);
2039 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2043 Con_Printf("%s: %i entity fields (%i in use), totalling %i bytes per edict (%i in use), %i edicts allocated, %i bytes total spent on edict fields (%i needed)\n", PRVM_NAME, prog->progs->entityfields, used, prog->progs->entityfields * 4, usedamount * 4, prog->max_edicts, prog->progs->entityfields * 4 * prog->max_edicts, usedamount * 4 * prog->max_edicts);
2048 void PRVM_Globals_f (void)
2051 const char *wildcard;
2057 Con_Print("no progs loaded\n");
2060 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2062 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2067 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2070 if( Cmd_Argc() == 3)
2071 wildcard = Cmd_Argv(2);
2075 Con_Printf("%s :", PRVM_NAME);
2077 for (i = 0;i < prog->progs->numglobaldefs;i++)
2080 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2085 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2087 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2097 void PRVM_Global_f(void)
2100 if( Cmd_Argc() != 3 ) {
2101 Con_Printf( "prvm_global <program name> <global name>\n" );
2106 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2109 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2111 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2113 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2122 void PRVM_GlobalSet_f(void)
2125 if( Cmd_Argc() != 4 ) {
2126 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2131 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2134 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2136 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2138 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2147 void PRVM_Init (void)
2149 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2150 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2151 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2152 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2153 Cmd_AddCommand ("prvm_callprofile", PRVM_CallProfile_f, "prints execution statistics about the most time consuming QuakeC calls from the engine in the selected VM (server, client, menu)");
2154 Cmd_AddCommand ("prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
2155 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2156 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2157 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2158 Cmd_AddCommand ("prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, menu)");
2159 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2160 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2161 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2162 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2164 // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2165 prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2167 Cvar_RegisterVariable (&prvm_traceqc);
2168 Cvar_RegisterVariable (&prvm_statementprofiling);
2169 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2170 Cvar_RegisterVariable (&prvm_leaktest);
2171 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2172 Cvar_RegisterVariable (&prvm_errordump);
2174 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2175 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2185 void PRVM_InitProg(int prognr)
2187 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2188 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2190 prog = &prog_list[prognr];
2195 memset(prog, 0, sizeof(prvm_prog_t));
2196 prog->starttime = Sys_DoubleTime();
2198 prog->error_cmd = Host_Error;
2199 prog->leaktest_active = prvm_leaktest.integer;
2202 int PRVM_GetProgNr()
2204 return prog - prog_list;
2207 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2209 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2212 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2214 _Mem_Free(buffer, filename, fileline);
2217 void _PRVM_FreeAll(const char *filename, int fileline)
2220 prog->fielddefs = NULL;
2221 prog->functions = NULL;
2222 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2225 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2226 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2228 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2233 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2235 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2239 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2242 n = e - prog->edicts;
2243 if ((unsigned int)n >= prog->limit_edicts)
2244 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2248 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2250 // return e - prog->edicts;
2253 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2254 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2255 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2258 n = e - prog->edicts;
2259 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2260 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2261 return n;// EXPERIMENTAL
2262 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2264 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2266 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2267 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2268 return prog->edicts + n; // EXPERIMENTAL
2269 //return prog->edicts + ((n) / (progs->entityfields * 4));
2274 sizebuf_t vm_tempstringsbuf;
2276 const char *PRVM_GetString(int num)
2280 if (num < prog->stringssize)
2281 return prog->strings + num;
2284 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2286 num -= prog->stringssize;
2287 if (num < vm_tempstringsbuf.cursize)
2288 return (char *)vm_tempstringsbuf.data + num;
2291 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2298 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2308 // special range reserved for tempstrings
2310 if (num < vm_tempstringsbuf.cursize)
2311 return (char *)vm_tempstringsbuf.data + num;
2314 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2320 if (num < prog->numknownstrings)
2322 if (!prog->knownstrings[num])
2323 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2324 return prog->knownstrings[num];
2328 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2334 int PRVM_SetEngineString(const char *s)
2339 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2340 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2341 // if it's in the tempstrings area, use a reserved range
2342 // (otherwise we'd get millions of useless string offsets cluttering the database)
2343 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2345 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2347 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2349 // see if it's a known string address
2350 for (i = 0;i < prog->numknownstrings;i++)
2351 if (prog->knownstrings[i] == s)
2353 // new unknown engine string
2354 if (developer.integer >= 200)
2355 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2356 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2357 if (!prog->knownstrings[i])
2359 if (i >= prog->numknownstrings)
2361 if (i >= prog->maxknownstrings)
2363 const char **oldstrings = prog->knownstrings;
2364 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2365 const char **oldstrings_origin = prog->knownstrings_origin;
2366 prog->maxknownstrings += 128;
2367 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2368 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2369 if(prog->leaktest_active)
2370 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2371 if (prog->numknownstrings)
2373 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2374 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2375 if(prog->leaktest_active)
2376 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2379 prog->numknownstrings++;
2381 prog->firstfreeknownstring = i + 1;
2382 prog->knownstrings[i] = s;
2383 prog->knownstrings_freeable[i] = false;
2384 if(prog->leaktest_active)
2385 prog->knownstrings_origin[i] = NULL;
2389 // temp string handling
2391 // all tempstrings go into this buffer consecutively, and it is reset
2392 // whenever PRVM_ExecuteProgram returns to the engine
2393 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2394 // restores it on return, so multiple recursive calls can share the same
2396 // the buffer size is automatically grown as needed
2398 int PRVM_SetTempString(const char *s)
2404 size = (int)strlen(s) + 1;
2405 if (developer.integer >= 300)
2406 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2407 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2409 sizebuf_t old = vm_tempstringsbuf;
2410 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2411 PRVM_ERROR("PRVM_SetTempString: ran out of tempstring memory! (refusing to grow tempstring buffer over 256MB, cursize %i, size %i)\n", vm_tempstringsbuf.cursize, size);
2412 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2413 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2414 vm_tempstringsbuf.maxsize *= 2;
2415 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2417 if (developer.integer >= 100)
2418 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2419 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2421 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2426 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2428 vm_tempstringsbuf.cursize += size;
2429 return PRVM_SetEngineString(t);
2432 int PRVM_AllocString(size_t bufferlength, char **pointer)
2437 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2438 if (!prog->knownstrings[i])
2440 if (i >= prog->numknownstrings)
2442 if (i >= prog->maxknownstrings)
2444 const char **oldstrings = prog->knownstrings;
2445 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2446 const char **oldstrings_origin = prog->knownstrings_origin;
2447 prog->maxknownstrings += 128;
2448 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2449 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2450 if(prog->leaktest_active)
2451 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2452 if (prog->numknownstrings)
2454 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2455 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2456 if(prog->leaktest_active)
2457 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2459 // TODO why not Mem_Free the old ones?
2461 prog->numknownstrings++;
2463 prog->firstfreeknownstring = i + 1;
2464 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2465 prog->knownstrings_freeable[i] = true;
2466 if(prog->leaktest_active)
2467 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2469 *pointer = (char *)(prog->knownstrings[i]);
2473 void PRVM_FreeString(int num)
2476 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2477 else if (num >= 0 && num < prog->stringssize)
2478 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2479 else if (num < 0 && num >= -prog->numknownstrings)
2482 if (!prog->knownstrings[num])
2483 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2484 if (!prog->knownstrings_freeable[num])
2485 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2486 PRVM_Free((char *)prog->knownstrings[num]);
2487 if(prog->leaktest_active)
2488 if(prog->knownstrings_origin[num])
2489 PRVM_Free((char *)prog->knownstrings_origin[num]);
2490 prog->knownstrings[num] = NULL;
2491 prog->knownstrings_freeable[num] = false;
2492 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2495 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2498 static qboolean PRVM_IsStringReferenced(string_t string)
2502 for (i = 0;i < prog->progs->numglobaldefs;i++)
2504 ddef_t *d = &prog->globaldefs[i];
2505 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2507 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2511 for(j = 0; j < prog->num_edicts; ++j)
2513 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2514 if (ed->priv.required->free)
2516 for (i=0; i<prog->progs->numfielddefs; ++i)
2518 ddef_t *d = &prog->fielddefs[i];
2519 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2521 if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2529 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2531 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2532 return true; // world or clients
2533 switch(prog - prog_list)
2535 case PRVM_SERVERPROG:
2537 entvars_t *ev = edict->fields.server;
2538 if(ev->solid) // can block other stuff, or is a trigger?
2540 if(ev->modelindex) // visible ent?
2542 if(ev->effects) // particle effect?
2544 if(ev->think) // has a think function?
2545 if(ev->nextthink > 0) // that actually will eventually run?
2549 if(*prvm_leaktest_ignore_classnames.string)
2551 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2556 case PRVM_CLIENTPROG:
2558 // TODO someone add more stuff here
2559 cl_entvars_t *ev = edict->fields.client;
2560 if(ev->entnum) // csqc networked
2562 if(ev->modelindex) // visible ent?
2564 if(ev->effects) // particle effect?
2566 if(ev->think) // has a think function?
2567 if(ev->nextthink > 0) // that actually will eventually run?
2569 if(*prvm_leaktest_ignore_classnames.string)
2571 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2577 // menu prog does not have classnames
2583 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2586 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2587 const char *targetname = NULL;
2589 switch(prog - prog_list)
2591 case PRVM_SERVERPROG:
2592 targetname = PRVM_GetString(edict->fields.server->targetname);
2597 if(!*targetname) // ""
2600 for (i = 0;i < prog->progs->numglobaldefs;i++)
2602 ddef_t *d = &prog->globaldefs[i];
2603 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2605 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2609 for(j = 0; j < prog->num_edicts; ++j)
2611 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2612 if (ed->priv.required->mark < mark)
2618 const char *target = PRVM_GetString(ed->fields.server->target);
2620 if(!strcmp(target, targetname))
2623 for (i=0; i<prog->progs->numfielddefs; ++i)
2625 ddef_t *d = &prog->fielddefs[i];
2626 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2628 if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2636 static void PRVM_MarkReferencedEdicts()
2642 for(j = 0; j < prog->num_edicts; ++j)
2644 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2645 if(ed->priv.required->free)
2647 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2654 for(j = 0; j < prog->num_edicts; ++j)
2656 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2657 if(ed->priv.required->free)
2659 if(ed->priv.required->mark)
2661 if(PRVM_IsEdictReferenced(ed, stage))
2663 ed->priv.required->mark = stage + 1;
2670 Con_DPrintf("leak check used %d stages to find all references\n", stage);
2673 void PRVM_LeakTest()
2676 qboolean leaked = false;
2678 if(!prog->leaktest_active)
2682 for (i = 0; i < prog->numknownstrings; ++i)
2684 if(prog->knownstrings[i])
2685 if(prog->knownstrings_freeable[i])
2686 if(prog->knownstrings_origin[i])
2687 if(!PRVM_IsStringReferenced(-1 - i))
2689 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2695 PRVM_MarkReferencedEdicts();
2696 for(j = 0; j < prog->num_edicts; ++j)
2698 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2699 if(ed->priv.required->free)
2701 if(!ed->priv.required->mark)
2702 if(ed->priv.required->allocation_origin)
2704 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
2705 PRVM_ED_Print(ed, NULL);
2711 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2713 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2715 if(stringbuffer->origin)
2717 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
2722 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2724 if(prog->openfiles[i])
2725 if(prog->openfiles_origin[i])
2727 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
2732 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2734 if(prog->opensearches[i])
2735 if(prog->opensearches_origin[i])
2737 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
2743 Con_Printf("Congratulations. No leaks found.\n");