diff --git a/include/progs.h b/include/progs.h index 7d678ea9f..ef4d503e0 100644 --- a/include/progs.h +++ b/include/progs.h @@ -30,6 +30,7 @@ #define _PROGS_H #include "link.h" +#include "quakeio.h" #include "pr_comp.h" typedef union eval_s @@ -45,8 +46,10 @@ typedef union eval_s typedef union pr_type_u { float float_var; int int_var; - string_t string_t_var; - func_t func_t_var; + string_t string_var; + func_t func_var; + int edict_var; + float vector_var[1]; // really 3, but this structure must be 32 bits } pr_type_t; #define MAX_ENT_LEAFS 16 @@ -135,7 +138,9 @@ typedef void (*builtin_t) (progs_t *pr); extern builtin_t *pr_builtins; extern int pr_numbuiltins; +ddef_t *PR_FindGlobal (progs_t *pr, const char *name); int FindFieldOffset (progs_t *pr, char *field); +eval_t *GETEDICTFIELDVALUE (edict_t *ed, int fieldoffset); extern func_t EndFrame; // 2000-01-02 EndFrame function by Maddes/FrikaC @@ -210,7 +215,7 @@ struct progs_s { edict_t **edicts; int *num_edicts; - int *max_edicts; + int *reserved_edicts; double *time; int null_bad; @@ -218,6 +223,14 @@ struct progs_s { void (*unlink)(edict_t *ent); void (*flush)(void); + + // required globals + float *g_time; + int *g_self; + // required fields (offsets into the edict) + int f_nextthink; + int f_frame; + int f_think; }; #endif // _PROGS_H diff --git a/libs/gamecode/pr_edict.c b/libs/gamecode/pr_edict.c index 75ed5e9b5..5bdfa51ce 100644 --- a/libs/gamecode/pr_edict.c +++ b/libs/gamecode/pr_edict.c @@ -61,7 +61,7 @@ int type_size[8] = { }; ddef_t *ED_FieldAtOfs (progs_t * pr, int ofs); -qboolean ED_ParseEpair (progs_t * pr, void *base, ddef_t *key, char *s); +qboolean ED_ParseEpair (progs_t * pr, pr_type_t *base, ddef_t *key, char *s); #define MAX_FIELD_LEN 64 #define GEFV_CACHESIZE 2 @@ -99,8 +99,9 @@ ED_Alloc (progs_t * pr) { int i; edict_t *e; + int start = pr->reserved_edicts ? *pr->reserved_edicts : 0; - for (i = MAX_CLIENTS + 1; i < *(pr)->num_edicts; i++) { + for (i = start + 1; i < *(pr)->num_edicts; i++) { e = EDICT_NUM (pr, i); // the first couple seconds of server time can involve a lot of // freeing and allocating, so relax the replacement policy @@ -196,10 +197,10 @@ ED_FindField (progs_t * pr, char *name) /* - ED_FindGlobal + PR_FindGlobal */ ddef_t * -ED_FindGlobal (progs_t * pr, char *name) +PR_FindGlobal (progs_t * pr, const char *name) { ddef_t *def; int i; @@ -545,26 +546,27 @@ ED_Count (progs_t * pr) int i; edict_t *ent; int active, models, solid, step; + ddef_t *solid_def; + ddef_t *model_def; + solid_def = ED_FindField (pr, "solid"); + model_def = ED_FindField (pr, "model"); active = models = solid = step = 0; for (i = 0; i < *(pr)->num_edicts; i++) { ent = EDICT_NUM (pr, i); if (ent->free) continue; active++; - if (ent->v.v.solid) + if (solid_def && ent->v[solid_def->ofs].float_var) solid++; - if (ent->v.v.model) + if (model_def && ent->v[model_def->ofs].float_var) models++; - if (ent->v.v.movetype == MOVETYPE_STEP) - step++; } Con_Printf ("num_edicts:%3i\n", *(pr)->num_edicts); Con_Printf ("active :%3i\n", active); Con_Printf ("view :%3i\n", models); Con_Printf ("touch :%3i\n", solid); - Con_Printf ("step :%3i\n", step); } @@ -632,13 +634,13 @@ ED_ParseGlobals (progs_t * pr, char *data) if (com_token[0] == '}') PR_Error (pr, "ED_ParseEntity: closing brace without data"); - key = ED_FindGlobal (pr, keyname); + key = PR_FindGlobal (pr, keyname); if (!key) { Con_Printf ("'%s' is not a global\n", keyname); continue; } - if (!ED_ParseEpair (pr, (void *) pr->pr_globals, key, com_token)) + if (!ED_ParseEpair (pr, pr->pr_globals, key, com_token)) PR_Error (pr, "ED_ParseGlobals: parse error"); } } @@ -681,24 +683,24 @@ ED_NewString (progs_t * pr, char *string) returns false if error */ qboolean -ED_ParseEpair (progs_t * pr, void *base, ddef_t *key, char *s) +ED_ParseEpair (progs_t * pr, pr_type_t *base, ddef_t *key, char *s) { int i; char string[128]; ddef_t *def; char *v, *w; - void *d; + eval_t *d; dfunction_t *func; - d = (void *) ((int *) base + key->ofs); + d = (eval_t*)&base[key->ofs]; switch (key->type & ~DEF_SAVEGLOBAL) { case ev_string: - *(string_t *) d = PR_SetString (pr, ED_NewString (pr, s)); + d->string = PR_SetString (pr, ED_NewString (pr, s)); break; case ev_float: - *(float *) d = atof (s); + d->_float = atof (s); break; case ev_vector: @@ -709,13 +711,13 @@ ED_ParseEpair (progs_t * pr, void *base, ddef_t *key, char *s) while (*v && *v != ' ') v++; *v = 0; - ((float *) d)[i] = atof (w); + d->vector[i] = atof (w); w = v = v + 1; } break; case ev_entity: - *(int *) d = EDICT_TO_PROG (pr, EDICT_NUM (pr, atoi (s))); + d->edict = EDICT_TO_PROG (pr, EDICT_NUM (pr, atoi (s))); break; case ev_field: @@ -724,7 +726,7 @@ ED_ParseEpair (progs_t * pr, void *base, ddef_t *key, char *s) Con_Printf ("Can't find field %s\n", s); return false; } - *(int *) d = G_INT (pr, def->ofs); + d->_int = G_INT (pr, def->ofs); break; case ev_function: @@ -733,7 +735,7 @@ ED_ParseEpair (progs_t * pr, void *base, ddef_t *key, char *s) Con_Printf ("Can't find function %s\n", s); return false; } - *(func_t *) d = func - pr->pr_functions; + d->function = func - pr->pr_functions; break; default: @@ -819,10 +821,9 @@ ED_ParseEdict (progs_t * pr, char *data, edict_t *ent) int ret; if (anglehack) { - ret = ED_ParseEpair (pr, (void *) &ent->v, key, - va ("0 %s 0", com_token)); + ret = ED_ParseEpair (pr, ent->v, key, va ("0 %s 0", com_token)); } else { - ret = ED_ParseEpair (pr, (void *) &ent->v, key, com_token); + ret = ED_ParseEpair (pr, ent->v, key, com_token); } if (!ret) PR_Error (pr, "ED_ParseEdict: parse error"); @@ -855,14 +856,16 @@ ED_LoadFromFile (progs_t * pr, char *data) edict_t *ent; int inhibit; dfunction_t *func; + eval_t *classname; ent = NULL; inhibit = 0; - pr->pr_global_struct->time = *(pr)->time; -// parse ents + *pr->g_time = *(pr)->time; + + // parse ents while (1) { -// parse the opening brace + // parse the opening brace data = COM_Parse (data); if (!data) break; @@ -875,23 +878,24 @@ ED_LoadFromFile (progs_t * pr, char *data) ent = ED_Alloc (pr); data = ED_ParseEdict (pr, data, ent); -// remove things from different skill levels or deathmatch + // remove things from different skill levels or deathmatch if (ED_Prune_Edict (pr, ent)) { ED_Free (pr, ent); inhibit++; continue; } -// -// immediately call spawn function -// - if (!ent->v.v.classname) { + // + // immediately call spawn function + // + classname = GETEDICTFIELDVALUE (ent, FindFieldOffset (pr, "classname")); + if (classname) { Con_Printf ("No classname for:\n"); ED_Print (pr, ent); ED_Free (pr, ent); continue; } // look for the spawn function - func = ED_FindFunction (pr, PR_GetString (pr, ent->v.v.classname)); + func = ED_FindFunction (pr, PR_GetString (pr, classname->string)); if (!func) { Con_Printf ("No spawn function for:\n"); @@ -900,7 +904,7 @@ ED_LoadFromFile (progs_t * pr, char *data) continue; } - pr->pr_global_struct->self = EDICT_TO_PROG (pr, ent); + *pr->g_self = EDICT_TO_PROG (pr, ent); PR_ExecuteProgram (pr, func - pr->pr_functions); if (pr->flush) pr->flush (); @@ -917,6 +921,7 @@ PR_LoadProgs (progs_t * pr, char *progsname) { int i; dstatement_t *st; + ddef_t *def; // flush the non-C variable lookup cache for (i = 0; i < GEFV_CACHESIZE; i++) @@ -928,18 +933,16 @@ PR_LoadProgs (progs_t * pr, char *progsname) Con_DPrintf ("Programs occupy %iK.\n", com_filesize / 1024); -// store prog crc + // store prog crc pr->crc = CRC_Block ((byte *) pr->progs, com_filesize); -// byte swap the header + // byte swap the header for (i = 0; i < sizeof (*pr->progs) / 4; i++) ((int *) pr->progs)[i] = LittleLong (((int *) pr->progs)[i]); if (pr->progs->version != PROG_VERSION) - PR_Error (pr, "progs.dat has wrong version number (%i should be %i)", - pr->progs->version, PROG_VERSION); - if (pr->progs->crc != PROGHEADER_CRC) - PR_Error (pr, "You must have the qwprogs.dat from QuakeWorld installed"); + PR_Error (pr, "%s has wrong version number (%i should be %i)", + progsname, pr->progs->version, PROG_VERSION); pr->pr_functions = (dfunction_t *) ((byte *) pr->progs + pr->progs->ofs_functions); @@ -999,6 +1002,21 @@ PR_LoadProgs (progs_t * pr, char *progsname) for (i = 0; i < pr->progs->numglobals; i++) ((int *) pr->pr_globals)[i] = LittleLong (((int *) pr->pr_globals)[i]); + def = PR_FindGlobal (pr, "time"); + if (!def) + PR_Error (pr, "%s: undefined symbol: time", progsname); + pr->g_time = &pr->pr_globals[def->ofs].float_var; + def = PR_FindGlobal (pr, "self"); + if (!def) + PR_Error (pr, "%s: undefined symbol: self", progsname); + pr->g_self = &pr->pr_globals[def->ofs].edict_var; + if (!(pr->f_nextthink = FindFieldOffset (pr, "nextthink"))) + PR_Error (pr, "%s: undefined field: nextthink", progsname); + if (!(pr->f_frame = FindFieldOffset (pr, "frame"))) + PR_Error (pr, "%s: undefined field: frame", progsname); + if (!(pr->f_think = FindFieldOffset (pr, "function"))) + PR_Error (pr, "%s: undefined field: function", progsname); + // LordHavoc: Ender added this FindEdictFieldOffsets (pr); diff --git a/libs/gamecode/pr_exec.c b/libs/gamecode/pr_exec.c index 82275b9d2..5e44501e6 100644 --- a/libs/gamecode/pr_exec.c +++ b/libs/gamecode/pr_exec.c @@ -40,9 +40,7 @@ #include "console.h" #include "cvar.h" -#include "host.h" #include "progs.h" -#include "server.h" #include "sys.h" char *pr_opnames[] = { @@ -249,10 +247,10 @@ PR_RunError (progs_t * pr, char *error, ...) PR_StackTrace (pr); Con_Printf ("%s\n", string); - pr->pr_depth = 0; // dump the stack so SV_Error can - // shutdown functions + pr->pr_depth = 0; // dump the stack so PR_Error can + // shutdown functions - SV_Error ("Program error"); + PR_Error (pr, "Program error"); } /* @@ -311,7 +309,7 @@ PR_LeaveFunction (progs_t * pr) int i, c; if (pr->pr_depth <= 0) - SV_Error ("prog stack underflow"); + PR_Error (pr, "prog stack underflow"); // restore locals from the stack c = pr->pr_xfunction->locals; @@ -354,9 +352,9 @@ PR_ExecuteProgram (progs_t * pr, func_t fnum) int profile, startprofile; if (!fnum || fnum >= pr->progs->numfunctions) { - if (pr->pr_global_struct->self) - ED_Print (pr, PROG_TO_EDICT (pr, pr->pr_global_struct->self)); - SV_Error ("PR_ExecuteProgram: NULL function"); + if (*pr->g_self) + ED_Print (pr, PROG_TO_EDICT (pr, *pr->g_self)); + PR_Error (pr, "PR_ExecuteProgram: NULL function"); } f = &pr->pr_functions[fnum]; @@ -677,10 +675,10 @@ PR_ExecuteProgram (progs_t * pr, func_t fnum) return; // all done break; case OP_STATE: - ed = PROG_TO_EDICT (pr, pr->pr_global_struct->self); - ed->v.v.nextthink = pr->pr_global_struct->time + 0.1; - ed->v.v.frame = E_OPA->_float; - ed->v.v.think = E_OPB->function; + ed = PROG_TO_EDICT (pr, *pr->g_self); + ed->v[pr->f_nextthink].float_var = *pr->time + 0.1; + ed->v[pr->f_frame].float_var = E_OPA->_float; + ed->v[pr->f_think].func_var = E_OPB->function; break; // LordHavoc: to be enabled when Progs version 7 (or whatever it will be numbered) is finalized /* diff --git a/nq/source/sv_progs.c b/nq/source/sv_progs.c index 6b935045c..227b1ab17 100644 --- a/nq/source/sv_progs.c +++ b/nq/source/sv_progs.c @@ -175,6 +175,7 @@ SV_Progs_Init (void) sv_pr_state.edicts = &sv.edicts; sv_pr_state.num_edicts = &sv.num_edicts; sv_pr_state.time = &sv.time; + sv_pr_state.reserved_edicts = &svs.maxclients; sv_pr_state.unlink = SV_UnlinkEdict; Cmd_AddCommand ("edict", ED_PrintEdict_f, diff --git a/qw/source/sv_ents.c b/qw/source/sv_ents.c index 1e6b085d1..ab8c3628e 100644 --- a/qw/source/sv_ents.c +++ b/qw/source/sv_ents.c @@ -46,8 +46,6 @@ extern int eval_alpha, eval_scale, eval_glowsize, eval_glowcolor, eval_colormod; -// Ender Extends (QSG - End) -extern eval_t *GETEDICTFIELDVALUE (edict_t *ed, int fieldoffset); /* The PVS must include a small area around the client to allow head diff --git a/qw/source/sv_progs.c b/qw/source/sv_progs.c index 929690b45..0fdb82c91 100644 --- a/qw/source/sv_progs.c +++ b/qw/source/sv_progs.c @@ -52,6 +52,8 @@ func_t SpectatorConnect; func_t SpectatorDisconnect; func_t SpectatorThink; +static reserved_edicts = MAX_CLIENTS; + void FindEdictFieldOffsets (progs_t *pr) { @@ -154,6 +156,7 @@ SV_Progs_Init (void) sv_pr_state.edicts = &sv.edicts; sv_pr_state.num_edicts = &sv.num_edicts; sv_pr_state.time = &sv.time; + sv_pr_state.reserved_edicts = &reserved_edicts; sv_pr_state.unlink = SV_UnlinkEdict; sv_pr_state.flush = SV_FlushSignon;