PRVM_GCALL(init_edict)(e);
}
-const char *PRVM_AllocationOrigin()
+const char *PRVM_AllocationOrigin(void)
{
char *buf = NULL;
if(prog->leaktest_active)
return buf;
}
+/*
+=================
+PRVM_ED_CanAlloc
+
+Returns if this particular edict could get allocated by PRVM_ED_Alloc
+=================
+*/
+qboolean PRVM_ED_CanAlloc(prvm_edict_t *e)
+{
+ if(!e->priv.required->free)
+ return false;
+ if(e->priv.required->freetime < prog->starttime + 2)
+ return true;
+ if(realtime > e->priv.required->freetime + 1)
+ return true;
+ return false; // entity slot still blocked because the entity was freed less than one second ago
+}
+
/*
=================
PRVM_ED_Alloc
for (i = prog->reserved_edicts + 1;i < prog->num_edicts;i++)
{
e = PRVM_EDICT_NUM(i);
- // the first couple seconds of server time can involve a lot of
- // freeing and allocating, so relax the replacement policy
- if (e->priv.required->free && ( e->priv.required->freetime < 2 || prog->globaloffsets.time < 0 || (PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float - e->priv.required->freetime) > 0.5 ) )
+ if(PRVM_ED_CanAlloc(e))
{
PRVM_ED_ClearEdict (e);
e->priv.required->allocation_origin = PRVM_AllocationOrigin();
PRVM_GCALL(free_edict)(ed);
ed->priv.required->free = true;
- ed->priv.required->freetime = prog->globaloffsets.time >= 0 ? PRVM_GLOBALFIELDVALUE(prog->globaloffsets.time)->_float : 0;
+ ed->priv.required->freetime = realtime;
if(ed->priv.required->allocation_origin)
{
PRVM_Free((char *)ed->priv.required->allocation_origin);
prog->fieldoffsets.viewmodelforclient = PRVM_ED_FindFieldOffset("viewmodelforclient");
prog->fieldoffsets.viewzoom = PRVM_ED_FindFieldOffset("viewzoom");
prog->fieldoffsets.yaw_speed = PRVM_ED_FindFieldOffset("yaw_speed");
+ prog->fieldoffsets.bouncefactor = PRVM_ED_FindFieldOffset("bouncefactor");
+ prog->fieldoffsets.bouncestop = PRVM_ED_FindFieldOffset("bouncestop");
prog->funcoffsets.CSQC_ConsoleCommand = PRVM_ED_FindFunctionOffset("CSQC_ConsoleCommand");
prog->funcoffsets.CSQC_Ent_Remove = PRVM_ED_FindFunctionOffset("CSQC_Ent_Remove");
prog->funcoffsets.CSQC_Ent_Spawn = PRVM_ED_FindFunctionOffset("CSQC_Ent_Spawn");
===============
*/
-void PRVM_LeakTest();
-void PRVM_ResetProg()
+void PRVM_LeakTest(void);
+void PRVM_ResetProg(void)
{
PRVM_LeakTest();
PRVM_GCALL(reset_cmd)();
prog->progs = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize);
if (prog->progs == NULL || filesize < (fs_offset_t)sizeof(dprograms_t))
PRVM_ERROR ("PRVM_LoadProgs: couldn't load %s for %s", filename, PRVM_NAME);
+ // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file
Con_DPrintf("%s programs occupy %iK.\n", PRVM_NAME, (int)(filesize/1024));
prog->functions[i].numparms = LittleLong (dfunctions[i].numparms);
prog->functions[i].locals = LittleLong (dfunctions[i].locals);
memcpy(prog->functions[i].parm_size, dfunctions[i].parm_size, sizeof(dfunctions[i].parm_size));
+ if(prog->functions[i].first_statement >= prog->progs->numstatements)
+ PRVM_ERROR("PRVM_LoadProgs: out of bounds function statement (function %d) in %s", i, PRVM_NAME);
+ // TODO bounds check parm_start, s_name, s_file, numparms, locals, parm_size
}
for (i=0 ; i<prog->progs->numglobaldefs ; i++)
prog->globaldefs[i].type = LittleShort (prog->globaldefs[i].type);
prog->globaldefs[i].ofs = LittleShort (prog->globaldefs[i].ofs);
prog->globaldefs[i].s_name = LittleLong (prog->globaldefs[i].s_name);
+ // TODO bounds check ofs, s_name
}
// copy the progs fields to the new fields list
PRVM_ERROR ("PRVM_LoadProgs: prog->fielddefs[i].type & DEF_SAVEGLOBAL in %s", PRVM_NAME);
prog->fielddefs[i].ofs = LittleShort (infielddefs[i].ofs);
prog->fielddefs[i].s_name = LittleLong (infielddefs[i].s_name);
+ // TODO bounds check ofs, s_name
}
// append the required fields
prog->fielddefs[prog->progs->numfielddefs].type = required_field[i].type;
prog->fielddefs[prog->progs->numfielddefs].ofs = prog->progs->entityfields;
prog->fielddefs[prog->progs->numfielddefs].s_name = PRVM_SetEngineString(required_field[i].name);
+ // TODO bounds check ofs, s_name
if (prog->fielddefs[prog->progs->numfielddefs].type == ev_vector)
prog->progs->entityfields += 3;
else
break;
}
}
+ if(prog->progs->numstatements < 1)
+ {
+ PRVM_ERROR("PRVM_LoadProgs: empty program in %s", PRVM_NAME);
+ }
+ else switch(prog->statements[prog->progs->numstatements - 1].op)
+ {
+ case OP_RETURN:
+ case OP_GOTO:
+ case OP_DONE:
+ break;
+ default:
+ PRVM_ERROR("PRVM_LoadProgs: program may fall off the edge (does not end with RETURN, GOTO or DONE) in %s", PRVM_NAME);
+ break;
+ }
PRVM_LoadLNO(filename);
Cmd_AddCommand ("prvm_edicts", PRVM_ED_PrintEdicts_f, "prints all data about all entities in the selected VM (server, client, menu)");
Cmd_AddCommand ("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, menu)");
Cmd_AddCommand ("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, menu)");
+ 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");
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)");
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)");
Cmd_AddCommand ("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, menu)");
prog->starttime = Sys_DoubleTime();
prog->error_cmd = Host_Error;
- prog->leaktest_active = prvm_leaktest.integer;
+ prog->leaktest_active = prvm_leaktest.integer != 0;
}
-int PRVM_GetProgNr()
+int PRVM_GetProgNr(void)
{
return prog - prog_list;
}
if (num < prog->numknownstrings)
{
if (!prog->knownstrings[num])
+ {
VM_Warning("PRVM_GetString: Invalid zone-string offset (%i has been freed)\n", num);
+ return "";
+ }
return prog->knownstrings[num];
}
else
return false;
}
-static void PRVM_MarkReferencedEdicts()
+static void PRVM_MarkReferencedEdicts(void)
{
int j;
qboolean found_new;
Con_DPrintf("leak check used %d stages to find all references\n", stage);
}
-void PRVM_LeakTest()
+void PRVM_LeakTest(void)
{
int i, j;
qboolean leaked = false;