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"};
42 cvar_t prvm_startupreuseedicttime = {0, "prvm_startupreuseedicttime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"};
44 qboolean prvm_runawaycheck = true;
46 // LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
47 // 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)
48 qboolean prvm_boundscheck = true;
50 extern sizebuf_t vm_tempstringsbuf;
52 //============================================================================
60 void PRVM_MEM_Alloc(void)
64 // reserve space for the null entity aka world
65 // check bound of max_edicts
66 prog->max_edicts = bound(1 + prog->reserved_edicts, prog->max_edicts, prog->limit_edicts);
67 prog->num_edicts = bound(1 + prog->reserved_edicts, prog->num_edicts, prog->max_edicts);
69 // edictprivate_size has to be min as big prvm_edict_private_t
70 prog->edictprivate_size = max(prog->edictprivate_size,(int)sizeof(prvm_edict_private_t));
73 prog->edicts = (prvm_edict_t *)Mem_Alloc(prog->progs_mempool,prog->limit_edicts * sizeof(prvm_edict_t));
75 // alloc edict private space
76 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
79 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
82 for(i = 0; i < prog->max_edicts; i++)
84 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
85 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
91 PRVM_MEM_IncreaseEdicts
94 void PRVM_MEM_IncreaseEdicts(void)
97 int oldmaxedicts = prog->max_edicts;
98 void *oldedictsfields = prog->edictsfields;
99 void *oldedictprivate = prog->edictprivate;
101 if(prog->max_edicts >= prog->limit_edicts)
104 PRVM_GCALL(begin_increase_edicts)();
107 prog->max_edicts = min(prog->max_edicts + 256, prog->limit_edicts);
109 prog->edictsfields = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edict_size);
110 prog->edictprivate = Mem_Alloc(prog->progs_mempool, prog->max_edicts * prog->edictprivate_size);
112 memcpy(prog->edictsfields, oldedictsfields, oldmaxedicts * prog->edict_size);
113 memcpy(prog->edictprivate, oldedictprivate, oldmaxedicts * prog->edictprivate_size);
115 //set e and v pointers
116 for(i = 0; i < prog->max_edicts; i++)
118 prog->edicts[i].priv.required = (prvm_edict_private_t *)((unsigned char *)prog->edictprivate + i * prog->edictprivate_size);
119 prog->edicts[i].fields.vp = (void*)((unsigned char *)prog->edictsfields + i * prog->edict_size);
122 PRVM_GCALL(end_increase_edicts)();
124 Mem_Free(oldedictsfields);
125 Mem_Free(oldedictprivate);
128 //============================================================================
131 int PRVM_ED_FindFieldOffset(const char *field)
134 d = PRVM_ED_FindField(field);
140 int PRVM_ED_FindGlobalOffset(const char *global)
143 d = PRVM_ED_FindGlobal(global);
149 func_t PRVM_ED_FindFunctionOffset(const char *function)
152 f = PRVM_ED_FindFunction(function);
155 return (func_t)(f - prog->functions);
158 qboolean PRVM_ProgLoaded(int prognr)
160 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
163 return (prog_list[prognr].loaded ? TRUE : FALSE);
168 PRVM_SetProgFromString
171 // perhaps add a return value when the str doesnt exist
172 qboolean PRVM_SetProgFromString(const char *str)
175 for(; i < PRVM_MAXPROGS ; i++)
176 if(prog_list[i].name && !strcmp(prog_list[i].name,str))
178 if(prog_list[i].loaded)
180 prog = &prog_list[i];
185 Con_Printf("%s not loaded !\n",PRVM_NAME);
190 Con_Printf("Invalid program name %s !\n", str);
199 void PRVM_SetProg(int prognr)
201 if(0 <= prognr && prognr < PRVM_MAXPROGS)
203 if(prog_list[prognr].loaded)
204 prog = &prog_list[prognr];
206 PRVM_ERROR("%i not loaded !", prognr);
209 PRVM_ERROR("Invalid program number %i", prognr);
216 Sets everything to NULL
219 void PRVM_ED_ClearEdict (prvm_edict_t *e)
221 memset (e->fields.vp, 0, prog->progs->entityfields * 4);
222 e->priv.required->free = false;
224 // AK: Let the init_edict function determine if something needs to be initialized
225 PRVM_GCALL(init_edict)(e);
228 const char *PRVM_AllocationOrigin(void)
231 if(prog->leaktest_active)
232 if(prog->depth > 0) // actually in QC code and not just parsing the entities block of a map/savegame
234 buf = (char *)PRVM_Alloc(128);
235 PRVM_ShortStackTrace(buf, 128);
244 Returns if this particular edict could get allocated by PRVM_ED_Alloc
247 qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
249 if(!e->priv.required->free)
251 if(e->priv.required->freetime < prog->starttime + prvm_startupreuseedicttime.value)
253 if(realtime > e->priv.required->freetime + 1)
255 return false; // entity slot still blocked because the entity was freed less than one second ago
262 Either finds a free edict, or allocates a new one.
263 Try to avoid reusing an entity that was recently freed, because it
264 can cause the client to think the entity morphed into something else
265 instead of being removed and recreated, which can cause interpolated
266 angles and bad trails.
269 prvm_edict_t *PRVM_ED_Alloc (void)
274 // the client qc dont need maxclients
275 // thus it doesnt need to use svs.maxclients
276 // AK: changed i=svs.maxclients+1
277 // AK: changed so the edict 0 wont spawn -> used as reserved/world entity
278 // although the menu/client has no world
279 for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
281 e = PRVM_EDICT_NUM(i);
282 if(PRVM_ED_CanAlloc(e))
284 PRVM_ED_ClearEdict (e);
285 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
290 if (i == prog->limit_edicts)
291 PRVM_ERROR ("%s: PRVM_ED_Alloc: no free edicts",PRVM_NAME);
294 if (prog->num_edicts >= prog->max_edicts)
295 PRVM_MEM_IncreaseEdicts();
297 e = PRVM_EDICT_NUM(i);
298 PRVM_ED_ClearEdict (e);
300 e->priv.required->allocation_origin = PRVM_AllocationOrigin();
309 Marks the edict as free
310 FIXME: walk all entities and NULL out references to this entity
313 void PRVM_ED_Free (prvm_edict_t *ed)
315 // dont delete the null entity (world) or reserved edicts
316 if(PRVM_NUM_FOR_EDICT(ed) <= prog->reserved_edicts )
319 PRVM_GCALL(free_edict)(ed);
321 ed->priv.required->free = true;
322 ed->priv.required->freetime = realtime;
323 if(ed->priv.required->allocation_origin)
325 PRVM_Free((char *)ed->priv.required->allocation_origin);
326 ed->priv.required->allocation_origin = NULL;
330 //===========================================================================
337 ddef_t *PRVM_ED_GlobalAtOfs (int ofs)
342 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
344 def = &prog->globaldefs[i];
356 ddef_t *PRVM_ED_FieldAtOfs (int ofs)
361 for (i=0 ; i<prog->progs->numfielddefs ; i++)
363 def = &prog->fielddefs[i];
375 ddef_t *PRVM_ED_FindField (const char *name)
380 for (i=0 ; i<prog->progs->numfielddefs ; i++)
382 def = &prog->fielddefs[i];
383 if (!strcmp(PRVM_GetString(def->s_name), name))
394 ddef_t *PRVM_ED_FindGlobal (const char *name)
399 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
401 def = &prog->globaldefs[i];
402 if (!strcmp(PRVM_GetString(def->s_name), name))
414 mfunction_t *PRVM_ED_FindFunction (const char *name)
419 for (i=0 ; i<prog->progs->numfunctions ; i++)
421 func = &prog->functions[i];
422 if (!strcmp(PRVM_GetString(func->s_name), name))
433 Returns a string describing *data in a type specific manner
436 char *PRVM_ValueString (etype_t type, prvm_eval_t *val)
438 static char line[MAX_INPUTLINE];
443 type = (etype_t)((int) type & ~DEF_SAVEGLOBAL);
448 strlcpy (line, PRVM_GetString (val->string), sizeof (line));
452 if (n < 0 || n >= prog->limit_edicts)
453 dpsnprintf (line, sizeof(line), "entity %i (invalid!)", n);
455 dpsnprintf (line, sizeof(line), "entity %i", n);
458 f = prog->functions + val->function;
459 dpsnprintf (line, sizeof(line), "%s()", PRVM_GetString(f->s_name));
462 def = PRVM_ED_FieldAtOfs ( val->_int );
463 dpsnprintf (line, sizeof(line), ".%s", PRVM_GetString(def->s_name));
466 dpsnprintf (line, sizeof(line), "void");
469 // LordHavoc: changed from %5.1f to %10.4f
470 dpsnprintf (line, sizeof(line), "%10.4f", val->_float);
473 // LordHavoc: changed from %5.1f to %10.4f
474 dpsnprintf (line, sizeof(line), "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]);
477 dpsnprintf (line, sizeof(line), "pointer");
480 dpsnprintf (line, sizeof(line), "bad type %i", (int) type);
491 Returns a string describing *data in a type specific manner
492 Easier to parse than PR_ValueString
495 char *PRVM_UglyValueString (etype_t type, prvm_eval_t *val)
497 static char line[MAX_INPUTLINE];
503 type = (etype_t)((int)type & ~DEF_SAVEGLOBAL);
508 // Parse the string a bit to turn special characters
509 // (like newline, specifically) into escape codes,
510 // this fixes saving games from various mods
511 s = PRVM_GetString (val->string);
512 for (i = 0;i < (int)sizeof(line) - 2 && *s;)
541 dpsnprintf (line, sizeof (line), "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict)));
544 f = prog->functions + val->function;
545 strlcpy (line, PRVM_GetString (f->s_name), sizeof (line));
548 def = PRVM_ED_FieldAtOfs ( val->_int );
549 dpsnprintf (line, sizeof (line), ".%s", PRVM_GetString(def->s_name));
552 dpsnprintf (line, sizeof (line), "void");
555 dpsnprintf (line, sizeof (line), "%f", val->_float);
558 dpsnprintf (line, sizeof (line), "%f %f %f", val->vector[0], val->vector[1], val->vector[2]);
561 dpsnprintf (line, sizeof (line), "bad type %i", type);
572 Returns a string with a description and the contents of a global,
573 padded to 20 field width
576 char *PRVM_GlobalString (int ofs)
582 static char line[128];
584 val = (void *)&prog->globals.generic[ofs];
585 def = PRVM_ED_GlobalAtOfs(ofs);
587 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
590 s = PRVM_ValueString ((etype_t)def->type, (prvm_eval_t *)val);
591 dpsnprintf (line, sizeof(line), "%s (=%s)", PRVM_GetString(def->s_name), s);
595 //for ( ; i<20 ; i++)
596 // strcat (line," ");
602 char *PRVM_GlobalStringNoContents (int ofs)
606 static char line[128];
608 def = PRVM_ED_GlobalAtOfs(ofs);
610 dpsnprintf (line, sizeof(line), "GLOBAL%i", ofs);
612 dpsnprintf (line, sizeof(line), "%s", PRVM_GetString(def->s_name));
615 //for ( ; i<20 ; i++)
616 // strcat (line," ");
630 // LordHavoc: optimized this to print out much more quickly (tempstring)
631 // LordHavoc: changed to print out every 4096 characters (incase there are a lot of fields to print)
632 void PRVM_ED_Print(prvm_edict_t *ed, const char *wildcard_fieldname)
640 char tempstring[MAX_INPUTLINE], tempstring2[260]; // temporary string buffers
642 if (ed->priv.required->free)
644 Con_Printf("%s: FREE\n",PRVM_NAME);
649 dpsnprintf(tempstring, sizeof(tempstring), "\n%s EDICT %i:\n", PRVM_NAME, PRVM_NUM_FOR_EDICT(ed));
650 for (i=1 ; i<prog->progs->numfielddefs ; i++)
652 d = &prog->fielddefs[i];
653 name = PRVM_GetString(d->s_name);
654 if (name[strlen(name)-2] == '_')
655 continue; // skip _x, _y, _z vars
657 // Check Field Name Wildcard
658 if(wildcard_fieldname)
659 if( !matchpattern(name, wildcard_fieldname, 1) )
660 // Didn't match; skip
663 v = (int *)((char *)ed->fields.vp + d->ofs*4);
665 // if the value is still all 0, skip the field
666 type = d->type & ~DEF_SAVEGLOBAL;
668 for (j=0 ; j<prvm_type_size[type] ; j++)
671 if (j == prvm_type_size[type])
674 if (strlen(name) > sizeof(tempstring2)-4)
676 memcpy (tempstring2, name, sizeof(tempstring2)-4);
677 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
678 tempstring2[sizeof(tempstring2)-1] = 0;
681 strlcat(tempstring, name, sizeof(tempstring));
682 for (l = strlen(name);l < 14;l++)
683 strlcat(tempstring, " ", sizeof(tempstring));
684 strlcat(tempstring, " ", sizeof(tempstring));
686 name = PRVM_ValueString((etype_t)d->type, (prvm_eval_t *)v);
687 if (strlen(name) > sizeof(tempstring2)-4)
689 memcpy (tempstring2, name, sizeof(tempstring2)-4);
690 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
691 tempstring2[sizeof(tempstring2)-1] = 0;
694 strlcat(tempstring, name, sizeof(tempstring));
695 strlcat(tempstring, "\n", sizeof(tempstring));
696 if (strlen(tempstring) >= sizeof(tempstring)/2)
698 Con_Print(tempstring);
703 Con_Print(tempstring);
713 extern cvar_t developer_entityparsing;
714 void PRVM_ED_Write (qfile_t *f, prvm_edict_t *ed)
724 if (ed->priv.required->free)
730 for (i=1 ; i<prog->progs->numfielddefs ; i++)
732 d = &prog->fielddefs[i];
733 name = PRVM_GetString(d->s_name);
735 if(developer_entityparsing.integer)
736 Con_Printf("PRVM_ED_Write: at entity %d field %s\n", PRVM_NUM_FOR_EDICT(ed), name);
738 if (name[strlen(name)-2] == '_')
739 continue; // skip _x, _y, _z vars
741 v = (int *)((char *)ed->fields.vp + d->ofs*4);
743 // if the value is still all 0, skip the field
744 type = d->type & ~DEF_SAVEGLOBAL;
745 for (j=0 ; j<prvm_type_size[type] ; j++)
748 if (j == prvm_type_size[type])
751 FS_Printf(f,"\"%s\" ",name);
752 prog->statestring = va("PRVM_ED_Write, ent=%d, name=%s", i, name);
753 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)d->type, (prvm_eval_t *)v));
754 prog->statestring = NULL;
760 void PRVM_ED_PrintNum (int ent, const char *wildcard_fieldname)
762 PRVM_ED_Print(PRVM_EDICT_NUM(ent), wildcard_fieldname);
767 PRVM_ED_PrintEdicts_f
769 For debugging, prints all the entities in the current server
772 void PRVM_ED_PrintEdicts_f (void)
775 const char *wildcard_fieldname;
777 if(Cmd_Argc() < 2 || Cmd_Argc() > 3)
779 Con_Print("prvm_edicts <program name> <optional field name wildcard>\n");
784 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
788 wildcard_fieldname = Cmd_Argv(2);
790 wildcard_fieldname = NULL;
792 Con_Printf("%s: %i entities\n", PRVM_NAME, prog->num_edicts);
793 for (i=0 ; i<prog->num_edicts ; i++)
794 PRVM_ED_PrintNum (i, wildcard_fieldname);
803 For debugging, prints a single edict
806 void PRVM_ED_PrintEdict_f (void)
809 const char *wildcard_fieldname;
811 if(Cmd_Argc() < 3 || Cmd_Argc() > 4)
813 Con_Print("prvm_edict <program name> <edict number> <optional field name wildcard>\n");
818 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
821 i = atoi (Cmd_Argv(2));
822 if (i >= prog->num_edicts)
824 Con_Print("Bad edict number\n");
829 // Optional Wildcard Provided
830 wildcard_fieldname = Cmd_Argv(3);
833 wildcard_fieldname = NULL;
834 PRVM_ED_PrintNum (i, wildcard_fieldname);
846 // 2 possibilities : 1. just displaying the active edict count
847 // 2. making a function pointer [x]
848 void PRVM_ED_Count_f (void)
856 Con_Print("prvm_count <program name>\n");
861 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
864 if(prog->count_edicts)
865 prog->count_edicts();
869 for (i=0 ; i<prog->num_edicts ; i++)
871 ent = PRVM_EDICT_NUM(i);
872 if (ent->priv.required->free)
877 Con_Printf("num_edicts:%3i\n", prog->num_edicts);
878 Con_Printf("active :%3i\n", active);
885 ==============================================================================
889 FIXME: need to tag constants, doesn't really work
890 ==============================================================================
898 void PRVM_ED_WriteGlobals (qfile_t *f)
906 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
908 def = &prog->globaldefs[i];
910 if ( !(def->type & DEF_SAVEGLOBAL) )
912 type &= ~DEF_SAVEGLOBAL;
914 if (type != ev_string && type != ev_float && type != ev_entity)
917 name = PRVM_GetString(def->s_name);
919 if(developer_entityparsing.integer)
920 Con_Printf("PRVM_ED_WriteGlobals: at global %s\n", name);
922 prog->statestring = va("PRVM_ED_WriteGlobals, name=%s", name);
923 FS_Printf(f,"\"%s\" ", name);
924 FS_Printf(f,"\"%s\"\n", PRVM_UglyValueString((etype_t)type, (prvm_eval_t *)&prog->globals.generic[def->ofs]));
925 prog->statestring = NULL;
935 void PRVM_ED_ParseGlobals (const char *data)
937 char keyname[MAX_INPUTLINE];
943 if (!COM_ParseToken_Simple(&data, false, false))
944 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
945 if (com_token[0] == '}')
948 if (developer_entityparsing.integer)
949 Con_Printf("Key: \"%s\"", com_token);
951 strlcpy (keyname, com_token, sizeof(keyname));
954 if (!COM_ParseToken_Simple(&data, false, true))
955 PRVM_ERROR ("PRVM_ED_ParseGlobals: EOF without closing brace");
957 if (developer_entityparsing.integer)
958 Con_Printf(" \"%s\"\n", com_token);
960 if (com_token[0] == '}')
961 PRVM_ERROR ("PRVM_ED_ParseGlobals: closing brace without data");
963 key = PRVM_ED_FindGlobal (keyname);
966 Con_DPrintf("'%s' is not a global on %s\n", keyname, PRVM_NAME);
970 if (!PRVM_ED_ParseEpair(NULL, key, com_token, true))
971 PRVM_ERROR ("PRVM_ED_ParseGlobals: parse error");
975 //============================================================================
982 Can parse either fields or globals
983 returns false if error
986 qboolean PRVM_ED_ParseEpair(prvm_edict_t *ent, ddef_t *key, const char *s, qboolean parsebackslash)
995 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
997 val = (prvm_eval_t *)((int *)prog->globals.generic + key->ofs);
998 switch (key->type & ~DEF_SAVEGLOBAL)
1001 l = (int)strlen(s) + 1;
1002 val->string = PRVM_AllocString(l, &new_p);
1003 for (i = 0;i < l;i++)
1005 if (s[i] == '\\' && s[i+1] && parsebackslash)
1010 else if (s[i] == 'r')
1021 while (*s && ISWHITESPACE(*s))
1023 val->_float = atof(s);
1027 for (i = 0;i < 3;i++)
1029 while (*s && ISWHITESPACE(*s))
1033 val->vector[i] = atof(s);
1034 while (!ISWHITESPACE(*s))
1042 while (*s && ISWHITESPACE(*s))
1045 if (i >= prog->limit_edicts)
1046 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);
1047 while (i >= prog->max_edicts)
1048 PRVM_MEM_IncreaseEdicts();
1049 // if IncreaseEdicts was called the base pointer needs to be updated
1051 val = (prvm_eval_t *)((int *)ent->fields.vp + key->ofs);
1052 val->edict = PRVM_EDICT_TO_PROG(PRVM_EDICT_NUM((int)i));
1058 Con_DPrintf("PRVM_ED_ParseEpair: Bogus field name %s in %s\n", s, PRVM_NAME);
1061 def = PRVM_ED_FindField(s + 1);
1064 Con_DPrintf("PRVM_ED_ParseEpair: Can't find field %s in %s\n", s, PRVM_NAME);
1067 val->_int = def->ofs;
1071 func = PRVM_ED_FindFunction(s);
1074 Con_Printf("PRVM_ED_ParseEpair: Can't find function %s in %s\n", s, PRVM_NAME);
1077 val->function = func - prog->functions;
1081 Con_Printf("PRVM_ED_ParseEpair: Unknown key->type %i for key \"%s\" on %s\n", key->type, PRVM_GetString(key->s_name), PRVM_NAME);
1091 Console command to send a string to QC function GameCommand of the
1095 sv_cmd adminmsg 3 "do not teamkill"
1096 cl_cmd someclientcommand
1097 menu_cmd somemenucommand
1099 All progs can support this extension; sg calls it in server QC, cg in client
1103 void PRVM_GameCommand(const char *whichprogs, const char *whichcmd)
1107 Con_Printf("%s text...\n", whichcmd);
1112 if(!PRVM_SetProgFromString(whichprogs))
1113 // note: this is not PRVM_SetProg because that one aborts "hard" using PRVM_Error
1114 // also, it makes printing error messages easier!
1116 Con_Printf("%s program not loaded.\n", whichprogs);
1120 if(!prog->funcoffsets.GameCommand)
1122 Con_Printf("%s program do not support GameCommand!\n", whichprogs);
1126 int restorevm_tempstringsbuf_cursize;
1131 restorevm_tempstringsbuf_cursize = vm_tempstringsbuf.cursize;
1132 PRVM_G_INT(OFS_PARM0) = PRVM_SetTempString(s ? s : "");
1133 PRVM_ExecuteProgram (prog->funcoffsets.GameCommand, "QC function GameCommand is missing");
1134 vm_tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
1139 void PRVM_GameCommand_Server_f(void)
1141 PRVM_GameCommand("server", "sv_cmd");
1143 void PRVM_GameCommand_Client_f(void)
1145 PRVM_GameCommand("client", "cl_cmd");
1147 void PRVM_GameCommand_Menu_f(void)
1149 PRVM_GameCommand("menu", "menu_cmd");
1156 Console command to load a field of a specified edict
1159 void PRVM_ED_EdictGet_f(void)
1166 if(Cmd_Argc() != 4 && Cmd_Argc() != 5)
1168 Con_Print("prvm_edictget <program name> <edict number> <field> [<cvar>]\n");
1173 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1175 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1179 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1181 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1183 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1187 v = (prvm_eval_t *)((char *)ed->fields.vp + key->ofs*4);
1188 s = PRVM_UglyValueString(key->type, v);
1191 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(4));
1192 if (cvar && cvar->flags & CVAR_READONLY)
1194 Con_Printf("prvm_edictget: %s is read-only\n", cvar->name);
1197 Cvar_Get(Cmd_Argv(4), s, 0, NULL);
1200 Con_Printf("%s\n", s);
1206 void PRVM_ED_GlobalGet_f(void)
1212 if(Cmd_Argc() != 3 && Cmd_Argc() != 4)
1214 Con_Print("prvm_globalget <program name> <global> [<cvar>]\n");
1219 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1221 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1225 key = PRVM_ED_FindGlobal(Cmd_Argv(2));
1228 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
1232 v = (prvm_eval_t *) &prog->globals.generic[key->ofs];
1233 s = PRVM_UglyValueString(key->type, v);
1236 cvar_t *cvar = Cvar_FindVar(Cmd_Argv(3));
1237 if (cvar && cvar->flags & CVAR_READONLY)
1239 Con_Printf("prvm_globalget: %s is read-only\n", cvar->name);
1242 Cvar_Get(Cmd_Argv(3), s, 0, NULL);
1245 Con_Printf("%s\n", s);
1255 Console command to set a field of a specified edict
1258 void PRVM_ED_EdictSet_f(void)
1265 Con_Print("prvm_edictset <program name> <edict number> <field> <value>\n");
1270 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
1272 Con_Printf("Wrong program name %s !\n", Cmd_Argv(1));
1276 ed = PRVM_EDICT_NUM(atoi(Cmd_Argv(2)));
1278 if((key = PRVM_ED_FindField(Cmd_Argv(3))) == 0)
1279 Con_Printf("Key %s not found !\n", Cmd_Argv(3));
1281 PRVM_ED_ParseEpair(ed, key, Cmd_Argv(4), true);
1287 ====================
1290 Parses an edict out of the given string, returning the new position
1291 ed should be a properly initialized empty edict.
1292 Used for initial level load and for savegames.
1293 ====================
1295 const char *PRVM_ED_ParseEdict (const char *data, prvm_edict_t *ent)
1305 // go through all the dictionary pairs
1309 if (!COM_ParseToken_Simple(&data, false, false))
1310 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1311 if (developer_entityparsing.integer)
1312 Con_Printf("Key: \"%s\"", com_token);
1313 if (com_token[0] == '}')
1316 // anglehack is to allow QuakeEd to write single scalar angles
1317 // and allow them to be turned into vectors. (FIXME...)
1318 if (!strcmp(com_token, "angle"))
1320 strlcpy (com_token, "angles", sizeof(com_token));
1326 // FIXME: change light to _light to get rid of this hack
1327 if (!strcmp(com_token, "light"))
1328 strlcpy (com_token, "light_lev", sizeof(com_token)); // hack for single light def
1330 strlcpy (keyname, com_token, sizeof(keyname));
1332 // another hack to fix keynames with trailing spaces
1333 n = strlen(keyname);
1334 while (n && keyname[n-1] == ' ')
1341 if (!COM_ParseToken_Simple(&data, false, false))
1342 PRVM_ERROR ("PRVM_ED_ParseEdict: EOF without closing brace");
1343 if (developer_entityparsing.integer)
1344 Con_Printf(" \"%s\"\n", com_token);
1346 if (com_token[0] == '}')
1347 PRVM_ERROR ("PRVM_ED_ParseEdict: closing brace without data");
1351 // ignore attempts to set key "" (this problem occurs in nehahra neh1m8.bsp)
1355 // keynames with a leading underscore are used for utility comments,
1356 // and are immediately discarded by quake
1357 if (keyname[0] == '_')
1360 key = PRVM_ED_FindField (keyname);
1363 Con_DPrintf("%s: '%s' is not a field\n", PRVM_NAME, keyname);
1370 strlcpy (temp, com_token, sizeof(temp));
1371 dpsnprintf (com_token, sizeof(com_token), "0 %s 0", temp);
1374 if (!PRVM_ED_ParseEpair(ent, key, com_token, strcmp(keyname, "wad") != 0))
1375 PRVM_ERROR ("PRVM_ED_ParseEdict: parse error");
1379 ent->priv.required->free = true;
1387 PRVM_ED_LoadFromFile
1389 The entities are directly placed in the array, rather than allocated with
1390 PRVM_ED_Alloc, because otherwise an error loading the map would have entity
1391 number references out of order.
1393 Creates a server's entity / program execution context by
1394 parsing textual entity definitions out of an ent file.
1396 Used for both fresh maps and savegame loads. A fresh map would also need
1397 to call PRVM_ED_CallSpawnFunctions () to let the objects initialize themselves.
1400 void PRVM_ED_LoadFromFile (const char *data)
1403 int parsed, inhibited, spawned, died;
1404 const char *funcname;
1416 // parse the opening brace
1417 if (!COM_ParseToken_Simple(&data, false, false))
1419 if (com_token[0] != '{')
1420 PRVM_ERROR ("PRVM_ED_LoadFromFile: %s: found %s when expecting {", PRVM_NAME, com_token);
1422 // CHANGED: this is not conform to PR_LoadFromFile
1423 if(prog->loadintoworld)
1425 prog->loadintoworld = false;
1426 ent = PRVM_EDICT_NUM(0);
1429 ent = PRVM_ED_Alloc();
1432 if (ent != prog->edicts) // hack
1433 memset (ent->fields.vp, 0, prog->progs->entityfields * 4);
1435 data = PRVM_ED_ParseEdict (data, ent);
1438 // remove the entity ?
1439 if(prog->load_edict && !prog->load_edict(ent))
1446 if (prog->funcoffsets.SV_OnEntityPreSpawnFunction)
1449 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1450 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPreSpawnFunction, "QC function SV_OnEntityPreSpawnFunction is missing");
1453 if(ent->priv.required->free)
1460 // immediately call spawn function, but only if there is a self global and a classname
1462 if(!ent->priv.required->free)
1463 if(prog->globaloffsets.self >= 0 && prog->fieldoffsets.classname >= 0)
1465 string_t handle = PRVM_EDICTFIELDVALUE(ent, prog->fieldoffsets.classname)->string;
1468 Con_Print("No classname for:\n");
1469 PRVM_ED_Print(ent, NULL);
1474 // look for the spawn function
1475 funcname = PRVM_GetString(handle);
1476 func = PRVM_ED_FindFunction (va("spawnfunc_%s", funcname));
1478 if(prog->globaloffsets.require_spawnfunc_prefix < 0)
1479 func = PRVM_ED_FindFunction (funcname);
1483 // check for OnEntityNoSpawnFunction
1484 if (prog->funcoffsets.SV_OnEntityNoSpawnFunction)
1487 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1488 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityNoSpawnFunction, "QC function SV_OnEntityNoSpawnFunction is missing");
1492 if (developer.integer) // don't confuse non-developers with errors
1494 Con_Print("No spawn function for:\n");
1495 PRVM_ED_Print(ent, NULL);
1498 continue; // not included in "inhibited" count
1504 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1505 PRVM_ExecuteProgram (func - prog->functions, "");
1509 if(!ent->priv.required->free)
1510 if (prog->funcoffsets.SV_OnEntityPostSpawnFunction)
1513 PRVM_GLOBALFIELDVALUE(prog->globaloffsets.self)->edict = PRVM_EDICT_TO_PROG(ent);
1514 PRVM_ExecuteProgram (prog->funcoffsets.SV_OnEntityPostSpawnFunction, "QC function SV_OnEntityPostSpawnFunction is missing");
1518 if (ent->priv.required->free)
1522 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);
1525 void PRVM_FindOffsets(void)
1527 // field and global searches use -1 for NULL
1528 memset(&prog->fieldoffsets, -1, sizeof(prog->fieldoffsets));
1529 memset(&prog->globaloffsets, -1, sizeof(prog->globaloffsets));
1530 // functions use 0 for NULL
1531 memset(&prog->funcoffsets, 0, sizeof(prog->funcoffsets));
1533 // server and client qc use a lot of similar fields, so this is combined
1534 prog->fieldoffsets.SendEntity = PRVM_ED_FindFieldOffset("SendEntity");
1535 prog->fieldoffsets.SendFlags = PRVM_ED_FindFieldOffset("SendFlags");
1536 prog->fieldoffsets.Version = PRVM_ED_FindFieldOffset("Version");
1537 prog->fieldoffsets.alpha = PRVM_ED_FindFieldOffset("alpha");
1538 prog->fieldoffsets.ammo_cells1 = PRVM_ED_FindFieldOffset("ammo_cells1");
1539 prog->fieldoffsets.ammo_lava_nails = PRVM_ED_FindFieldOffset("ammo_lava_nails");
1540 prog->fieldoffsets.ammo_multi_rockets = PRVM_ED_FindFieldOffset("ammo_multi_rockets");
1541 prog->fieldoffsets.ammo_nails1 = PRVM_ED_FindFieldOffset("ammo_nails1");
1542 prog->fieldoffsets.ammo_plasma = PRVM_ED_FindFieldOffset("ammo_plasma");
1543 prog->fieldoffsets.ammo_rockets1 = PRVM_ED_FindFieldOffset("ammo_rockets1");
1544 prog->fieldoffsets.ammo_shells1 = PRVM_ED_FindFieldOffset("ammo_shells1");
1545 prog->fieldoffsets.angles = PRVM_ED_FindFieldOffset("angles");
1546 prog->fieldoffsets.button3 = PRVM_ED_FindFieldOffset("button3");
1547 prog->fieldoffsets.button4 = PRVM_ED_FindFieldOffset("button4");
1548 prog->fieldoffsets.button5 = PRVM_ED_FindFieldOffset("button5");
1549 prog->fieldoffsets.button6 = PRVM_ED_FindFieldOffset("button6");
1550 prog->fieldoffsets.button7 = PRVM_ED_FindFieldOffset("button7");
1551 prog->fieldoffsets.button8 = PRVM_ED_FindFieldOffset("button8");
1552 prog->fieldoffsets.button9 = PRVM_ED_FindFieldOffset("button9");
1553 prog->fieldoffsets.button10 = PRVM_ED_FindFieldOffset("button10");
1554 prog->fieldoffsets.button11 = PRVM_ED_FindFieldOffset("button11");
1555 prog->fieldoffsets.button12 = PRVM_ED_FindFieldOffset("button12");
1556 prog->fieldoffsets.button13 = PRVM_ED_FindFieldOffset("button13");
1557 prog->fieldoffsets.button14 = PRVM_ED_FindFieldOffset("button14");
1558 prog->fieldoffsets.button15 = PRVM_ED_FindFieldOffset("button15");
1559 prog->fieldoffsets.button16 = PRVM_ED_FindFieldOffset("button16");
1560 prog->fieldoffsets.buttonchat = PRVM_ED_FindFieldOffset("buttonchat");
1561 prog->fieldoffsets.buttonuse = PRVM_ED_FindFieldOffset("buttonuse");
1562 prog->fieldoffsets.chain = PRVM_ED_FindFieldOffset("chain");
1563 prog->fieldoffsets.classname = PRVM_ED_FindFieldOffset("classname");
1564 prog->fieldoffsets.clientcamera = PRVM_ED_FindFieldOffset("clientcamera");
1565 prog->fieldoffsets.clientcolors = PRVM_ED_FindFieldOffset("clientcolors");
1566 prog->fieldoffsets.clientstatus = PRVM_ED_FindFieldOffset("clientstatus");
1567 prog->fieldoffsets.color = PRVM_ED_FindFieldOffset("color");
1568 prog->fieldoffsets.colormod = PRVM_ED_FindFieldOffset("colormod");
1569 prog->fieldoffsets.contentstransition = PRVM_ED_FindFieldOffset("contentstransition");
1570 prog->fieldoffsets.cursor_active = PRVM_ED_FindFieldOffset("cursor_active");
1571 prog->fieldoffsets.cursor_screen = PRVM_ED_FindFieldOffset("cursor_screen");
1572 prog->fieldoffsets.cursor_trace_endpos = PRVM_ED_FindFieldOffset("cursor_trace_endpos");
1573 prog->fieldoffsets.cursor_trace_ent = PRVM_ED_FindFieldOffset("cursor_trace_ent");
1574 prog->fieldoffsets.cursor_trace_start = PRVM_ED_FindFieldOffset("cursor_trace_start");
1575 prog->fieldoffsets.customizeentityforclient = PRVM_ED_FindFieldOffset("customizeentityforclient");
1576 prog->fieldoffsets.dimension_hit = PRVM_ED_FindFieldOffset("dimension_hit");
1577 prog->fieldoffsets.dimension_solid = PRVM_ED_FindFieldOffset("dimension_solid");
1578 prog->fieldoffsets.disableclientprediction = PRVM_ED_FindFieldOffset("disableclientprediction");
1579 prog->fieldoffsets.dphitcontentsmask = PRVM_ED_FindFieldOffset("dphitcontentsmask");
1580 prog->fieldoffsets.drawonlytoclient = PRVM_ED_FindFieldOffset("drawonlytoclient");
1581 prog->fieldoffsets.exteriormodeltoclient = PRVM_ED_FindFieldOffset("exteriormodeltoclient");
1582 prog->fieldoffsets.fatness = PRVM_ED_FindFieldOffset("fatness");
1583 prog->fieldoffsets.forceshader = PRVM_ED_FindFieldOffset("forceshader");
1584 prog->fieldoffsets.frame = PRVM_ED_FindFieldOffset("frame");
1585 prog->fieldoffsets.frame1time = PRVM_ED_FindFieldOffset("frame1time");
1586 prog->fieldoffsets.frame2 = PRVM_ED_FindFieldOffset("frame2");
1587 prog->fieldoffsets.frame2time = PRVM_ED_FindFieldOffset("frame2time");
1588 prog->fieldoffsets.frame3 = PRVM_ED_FindFieldOffset("frame3");
1589 prog->fieldoffsets.frame3time = PRVM_ED_FindFieldOffset("frame3time");
1590 prog->fieldoffsets.frame4 = PRVM_ED_FindFieldOffset("frame4");
1591 prog->fieldoffsets.frame4time = PRVM_ED_FindFieldOffset("frame4time");
1592 prog->fieldoffsets.fullbright = PRVM_ED_FindFieldOffset("fullbright");
1593 prog->fieldoffsets.glow_color = PRVM_ED_FindFieldOffset("glow_color");
1594 prog->fieldoffsets.glow_size = PRVM_ED_FindFieldOffset("glow_size");
1595 prog->fieldoffsets.glow_trail = PRVM_ED_FindFieldOffset("glow_trail");
1596 prog->fieldoffsets.gravity = PRVM_ED_FindFieldOffset("gravity");
1597 prog->fieldoffsets.groundentity = PRVM_ED_FindFieldOffset("groundentity");
1598 prog->fieldoffsets.hull = PRVM_ED_FindFieldOffset("hull");
1599 prog->fieldoffsets.ideal_yaw = PRVM_ED_FindFieldOffset("ideal_yaw");
1600 prog->fieldoffsets.idealpitch = PRVM_ED_FindFieldOffset("idealpitch");
1601 prog->fieldoffsets.items2 = PRVM_ED_FindFieldOffset("items2");
1602 prog->fieldoffsets.lerpfrac = PRVM_ED_FindFieldOffset("lerpfrac");
1603 prog->fieldoffsets.lerpfrac3 = PRVM_ED_FindFieldOffset("lerpfrac3");
1604 prog->fieldoffsets.lerpfrac4 = PRVM_ED_FindFieldOffset("lerpfrac4");
1605 prog->fieldoffsets.light_lev = PRVM_ED_FindFieldOffset("light_lev");
1606 prog->fieldoffsets.message = PRVM_ED_FindFieldOffset("message");
1607 prog->fieldoffsets.modelflags = PRVM_ED_FindFieldOffset("modelflags");
1608 prog->fieldoffsets.movement = PRVM_ED_FindFieldOffset("movement");
1609 prog->fieldoffsets.movetypesteplandevent = PRVM_ED_FindFieldOffset("movetypesteplandevent");
1610 prog->fieldoffsets.netaddress = PRVM_ED_FindFieldOffset("netaddress");
1611 prog->fieldoffsets.nextthink = PRVM_ED_FindFieldOffset("nextthink");
1612 prog->fieldoffsets.nodrawtoclient = PRVM_ED_FindFieldOffset("nodrawtoclient");
1613 prog->fieldoffsets.pflags = PRVM_ED_FindFieldOffset("pflags");
1614 prog->fieldoffsets.ping = PRVM_ED_FindFieldOffset("ping");
1615 prog->fieldoffsets.pitch_speed = PRVM_ED_FindFieldOffset("pitch_speed");
1616 prog->fieldoffsets.playermodel = PRVM_ED_FindFieldOffset("playermodel");
1617 prog->fieldoffsets.playerskin = PRVM_ED_FindFieldOffset("playerskin");
1618 prog->fieldoffsets.pmodel = PRVM_ED_FindFieldOffset("pmodel");
1619 prog->fieldoffsets.punchvector = PRVM_ED_FindFieldOffset("punchvector");
1620 prog->fieldoffsets.renderamt = PRVM_ED_FindFieldOffset("renderamt"); // HalfLife support
1621 prog->fieldoffsets.renderflags = PRVM_ED_FindFieldOffset("renderflags");
1622 prog->fieldoffsets.rendermode = PRVM_ED_FindFieldOffset("rendermode"); // HalfLife support
1623 prog->fieldoffsets.scale = PRVM_ED_FindFieldOffset("scale");
1624 prog->fieldoffsets.shadertime = PRVM_ED_FindFieldOffset("shadertime");
1625 prog->fieldoffsets.style = PRVM_ED_FindFieldOffset("style");
1626 prog->fieldoffsets.tag_entity = PRVM_ED_FindFieldOffset("tag_entity");
1627 prog->fieldoffsets.tag_index = PRVM_ED_FindFieldOffset("tag_index");
1628 prog->fieldoffsets.think = PRVM_ED_FindFieldOffset("think");
1629 prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
1630 prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
1631 prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
1632 prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
1633 prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
1634 prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
1635 prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
1636 prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
1637 prog->funcoffsets.CSQC_Ent_Update = PRVM_ED_FindFunctionOffset("CSQC_Ent_Update");
1638 prog->funcoffsets.CSQC_Event = PRVM_ED_FindFunctionOffset("CSQC_Event");
1639 prog->funcoffsets.CSQC_Event_Sound = PRVM_ED_FindFunctionOffset("CSQC_Event_Sound");
1640 prog->funcoffsets.CSQC_Init = PRVM_ED_FindFunctionOffset("CSQC_Init");
1641 prog->funcoffsets.CSQC_InputEvent = PRVM_ED_FindFunctionOffset("CSQC_InputEvent");
1642 prog->funcoffsets.CSQC_Parse_CenterPrint = PRVM_ED_FindFunctionOffset("CSQC_Parse_CenterPrint");
1643 prog->funcoffsets.CSQC_Parse_Print = PRVM_ED_FindFunctionOffset("CSQC_Parse_Print");
1644 prog->funcoffsets.CSQC_Parse_StuffCmd = PRVM_ED_FindFunctionOffset("CSQC_Parse_StuffCmd");
1645 prog->funcoffsets.CSQC_Parse_TempEntity = PRVM_ED_FindFunctionOffset("CSQC_Parse_TempEntity");
1646 prog->funcoffsets.CSQC_Shutdown = PRVM_ED_FindFunctionOffset("CSQC_Shutdown");
1647 prog->funcoffsets.CSQC_UpdateView = PRVM_ED_FindFunctionOffset("CSQC_UpdateView");
1648 prog->funcoffsets.EndFrame = PRVM_ED_FindFunctionOffset("EndFrame");
1649 prog->funcoffsets.GameCommand = PRVM_ED_FindFunctionOffset("GameCommand");
1650 prog->funcoffsets.Gecko_Query = PRVM_ED_FindFunctionOffset("Gecko_Query");
1651 prog->funcoffsets.RestoreGame = PRVM_ED_FindFunctionOffset("RestoreGame");
1652 prog->funcoffsets.SV_ChangeTeam = PRVM_ED_FindFunctionOffset("SV_ChangeTeam");
1653 prog->funcoffsets.SV_OnEntityNoSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityNoSpawnFunction");
1654 prog->funcoffsets.SV_OnEntityPostSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPostSpawnFunction");
1655 prog->funcoffsets.SV_OnEntityPreSpawnFunction = PRVM_ED_FindFunctionOffset("SV_OnEntityPreSpawnFunction");
1656 prog->funcoffsets.SV_ParseClientCommand = PRVM_ED_FindFunctionOffset("SV_ParseClientCommand");
1657 prog->funcoffsets.SV_PausedTic = PRVM_ED_FindFunctionOffset("SV_PausedTic");
1658 prog->funcoffsets.SV_PlayerPhysics = PRVM_ED_FindFunctionOffset("SV_PlayerPhysics");
1659 prog->funcoffsets.SV_Shutdown = PRVM_ED_FindFunctionOffset("SV_Shutdown");
1660 prog->funcoffsets.URI_Get_Callback = PRVM_ED_FindFunctionOffset("URI_Get_Callback");
1661 prog->globaloffsets.SV_InitCmd = PRVM_ED_FindGlobalOffset("SV_InitCmd");
1662 prog->globaloffsets.coop = PRVM_ED_FindGlobalOffset("coop");
1663 prog->globaloffsets.deathmatch = PRVM_ED_FindGlobalOffset("deathmatch");
1664 prog->globaloffsets.dmg_origin = PRVM_ED_FindGlobalOffset("dmg_origin");
1665 prog->globaloffsets.dmg_save = PRVM_ED_FindGlobalOffset("dmg_save");
1666 prog->globaloffsets.dmg_take = PRVM_ED_FindGlobalOffset("dmg_take");
1667 prog->globaloffsets.drawfont = PRVM_ED_FindGlobalOffset("drawfont");
1668 prog->globaloffsets.gettaginfo_forward = PRVM_ED_FindGlobalOffset("gettaginfo_forward");
1669 prog->globaloffsets.gettaginfo_name = PRVM_ED_FindGlobalOffset("gettaginfo_name");
1670 prog->globaloffsets.gettaginfo_offset = PRVM_ED_FindGlobalOffset("gettaginfo_offset");
1671 prog->globaloffsets.gettaginfo_parent = PRVM_ED_FindGlobalOffset("gettaginfo_parent");
1672 prog->globaloffsets.gettaginfo_right = PRVM_ED_FindGlobalOffset("gettaginfo_right");
1673 prog->globaloffsets.gettaginfo_up = PRVM_ED_FindGlobalOffset("gettaginfo_up");
1674 prog->globaloffsets.intermission = PRVM_ED_FindGlobalOffset("intermission");
1675 prog->globaloffsets.require_spawnfunc_prefix = PRVM_ED_FindGlobalOffset("require_spawnfunc_prefix");
1676 prog->globaloffsets.sb_showscores = PRVM_ED_FindGlobalOffset("sb_showscores");
1677 prog->globaloffsets.self = PRVM_ED_FindGlobalOffset("self");
1678 prog->globaloffsets.serverdeltatime = PRVM_ED_FindGlobalOffset("serverdeltatime");
1679 prog->globaloffsets.serverprevtime = PRVM_ED_FindGlobalOffset("serverprevtime");
1680 prog->globaloffsets.servertime = PRVM_ED_FindGlobalOffset("servertime");
1681 prog->globaloffsets.time = PRVM_ED_FindGlobalOffset("time");
1682 prog->globaloffsets.trace_allsolid = PRVM_ED_FindGlobalOffset("trace_allsolid");
1683 prog->globaloffsets.trace_dphitcontents = PRVM_ED_FindGlobalOffset("trace_dphitcontents");
1684 prog->globaloffsets.trace_dphitq3surfaceflags = PRVM_ED_FindGlobalOffset("trace_dphitq3surfaceflags");
1685 prog->globaloffsets.trace_dphittexturename = PRVM_ED_FindGlobalOffset("trace_dphittexturename");
1686 prog->globaloffsets.trace_dpstartcontents = PRVM_ED_FindGlobalOffset("trace_dpstartcontents");
1687 prog->globaloffsets.trace_endpos = PRVM_ED_FindGlobalOffset("trace_endpos");
1688 prog->globaloffsets.trace_ent = PRVM_ED_FindGlobalOffset("trace_ent");
1689 prog->globaloffsets.trace_fraction = PRVM_ED_FindGlobalOffset("trace_fraction");
1690 prog->globaloffsets.trace_inopen = PRVM_ED_FindGlobalOffset("trace_inopen");
1691 prog->globaloffsets.trace_inwater = PRVM_ED_FindGlobalOffset("trace_inwater");
1692 prog->globaloffsets.trace_networkentity = PRVM_ED_FindGlobalOffset("trace_networkentity");
1693 prog->globaloffsets.trace_plane_dist = PRVM_ED_FindGlobalOffset("trace_plane_dist");
1694 prog->globaloffsets.trace_plane_normal = PRVM_ED_FindGlobalOffset("trace_plane_normal");
1695 prog->globaloffsets.trace_startsolid = PRVM_ED_FindGlobalOffset("trace_startsolid");
1696 prog->globaloffsets.v_forward = PRVM_ED_FindGlobalOffset("v_forward");
1697 prog->globaloffsets.v_right = PRVM_ED_FindGlobalOffset("v_right");
1698 prog->globaloffsets.v_up = PRVM_ED_FindGlobalOffset("v_up");
1699 prog->globaloffsets.view_angles = PRVM_ED_FindGlobalOffset("view_angles");
1700 prog->globaloffsets.worldstatus = PRVM_ED_FindGlobalOffset("worldstatus");
1702 // menu qc only uses some functions, nothing else
1703 prog->funcoffsets.m_draw = PRVM_ED_FindFunctionOffset("m_draw");
1704 prog->funcoffsets.m_init = PRVM_ED_FindFunctionOffset("m_init");
1705 prog->funcoffsets.m_keydown = PRVM_ED_FindFunctionOffset("m_keydown");
1706 prog->funcoffsets.m_keyup = PRVM_ED_FindFunctionOffset("m_keyup");
1707 prog->funcoffsets.m_shutdown = PRVM_ED_FindFunctionOffset("m_shutdown");
1708 prog->funcoffsets.m_toggle = PRVM_ED_FindFunctionOffset("m_toggle");
1713 typedef struct dpfield_s
1720 #define DPFIELDS (sizeof(dpfields) / sizeof(dpfield_t))
1722 dpfield_t dpfields[] =
1733 void PRVM_LeakTest(void);
1734 void PRVM_ResetProg(void)
1737 PRVM_GCALL(reset_cmd)();
1738 Mem_FreePool(&prog->progs_mempool);
1739 memset(prog,0,sizeof(prvm_prog_t));
1740 prog->starttime = Sys_DoubleTime();
1748 void PRVM_LoadLNO( const char *progname ) {
1749 fs_offset_t filesize;
1751 unsigned int *header;
1754 FS_StripExtension( progname, filename, sizeof( filename ) );
1755 strlcat( filename, ".lno", sizeof( filename ) );
1757 lno = FS_LoadFile( filename, tempmempool, false, &filesize );
1763 <Spike> SafeWrite (h, &lnotype, sizeof(int));
1764 <Spike> SafeWrite (h, &version, sizeof(int));
1765 <Spike> SafeWrite (h, &numglobaldefs, sizeof(int));
1766 <Spike> SafeWrite (h, &numpr_globals, sizeof(int));
1767 <Spike> SafeWrite (h, &numfielddefs, sizeof(int));
1768 <Spike> SafeWrite (h, &numstatements, sizeof(int));
1769 <Spike> SafeWrite (h, statement_linenums, numstatements*sizeof(int));
1771 if( (unsigned) filesize < (6 + prog->progs->numstatements) * sizeof( int ) ) {
1776 header = (unsigned int *) lno;
1777 if( header[ 0 ] == *(unsigned int *) "LNOF" &&
1778 LittleLong( header[ 1 ] ) == 1 &&
1779 (unsigned int)LittleLong( header[ 2 ] ) == (unsigned int)prog->progs->numglobaldefs &&
1780 (unsigned int)LittleLong( header[ 3 ] ) == (unsigned int)prog->progs->numglobals &&
1781 (unsigned int)LittleLong( header[ 4 ] ) == (unsigned int)prog->progs->numfielddefs &&
1782 (unsigned int)LittleLong( header[ 5 ] ) == (unsigned int)prog->progs->numstatements )
1784 prog->statement_linenums = (int *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof( int ) );
1785 memcpy( prog->statement_linenums, (int *) lno + 6, prog->progs->numstatements * sizeof( int ) );
1795 void PRVM_LoadProgs (const char * filename, int numrequiredfunc, char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, char **required_global)
1799 ddef_t *infielddefs;
1800 dfunction_t *dfunctions;
1801 fs_offset_t filesize;
1803 if( prog->loaded ) {
1804 PRVM_ERROR ("PRVM_LoadProgs: there is already a %s program loaded!", PRVM_NAME );
1807 prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
1808 if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
1809 PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
1810 // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
1812 Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
1814 prog->filecrc = CRC_Block((unsigned char *)prog->progs, filesize);
1816 // byte swap the header
1817 for (i = 0;i < (int) sizeof(*prog->progs) / 4;i++)
1818 ((int *)prog->progs)[i] = LittleLong ( ((int *)prog->progs)[i] );
1820 if (prog->progs->version != PROG_VERSION)
1821 PRVM_ERROR ("%s: %s has wrong version number (%i should be %i)", PRVM_NAME, filename, prog->progs->version, PROG_VERSION);
1822 if (prog->progs->crc != prog->headercrc && prog->progs->crc != prog->headercrc2)
1823 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);
1825 //prog->functions = (dfunction_t *)((unsigned char *)progs + progs->ofs_functions);
1826 dfunctions = (dfunction_t *)((unsigned char *)prog->progs + prog->progs->ofs_functions);
1828 if (prog->progs->ofs_strings + prog->progs->numstrings >= (int)filesize)
1829 PRVM_ERROR ("%s: %s strings go past end of file", PRVM_NAME, filename);
1830 prog->strings = (char *)prog->progs + prog->progs->ofs_strings;
1831 prog->stringssize = prog->progs->numstrings;
1833 prog->numknownstrings = 0;
1834 prog->maxknownstrings = 0;
1835 prog->knownstrings = NULL;
1836 prog->knownstrings_freeable = NULL;
1838 Mem_ExpandableArray_NewArray(&prog->stringbuffersarray, prog->progs_mempool, sizeof(prvm_stringbuffer_t), 64);
1840 prog->globaldefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_globaldefs);
1842 // we need to expand the fielddefs list to include all the engine fields,
1843 // so allocate a new place for it
1844 infielddefs = (ddef_t *)((unsigned char *)prog->progs + prog->progs->ofs_fielddefs);
1846 prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs->numfielddefs + numrequiredfields) * sizeof(ddef_t));
1848 prog->statements = (dstatement_t *)((unsigned char *)prog->progs + prog->progs->ofs_statements);
1850 prog->statement_profile = (double *)Mem_Alloc(prog->progs_mempool, prog->progs->numstatements * sizeof(*prog->statement_profile));
1852 // moved edict_size calculation down below field adding code
1854 //pr_global_struct = (globalvars_t *)((unsigned char *)progs + progs->ofs_globals);
1855 prog->globals.generic = (float *)((unsigned char *)prog->progs + prog->progs->ofs_globals);
1857 // byte swap the lumps
1858 for (i=0 ; i<prog->progs->numstatements ; i++)
1860 prog->statements[i].op = LittleShort(prog->statements[i].op);
1861 prog->statements[i].a = LittleShort(prog->statements[i].a);
1862 prog->statements[i].b = LittleShort(prog->statements[i].b);
1863 prog->statements[i].c = LittleShort(prog->statements[i].c);
1866 prog->functions = (mfunction_t *)Mem_Alloc(prog->progs_mempool, sizeof(mfunction_t) * prog->progs->numfunctions);
1867 for (i = 0;i < prog->progs->numfunctions;i++)
1869 prog->functions[i].first_statement = LittleLong (dfunctions[i].first_statement);
1870 prog->functions[i].parm_start = LittleLong (dfunctions[i].parm_start);
1871 prog->functions[i].s_name = LittleLong (dfunctions[i].s_name);
1872 prog->functions[i].s_file = LittleLong (dfunctions[i].s_file);
1873 prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
1874 prog->functions[i].locals = LittleLong (dfunctions[i].locals);
1875 memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
1876 if(prog->functions[i].first_statement >= prog->progs->numstatements)
1877 PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
1878 // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
1881 for (i=0 ; i<prog->progs->numglobaldefs ; i++)
1883 prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
1884 prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
1885 prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
1886 // TODO bounds check ofs, s_name
1889 // copy the progs fields to the new fields list
1890 for (i = 0;i < prog->progs->numfielddefs;i++)
1892 prog->fielddefs[i].type = LittleShort (infielddefs[i].type);
1893 if (prog->fielddefs[i].type & DEF_SAVEGLOBAL)
1894 PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
1895 prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
1896 prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
1897 // TODO bounds check ofs, s_name
1900 // append the required fields
1901 for (i = 0;i < (int) numrequiredfields;i++)
1903 prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
1904 prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
1905 prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
1906 // TODO bounds check ofs, s_name
1907 if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
1908 prog->progs->entityfields += 3;
1910 prog->progs->entityfields++;
1911 prog->progs->numfielddefs++;
1914 // check required functions
1915 for(i=0 ; i < numrequiredfunc ; i++)
1916 if(PRVM_ED_FindFunction(required_func[i]) == 0)
1917 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_func[i], filename);
1919 // check required globals
1920 for(i=0 ; i < numrequiredglobals ; i++)
1921 if(PRVM_ED_FindGlobal(required_global[i]) == 0)
1922 PRVM_ERROR("%s: %s not found in %s",PRVM_NAME, required_global[i], filename);
1924 for (i=0 ; i<prog->progs->numglobals ; i++)
1925 ((int *)prog->globals.generic)[i] = LittleLong (((int *)prog->globals.generic)[i]);
1927 // moved edict_size calculation down here, below field adding code
1928 // LordHavoc: this no longer includes the prvm_edict_t header
1929 prog->edict_size = prog->progs->entityfields * 4;
1930 prog->edictareasize = prog->edict_size * prog->limit_edicts;
1932 // LordHavoc: bounds check anything static
1933 for (i = 0,st = prog->statements;i < prog->progs->numstatements;i++,st++)
1939 if ((unsigned short) st->a >= prog->progs->numglobals || st->b + i < 0 || st->b + i >= prog->progs->numstatements)
1940 PRVM_ERROR("PRVM_LoadProgs: out of bounds IF/IFNOT (statement %d) in %s", i, PRVM_NAME);
1943 if (st->a + i < 0 || st->a + i >= prog->progs->numstatements)
1944 PRVM_ERROR("PRVM_LoadProgs: out of bounds GOTO (statement %d) in %s", i, PRVM_NAME);
1946 // global global global
1981 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1982 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d)", i);
1984 // global none global
1990 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->c >= prog->progs->numglobals)
1991 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2007 if ((unsigned short) st->a >= prog->progs->numglobals || (unsigned short) st->b >= prog->progs->numglobals)
2008 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2022 if ((unsigned short) st->a >= prog->progs->numglobals)
2023 PRVM_ERROR("PRVM_LoadProgs: out of bounds global index (statement %d) in %s", i, PRVM_NAME);
2026 Con_DPrintf("PRVM_LoadProgs: unknown opcode %d at statement %d in %s\n", st->op, i, PRVM_NAME);
2030 if(prog->progs->numstatements < 1)
2032 PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
2034 else switch(prog->statements[prog->progs->numstatements - 1].op)
2041 PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
2045 PRVM_LoadLNO(filename);
2049 prog->loaded = TRUE;
2051 // set flags & ddef_ts in prog
2057 PRVM_GCALL(init_cmd)();
2064 void PRVM_Fields_f (void)
2066 int i, j, ednum, used, usedamount;
2068 char tempstring[MAX_INPUTLINE], tempstring2[260];
2078 Con_Print("no progs loaded\n");
2085 Con_Print("prvm_fields <program name>\n");
2090 if(!PRVM_SetProgFromString(Cmd_Argv(1)))
2093 counts = (int *)Mem_Alloc(tempmempool, prog->progs->numfielddefs * sizeof(int));
2094 for (ednum = 0;ednum < prog->max_edicts;ednum++)
2096 ed = PRVM_EDICT_NUM(ednum);
2097 if (ed->priv.required->free)
2099 for (i = 1;i < prog->progs->numfielddefs;i++)
2101 d = &prog->fielddefs[i];
2102 name = PRVM_GetString(d->s_name);
2103 if (name[strlen(name)-2] == '_')
2104 continue; // skip _x, _y, _z vars
2105 v = (int *)((char *)ed->fields.vp + d->ofs*4);
2106 // if the value is still all 0, skip the field
2107 for (j = 0;j < prvm_type_size[d->type & ~DEF_SAVEGLOBAL];j++)
2120 for (i = 0;i < prog->progs->numfielddefs;i++)
2122 d = &prog->fielddefs[i];
2123 name = PRVM_GetString(d->s_name);
2124 if (name[strlen(name)-2] == '_')
2125 continue; // skip _x, _y, _z vars
2126 switch(d->type & ~DEF_SAVEGLOBAL)
2129 strlcat(tempstring, "string ", sizeof(tempstring));
2132 strlcat(tempstring, "entity ", sizeof(tempstring));
2135 strlcat(tempstring, "function ", sizeof(tempstring));
2138 strlcat(tempstring, "field ", sizeof(tempstring));
2141 strlcat(tempstring, "void ", sizeof(tempstring));
2144 strlcat(tempstring, "float ", sizeof(tempstring));
2147 strlcat(tempstring, "vector ", sizeof(tempstring));
2150 strlcat(tempstring, "pointer ", sizeof(tempstring));
2153 dpsnprintf (tempstring2, sizeof(tempstring2), "bad type %i ", d->type & ~DEF_SAVEGLOBAL);
2154 strlcat(tempstring, tempstring2, sizeof(tempstring));
2157 if (strlen(name) > sizeof(tempstring2)-4)
2159 memcpy (tempstring2, name, sizeof(tempstring2)-4);
2160 tempstring2[sizeof(tempstring2)-4] = tempstring2[sizeof(tempstring2)-3] = tempstring2[sizeof(tempstring2)-2] = '.';
2161 tempstring2[sizeof(tempstring2)-1] = 0;
2164 strlcat(tempstring, name, sizeof(tempstring));
2165 for (j = (int)strlen(name);j < 25;j++)
2166 strlcat(tempstring, " ", sizeof(tempstring));
2167 dpsnprintf(tempstring2, sizeof(tempstring2), "%5d", counts[i]);
2168 strlcat(tempstring, tempstring2, sizeof(tempstring));
2169 strlcat(tempstring, "\n", sizeof(tempstring));
2170 if (strlen(tempstring) >= sizeof(tempstring)/2)
2172 Con_Print(tempstring);
2178 usedamount += prvm_type_size[d->type & ~DEF_SAVEGLOBAL];
2182 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);
2187 void PRVM_Globals_f (void)
2190 const char *wildcard;
2196 Con_Print("no progs loaded\n");
2199 if(Cmd_Argc () < 2 || Cmd_Argc() > 3)
2201 Con_Print("prvm_globals <program name> <optional name wildcard>\n");
2206 if(!PRVM_SetProgFromString (Cmd_Argv (1)))
2209 if( Cmd_Argc() == 3)
2210 wildcard = Cmd_Argv(2);
2214 Con_Printf("%s :", PRVM_NAME);
2216 for (i = 0;i < prog->progs->numglobaldefs;i++)
2219 if( !matchpattern( PRVM_GetString(prog->globaldefs[i].s_name), wildcard, 1) )
2224 Con_Printf("%s\n", PRVM_GetString(prog->globaldefs[i].s_name));
2226 Con_Printf("%i global variables, %i culled, totalling %i bytes\n", prog->progs->numglobals, numculled, prog->progs->numglobals * 4);
2236 void PRVM_Global_f(void)
2239 if( Cmd_Argc() != 3 ) {
2240 Con_Printf( "prvm_global <program name> <global name>\n" );
2245 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2248 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2250 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2252 Con_Printf( "%s: %s\n", Cmd_Argv(2), PRVM_ValueString( (etype_t)global->type, (prvm_eval_t *) &prog->globals.generic[ global->ofs ] ) );
2261 void PRVM_GlobalSet_f(void)
2264 if( Cmd_Argc() != 4 ) {
2265 Con_Printf( "prvm_globalset <program name> <global name> <value>\n" );
2270 if( !PRVM_SetProgFromString( Cmd_Argv(1) ) )
2273 global = PRVM_ED_FindGlobal( Cmd_Argv(2) );
2275 Con_Printf( "No global '%s' in %s!\n", Cmd_Argv(2), Cmd_Argv(1) );
2277 PRVM_ED_ParseEpair( NULL, global, Cmd_Argv(3), true );
2286 void PRVM_Init (void)
2288 Cmd_AddCommand ("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, menu)");
2289 Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
2290 Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
2291 Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
2292 Cmd_AddCommand ("prvm_childprofile", PRVM_ChildProfile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu), sorted by time taken in function with child calls");
2293 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)");
2294 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)");
2295 Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
2296 Cmd_AddCommand ("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, menu)");
2297 Cmd_AddCommand ("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, menu)");
2298 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)");
2299 Cmd_AddCommand ("prvm_edictget", PRVM_ED_EdictGet_f, "retrieves the value of a specified property of a specified entity in the selected VM (server, client menu) into a cvar or to the console");
2300 Cmd_AddCommand ("prvm_globalget", PRVM_ED_GlobalGet_f, "retrieves the value of a specified global variable in the selected VM (server, client menu) into a cvar or to the console");
2301 Cmd_AddCommand ("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, menu)");
2302 Cmd_AddCommand ("cl_cmd", PRVM_GameCommand_Client_f, "calls the client QC function GameCommand with the supplied string as argument");
2303 Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument");
2304 Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument");
2306 // COMMANDLINEOPTION: PRVM: -noboundscheck disables the bounds checks (security hole if CSQC is in use!)
2307 prvm_boundscheck = !COM_CheckParm("-noboundscheck");
2309 Cvar_RegisterVariable (&prvm_traceqc);
2310 Cvar_RegisterVariable (&prvm_statementprofiling);
2311 Cvar_RegisterVariable (&prvm_backtraceforwarnings);
2312 Cvar_RegisterVariable (&prvm_leaktest);
2313 Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames);
2314 Cvar_RegisterVariable (&prvm_errordump);
2315 Cvar_RegisterVariable (&prvm_startupreuseedicttime);
2317 // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!)
2318 prvm_runawaycheck = !COM_CheckParm("-norunaway");
2328 void PRVM_InitProg(int prognr)
2330 if(prognr < 0 || prognr >= PRVM_MAXPROGS)
2331 Sys_Error("PRVM_InitProg: Invalid program number %i",prognr);
2333 prog = &prog_list[prognr];
2338 memset(prog, 0, sizeof(prvm_prog_t));
2339 prog->starttime = Sys_DoubleTime();
2341 prog->error_cmd = Host_Error;
2342 prog->leaktest_active = prvm_leaktest.integer != 0;
2345 int PRVM_GetProgNr(void)
2347 return prog - prog_list;
2350 void *_PRVM_Alloc(size_t buffersize, const char *filename, int fileline)
2352 return _Mem_Alloc(prog->progs_mempool, buffersize, filename, fileline);
2355 void _PRVM_Free(void *buffer, const char *filename, int fileline)
2357 _Mem_Free(buffer, filename, fileline);
2360 void _PRVM_FreeAll(const char *filename, int fileline)
2363 prog->fielddefs = NULL;
2364 prog->functions = NULL;
2365 _Mem_EmptyPool(prog->progs_mempool, filename, fileline);
2368 // LordHavoc: turned PRVM_EDICT_NUM into a #define for speed reasons
2369 unsigned int PRVM_EDICT_NUM_ERROR(unsigned int n, char *filename, int fileline)
2371 PRVM_ERROR ("PRVM_EDICT_NUM: %s: bad number %i (called at %s:%i)", PRVM_NAME, n, filename, fileline);
2376 int NUM_FOR_EDICT_ERROR(prvm_edict_t *e)
2378 PRVM_ERROR ("PRVM_NUM_FOR_EDICT: bad pointer %p (world is %p, entity number would be %i)", e, prog->edicts, e - prog->edicts);
2382 int PRVM_NUM_FOR_EDICT(prvm_edict_t *e)
2385 n = e - prog->edicts;
2386 if ((unsigned int)n >= prog->limit_edicts)
2387 Host_Error ("PRVM_NUM_FOR_EDICT: bad pointer");
2391 //int NoCrash_NUM_FOR_EDICT(prvm_edict_t *e)
2393 // return e - prog->edicts;
2396 //#define PRVM_EDICT_TO_PROG(e) ((unsigned char *)(((prvm_edict_t *)e)->v) - (unsigned char *)(prog->edictsfields))
2397 //#define PRVM_PROG_TO_EDICT(e) (prog->edicts + ((e) / (progs->entityfields * 4)))
2398 int PRVM_EDICT_TO_PROG(prvm_edict_t *e)
2401 n = e - prog->edicts;
2402 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2403 Host_Error("PRVM_EDICT_TO_PROG: invalid edict %8p (number %i compared to world at %8p)", e, n, prog->edicts);
2404 return n;// EXPERIMENTAL
2405 //return (unsigned char *)e->v - (unsigned char *)prog->edictsfields;
2407 prvm_edict_t *PRVM_PROG_TO_EDICT(int n)
2409 if ((unsigned int)n >= (unsigned int)prog->max_edicts)
2410 Host_Error("PRVM_PROG_TO_EDICT: invalid edict number %i", n);
2411 return prog->edicts + n; // EXPERIMENTAL
2412 //return prog->edicts + ((n) / (progs->entityfields * 4));
2417 sizebuf_t vm_tempstringsbuf;
2419 const char *PRVM_GetString(int num)
2423 if (num < prog->stringssize)
2424 return prog->strings + num;
2427 if (num <= prog->stringssize + vm_tempstringsbuf.maxsize)
2429 num -= prog->stringssize;
2430 if (num < vm_tempstringsbuf.cursize)
2431 return (char *)vm_tempstringsbuf.data + num;
2434 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2441 VM_Warning("PRVM_GetString: Invalid constant-string offset (%i >= %i prog->stringssize)\n", num, prog->stringssize);
2451 // special range reserved for tempstrings
2453 if (num < vm_tempstringsbuf.cursize)
2454 return (char *)vm_tempstringsbuf.data + num;
2457 VM_Warning("PRVM_GetString: Invalid temp-string offset (%i >= %i vm_tempstringsbuf.cursize)\n", num, vm_tempstringsbuf.cursize);
2463 if (num < prog->numknownstrings)
2465 if (!prog->knownstrings[num])
2467 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
2470 return prog->knownstrings[num];
2474 VM_Warning("PRVM_GetString: Invalid zone-string offset (%i >= %i)\n", num, prog->numknownstrings);
2480 int PRVM_SetEngineString(const char *s)
2485 if (s >= prog->strings && s <= prog->strings + prog->stringssize)
2486 PRVM_ERROR("PRVM_SetEngineString: s in prog->strings area");
2487 // if it's in the tempstrings area, use a reserved range
2488 // (otherwise we'd get millions of useless string offsets cluttering the database)
2489 if (s >= (char *)vm_tempstringsbuf.data && s < (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.maxsize)
2491 return prog->stringssize + (s - (char *)vm_tempstringsbuf.data);
2493 return -1 - ((1<<30) + (s - (char *)vm_tempstringsbuf.data));
2495 // see if it's a known string address
2496 for (i = 0;i < prog->numknownstrings;i++)
2497 if (prog->knownstrings[i] == s)
2499 // new unknown engine string
2500 if (developer.integer >= 200)
2501 Con_Printf("new engine string %p = \"%s\"\n", s, s);
2502 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2503 if (!prog->knownstrings[i])
2505 if (i >= prog->numknownstrings)
2507 if (i >= prog->maxknownstrings)
2509 const char **oldstrings = prog->knownstrings;
2510 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2511 const char **oldstrings_origin = prog->knownstrings_origin;
2512 prog->maxknownstrings += 128;
2513 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2514 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2515 if(prog->leaktest_active)
2516 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2517 if (prog->numknownstrings)
2519 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2520 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2521 if(prog->leaktest_active)
2522 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2525 prog->numknownstrings++;
2527 prog->firstfreeknownstring = i + 1;
2528 prog->knownstrings[i] = s;
2529 prog->knownstrings_freeable[i] = false;
2530 if(prog->leaktest_active)
2531 prog->knownstrings_origin[i] = NULL;
2535 // temp string handling
2537 // all tempstrings go into this buffer consecutively, and it is reset
2538 // whenever PRVM_ExecuteProgram returns to the engine
2539 // (technically each PRVM_ExecuteProgram call saves the cursize value and
2540 // restores it on return, so multiple recursive calls can share the same
2542 // the buffer size is automatically grown as needed
2544 int PRVM_SetTempString(const char *s)
2550 size = (int)strlen(s) + 1;
2551 if (developer.integer >= 300)
2552 Con_Printf("PRVM_SetTempString: cursize %i, size %i\n", vm_tempstringsbuf.cursize, size);
2553 if (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2555 sizebuf_t old = vm_tempstringsbuf;
2556 if (vm_tempstringsbuf.cursize + size >= 1<<28)
2557 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);
2558 vm_tempstringsbuf.maxsize = max(vm_tempstringsbuf.maxsize, 65536);
2559 while (vm_tempstringsbuf.maxsize < vm_tempstringsbuf.cursize + size)
2560 vm_tempstringsbuf.maxsize *= 2;
2561 if (vm_tempstringsbuf.maxsize != old.maxsize || vm_tempstringsbuf.data == NULL)
2563 if (developer.integer >= 100)
2564 Con_Printf("PRVM_SetTempString: enlarging tempstrings buffer (%iKB -> %iKB)\n", old.maxsize/1024, vm_tempstringsbuf.maxsize/1024);
2565 vm_tempstringsbuf.data = (unsigned char *) Mem_Alloc(sv_mempool, vm_tempstringsbuf.maxsize);
2567 memcpy(vm_tempstringsbuf.data, old.data, old.cursize);
2572 t = (char *)vm_tempstringsbuf.data + vm_tempstringsbuf.cursize;
2574 vm_tempstringsbuf.cursize += size;
2575 return PRVM_SetEngineString(t);
2578 int PRVM_AllocString(size_t bufferlength, char **pointer)
2583 for (i = prog->firstfreeknownstring;i < prog->numknownstrings;i++)
2584 if (!prog->knownstrings[i])
2586 if (i >= prog->numknownstrings)
2588 if (i >= prog->maxknownstrings)
2590 const char **oldstrings = prog->knownstrings;
2591 const unsigned char *oldstrings_freeable = prog->knownstrings_freeable;
2592 const char **oldstrings_origin = prog->knownstrings_origin;
2593 prog->maxknownstrings += 128;
2594 prog->knownstrings = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2595 prog->knownstrings_freeable = (unsigned char *)PRVM_Alloc(prog->maxknownstrings * sizeof(unsigned char));
2596 if(prog->leaktest_active)
2597 prog->knownstrings_origin = (const char **)PRVM_Alloc(prog->maxknownstrings * sizeof(char *));
2598 if (prog->numknownstrings)
2600 memcpy((char **)prog->knownstrings, oldstrings, prog->numknownstrings * sizeof(char *));
2601 memcpy((char **)prog->knownstrings_freeable, oldstrings_freeable, prog->numknownstrings * sizeof(unsigned char));
2602 if(prog->leaktest_active)
2603 memcpy((char **)prog->knownstrings_origin, oldstrings_origin, prog->numknownstrings * sizeof(char *));
2605 // TODO why not Mem_Free the old ones?
2607 prog->numknownstrings++;
2609 prog->firstfreeknownstring = i + 1;
2610 prog->knownstrings[i] = (char *)PRVM_Alloc(bufferlength);
2611 prog->knownstrings_freeable[i] = true;
2612 if(prog->leaktest_active)
2613 prog->knownstrings_origin[i] = PRVM_AllocationOrigin();
2615 *pointer = (char *)(prog->knownstrings[i]);
2619 void PRVM_FreeString(int num)
2622 PRVM_ERROR("PRVM_FreeString: attempt to free a NULL string");
2623 else if (num >= 0 && num < prog->stringssize)
2624 PRVM_ERROR("PRVM_FreeString: attempt to free a constant string");
2625 else if (num < 0 && num >= -prog->numknownstrings)
2628 if (!prog->knownstrings[num])
2629 PRVM_ERROR("PRVM_FreeString: attempt to free a non-existent or already freed string");
2630 if (!prog->knownstrings_freeable[num])
2631 PRVM_ERROR("PRVM_FreeString: attempt to free a string owned by the engine");
2632 PRVM_Free((char *)prog->knownstrings[num]);
2633 if(prog->leaktest_active)
2634 if(prog->knownstrings_origin[num])
2635 PRVM_Free((char *)prog->knownstrings_origin[num]);
2636 prog->knownstrings[num] = NULL;
2637 prog->knownstrings_freeable[num] = false;
2638 prog->firstfreeknownstring = min(prog->firstfreeknownstring, num);
2641 PRVM_ERROR("PRVM_FreeString: invalid string offset %i", num);
2644 static qboolean PRVM_IsStringReferenced(string_t string)
2648 for (i = 0;i < prog->progs->numglobaldefs;i++)
2650 ddef_t *d = &prog->globaldefs[i];
2651 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2653 if(string == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->string)
2657 for(j = 0; j < prog->num_edicts; ++j)
2659 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2660 if (ed->priv.required->free)
2662 for (i=0; i<prog->progs->numfielddefs; ++i)
2664 ddef_t *d = &prog->fielddefs[i];
2665 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_string)
2667 if(string == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->string)
2675 static qboolean PRVM_IsEdictRelevant(prvm_edict_t *edict)
2677 if(PRVM_NUM_FOR_EDICT(edict) <= prog->reserved_edicts)
2678 return true; // world or clients
2679 switch(prog - prog_list)
2681 case PRVM_SERVERPROG:
2683 entvars_t *ev = edict->fields.server;
2684 if(ev->solid) // can block other stuff, or is a trigger?
2686 if(ev->modelindex) // visible ent?
2688 if(ev->effects) // particle effect?
2690 if(ev->think) // has a think function?
2691 if(ev->nextthink > 0) // that actually will eventually run?
2695 if(*prvm_leaktest_ignore_classnames.string)
2697 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2702 case PRVM_CLIENTPROG:
2704 // TODO someone add more stuff here
2705 cl_entvars_t *ev = edict->fields.client;
2706 if(ev->entnum) // csqc networked
2708 if(ev->modelindex) // visible ent?
2710 if(ev->effects) // particle effect?
2712 if(ev->think) // has a think function?
2713 if(ev->nextthink > 0) // that actually will eventually run?
2715 if(*prvm_leaktest_ignore_classnames.string)
2717 if(strstr(va(" %s ", prvm_leaktest_ignore_classnames.string), va(" %s ", PRVM_GetString(ev->classname))))
2723 // menu prog does not have classnames
2729 static qboolean PRVM_IsEdictReferenced(prvm_edict_t *edict, int mark)
2732 int edictnum = PRVM_NUM_FOR_EDICT(edict);
2733 const char *targetname = NULL;
2735 switch(prog - prog_list)
2737 case PRVM_SERVERPROG:
2738 targetname = PRVM_GetString(edict->fields.server->targetname);
2743 if(!*targetname) // ""
2746 for (i = 0;i < prog->progs->numglobaldefs;i++)
2748 ddef_t *d = &prog->globaldefs[i];
2749 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2751 if(edictnum == ((prvm_eval_t *) &prog->globals.generic[d->ofs])->edict)
2755 for(j = 0; j < prog->num_edicts; ++j)
2757 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2758 if (ed->priv.required->mark < mark)
2764 const char *target = PRVM_GetString(ed->fields.server->target);
2766 if(!strcmp(target, targetname))
2769 for (i=0; i<prog->progs->numfielddefs; ++i)
2771 ddef_t *d = &prog->fielddefs[i];
2772 if((etype_t)((int) d->type & ~DEF_SAVEGLOBAL) != ev_entity)
2774 if(edictnum == ((prvm_eval_t *) &((float*)ed->fields.vp)[d->ofs])->edict)
2782 static void PRVM_MarkReferencedEdicts(void)
2788 for(j = 0; j < prog->num_edicts; ++j)
2790 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2791 if(ed->priv.required->free)
2793 ed->priv.required->mark = PRVM_IsEdictRelevant(ed) ? 1 : 0;
2800 for(j = 0; j < prog->num_edicts; ++j)
2802 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2803 if(ed->priv.required->free)
2805 if(ed->priv.required->mark)
2807 if(PRVM_IsEdictReferenced(ed, stage))
2809 ed->priv.required->mark = stage + 1;
2816 Con_DPrintf("leak check used %d stages to find all references\n", stage);
2819 void PRVM_LeakTest(void)
2822 qboolean leaked = false;
2824 if(!prog->leaktest_active)
2828 for (i = 0; i < prog->numknownstrings; ++i)
2830 if(prog->knownstrings[i])
2831 if(prog->knownstrings_freeable[i])
2832 if(prog->knownstrings_origin[i])
2833 if(!PRVM_IsStringReferenced(-1 - i))
2835 Con_Printf("Unreferenced string found!\n Value: %s\n Origin: %s\n", prog->knownstrings[i], prog->knownstrings_origin[i]);
2841 PRVM_MarkReferencedEdicts();
2842 for(j = 0; j < prog->num_edicts; ++j)
2844 prvm_edict_t *ed = PRVM_EDICT_NUM(j);
2845 if(ed->priv.required->free)
2847 if(!ed->priv.required->mark)
2848 if(ed->priv.required->allocation_origin)
2850 Con_Printf("Unreferenced edict found!\n Allocated at: %s\n", ed->priv.required->allocation_origin);
2851 PRVM_ED_Print(ed, NULL);
2857 for (i = 0; i < (int)Mem_ExpandableArray_IndexRange(&prog->stringbuffersarray); ++i)
2859 prvm_stringbuffer_t *stringbuffer = (prvm_stringbuffer_t*) Mem_ExpandableArray_RecordAtIndex(&prog->stringbuffersarray, i);
2861 if(stringbuffer->origin)
2863 Con_Printf("Open string buffer handle found!\n Allocated at: %s\n", stringbuffer->origin);
2868 for(i = 0; i < PRVM_MAX_OPENFILES; ++i)
2870 if(prog->openfiles[i])
2871 if(prog->openfiles_origin[i])
2873 Con_Printf("Open file handle found!\n Allocated at: %s\n", prog->openfiles_origin[i]);
2878 for(i = 0; i < PRVM_MAX_OPENSEARCHES; ++i)
2880 if(prog->opensearches[i])
2881 if(prog->opensearches_origin[i])
2883 Con_Printf("Open search handle found!\n Allocated at: %s\n", prog->opensearches_origin[i]);
2889 Con_Printf("Congratulations. No leaks found.\n");