quakeforge/nq/source/sv_progs.c

708 lines
19 KiB
C

/*
sv_progs.c
Quick QuakeC server code
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/cmd.h"
#include "QF/cvar.h"
#include "QF/quakefs.h"
#include "QF/sys.h"
#include "compat.h"
#include "world.h"
#include "nq/include/host.h"
#include "nq/include/server.h"
#include "nq/include/sv_progs.h"
progs_t sv_pr_state;
sv_globals_t sv_globals;
sv_funcs_t sv_funcs;
sv_fields_t sv_fields;
edict_t sv_edicts[MAX_EDICTS];
sv_data_t sv_data[MAX_EDICTS];
char *sv_progs;
static cvar_t sv_progs_cvar = {
.name = "sv_progs",
.description =
"Override the default game progs.",
.default_value = "",
.flags = CVAR_NONE,
.value = { .type = 0, .value = &sv_progs },
};
int sv_progs_zone;
static cvar_t sv_progs_zone_cvar = {
.name = "sv_progs_zone",
.description =
"size of the zone for progs in kb",
.default_value = "256",
.flags = CVAR_NONE,
.value = { .type = &cexpr_int, .value = &sv_progs_zone },
};
int sv_progs_stack;
static cvar_t sv_progs_stack_cvar = {
.name = "sv_progs_stack",
.description =
"size of the stack for progs in kb",
.default_value = "256",
.flags = CVAR_NONE,
.value = { .type = &cexpr_int, .value = &sv_progs_stack },
};
char *sv_progs_ext;
static cvar_t sv_progs_ext_cvar = {
.name = "sv_progs_ext",
.description =
"extention mapping to use: none, id, qf",
.default_value = "qf",
.flags = CVAR_NONE,
.value = { .type = 0, .value = &sv_progs_ext },
};
float pr_checkextensions;
static cvar_t pr_checkextensions_cvar = {
.name = "pr_checkextensions",
.description =
"indicate the presence of the checkextentions qc function",
.default_value = "1",
.flags = CVAR_ROM,
.value = { .type = &cexpr_float, .value = &pr_checkextensions },
};
float nomonsters;
static cvar_t nomonsters_cvar = {
.name = "nomonsters",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_NONE,
.value = { .type = &cexpr_float, .value = &nomonsters },
};
float gamecfg;
static cvar_t gamecfg_cvar = {
.name = "gamecfg",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_NONE,
.value = { .type = &cexpr_float, .value = &gamecfg },
};
float scratch1;
static cvar_t scratch1_cvar = {
.name = "scratch1",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_NONE,
.value = { .type = &cexpr_float, .value = &scratch1 },
};
float scratch2;
static cvar_t scratch2_cvar = {
.name = "scratch2",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_NONE,
.value = { .type = &cexpr_float, .value = &scratch2 },
};
float scratch3;
static cvar_t scratch3_cvar = {
.name = "scratch3",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_NONE,
.value = { .type = &cexpr_float, .value = &scratch3 },
};
float scratch4;
static cvar_t scratch4_cvar = {
.name = "scratch4",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_NONE,
.value = { .type = &cexpr_float, .value = &scratch4 },
};
float savedgamecfg;
static cvar_t savedgamecfg_cvar = {
.name = "savedgamecfg",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_ARCHIVE,
.value = { .type = &cexpr_float, .value = &savedgamecfg },
};
float saved1;
static cvar_t saved1_cvar = {
.name = "saved1",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_ARCHIVE,
.value = { .type = &cexpr_float, .value = &saved1 },
};
float saved2;
static cvar_t saved2_cvar = {
.name = "saved2",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_ARCHIVE,
.value = { .type = &cexpr_float, .value = &saved2 },
};
float saved3;
static cvar_t saved3_cvar = {
.name = "saved3",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_ARCHIVE,
.value = { .type = &cexpr_float, .value = &saved3 },
};
float saved4;
static cvar_t saved4_cvar = {
.name = "saved4",
.description =
"No Description",
.default_value = "0",
.flags = CVAR_ARCHIVE,
.value = { .type = &cexpr_float, .value = &saved4 },
};
static int sv_range;
static unsigned
bi_map (progs_t *pr, unsigned binum)
{
unsigned range;
if (sv_range != PR_RANGE_NONE) {
range = (binum & PR_RANGE_MASK) >> PR_RANGE_SHIFT;
if (!range && binum > PR_RANGE_ID_MAX)
binum |= sv_range << PR_RANGE_SHIFT;
}
return binum;
}
static int
prune_edict (progs_t *pr, edict_t *ent)
{
// remove things from different skill levels or deathmatch
if (deathmatch) {
if (((int) SVfloat (ent, spawnflags)
& SPAWNFLAG_NOT_DEATHMATCH)) {
return 1;
}
} else if ((current_skill == 0
&& ((int) SVfloat (ent, spawnflags)
& SPAWNFLAG_NOT_EASY))
|| (current_skill == 1
&& ((int) SVfloat (ent, spawnflags)
& SPAWNFLAG_NOT_MEDIUM))
|| (current_skill >= 2
&& ((int) SVfloat (ent, spawnflags)
& SPAWNFLAG_NOT_HARD))) {
return 1;
}
return 0;
}
static void
ED_PrintEdicts_f (void)
{
ED_PrintEdicts (&sv_pr_state, Cmd_Argv (1));
}
/*
ED_PrintEdict_f
For debugging, prints a single edict
*/
static void
ED_PrintEdict_f (void)
{
int i;
const char *fieldname = 0;
if (Cmd_Argc () < 2) {
Sys_Printf ("edict num [fieldname]\n");
return;
}
if (Cmd_Argc () >= 3) {
fieldname = Cmd_Argv (2);
}
i = atoi (Cmd_Argv (1));
ED_PrintNum (&sv_pr_state, i, fieldname);
}
static void
ED_Count_f (void)
{
ED_Count (&sv_pr_state);
}
static void
PR_Profile_f (void)
{
if (!sv_pr_state.progs) {
Sys_Printf ("no progs loaded\n");
return;
}
PR_Profile (&sv_pr_state);
}
static void
watch_f (void)
{
PR_Debug_Watch (&sv_pr_state, Cmd_Argc () < 2 ? 0 : Cmd_Args (1));
}
static void
print_f (void)
{
PR_Debug_Print (&sv_pr_state, Cmd_Argc () < 2 ? 0 : Cmd_Args (1));
}
static int
parse_field (progs_t *pr, const char *key, const char *value)
{
if (strequal (key, "sky")
|| strequal (key, "skyname")
|| strequal (key, "qlsky")
|| strequal (key, "fog"))
return 1;
if (strequal (key, "mapversion")) // ignore HL(?) version field
return 1;
if (*key == '_') // ignore _fields
return 1;
return 0;
}
typedef struct sv_def_s {
etype_t type;
unsigned short offset;
const char *name;
void *field;
} sv_def_t;
static sv_def_t nq_self[] = {
{ev_entity, 28, "self", &sv_globals.self},
{ev_entity, 28, ".self", &sv_globals.self},
{ev_void, 0, 0},
};
static sv_def_t nq_defs[] = {
{ev_entity, 29, "other", &sv_globals.other},
{ev_entity, 30, "world", &sv_globals.world},
{ev_float, 31, "time", &sv_globals.time},
{ev_float, 32, "frametime", &sv_globals.frametime},
{ev_float, 33, "force_retouch", &sv_globals.force_retouch},
{ev_string, 34, "mapname", &sv_globals.mapname},
{ev_float, 35, "deathmatch", &sv_globals.deathmatch},
{ev_float, 36, "coop", &sv_globals.coop},
{ev_float, 37, "teamplay", &sv_globals.teamplay},
{ev_float, 38, "serverflags", &sv_globals.serverflags},
{ev_float, 39, "total_secrets", &sv_globals.total_secrets},
{ev_float, 40, "total_monsters", &sv_globals.total_monsters},
{ev_float, 41, "found_secrets", &sv_globals.found_secrets},
{ev_float, 42, "killed_monsters", &sv_globals.killed_monsters},
//parm1-16 form an array
{ev_float, 43, "parm1", &sv_globals.parms},
{ev_vector, 59, "v_forward", &sv_globals.v_forward},
{ev_vector, 62, "v_up", &sv_globals.v_up},
{ev_vector, 65, "v_right", &sv_globals.v_right},
{ev_float, 68, "trace_allsolid", &sv_globals.trace_allsolid},
{ev_float, 69, "trace_startsolid", &sv_globals.trace_startsolid},
{ev_float, 70, "trace_fraction", &sv_globals.trace_fraction},
{ev_vector, 71, "trace_endpos", &sv_globals.trace_endpos},
{ev_vector, 74, "trace_plane_normal", &sv_globals.trace_plane_normal},
{ev_float, 77, "trace_plane_dist", &sv_globals.trace_plane_dist},
{ev_entity, 78, "trace_ent", &sv_globals.trace_ent},
{ev_float, 79, "trace_inopen", &sv_globals.trace_inopen},
{ev_float, 80, "trace_inwater", &sv_globals.trace_inwater},
{ev_entity, 81, "msg_entity", &sv_globals.msg_entity},
{ev_void, 0, 0},
};
static sv_def_t nq_funcs[] = {
{ev_func, 82, "main", &sv_funcs.main},
{ev_func, 83, "StartFrame", &sv_funcs.StartFrame},
{ev_func, 84, "PlayerPreThink", &sv_funcs.PlayerPreThink},
{ev_func, 85, "PlayerPostThink", &sv_funcs.PlayerPostThink},
{ev_func, 86, "ClientKill", &sv_funcs.ClientKill},
{ev_func, 87, "ClientConnect", &sv_funcs.ClientConnect},
{ev_func, 88, "PutClientInServer", &sv_funcs.PutClientInServer},
{ev_func, 89, "ClientDisconnect", &sv_funcs.ClientDisconnect},
{ev_func, 90, "SetNewParms", &sv_funcs.SetNewParms},
{ev_func, 91, "SetChangeParms", &sv_funcs.SetChangeParms},
{ev_void, 0, 0},
};
static sv_def_t nq_fields[] = {
{ev_float, 0, "modelindex", &sv_fields.modelindex},
{ev_vector, 1, "absmin", &sv_fields.absmin},
{ev_vector, 4, "absmax", &sv_fields.absmax},
{ev_float, 7, "ltime", &sv_fields.ltime},
{ev_float, 8, "movetype", &sv_fields.movetype},
{ev_float, 9, "solid", &sv_fields.solid},
{ev_vector, 10, "origin", &sv_fields.origin},
{ev_vector, 13, "oldorigin", &sv_fields.oldorigin},
{ev_vector, 16, "velocity", &sv_fields.velocity},
{ev_vector, 19, "angles", &sv_fields.angles},
{ev_vector, 22, "avelocity", &sv_fields.avelocity},
{ev_vector, 25, "punchangle", &sv_fields.punchangle},
{ev_string, 28, "classname", &sv_fields.classname},
{ev_string, 29, "model", &sv_fields.model},
{ev_float, 30, "frame", &sv_fields.frame},
{ev_float, 31, "skin", &sv_fields.skin},
{ev_float, 32, "effects", &sv_fields.effects},
{ev_vector, 33, "mins", &sv_fields.mins},
{ev_vector, 36, "maxs", &sv_fields.maxs},
{ev_vector, 39, "size", &sv_fields.size},
{ev_func, 42, "touch", &sv_fields.touch},
{ev_func, 44, "think", &sv_fields.think},
{ev_func, 45, "blocked", &sv_fields.blocked},
{ev_float, 46, "nextthink", &sv_fields.nextthink},
{ev_entity, 47, "groundentity", &sv_fields.groundentity},
{ev_float, 48, "health", &sv_fields.health},
{ev_float, 49, "frags", &sv_fields.frags},
{ev_float, 50, "weapon", &sv_fields.weapon},
{ev_string, 51, "weaponmodel", &sv_fields.weaponmodel},
{ev_float, 52, "weaponframe", &sv_fields.weaponframe},
{ev_float, 53, "currentammo", &sv_fields.currentammo},
{ev_float, 54, "ammo_shells", &sv_fields.ammo_shells},
{ev_float, 55, "ammo_nails", &sv_fields.ammo_nails},
{ev_float, 56, "ammo_rockets", &sv_fields.ammo_rockets},
{ev_float, 57, "ammo_cells", &sv_fields.ammo_cells},
{ev_float, 58, "items", &sv_fields.items},
{ev_float, 59, "takedamage", &sv_fields.takedamage},
{ev_entity, 60, "chain", &sv_fields.chain},
{ev_vector, 62, "view_ofs", &sv_fields.view_ofs},
{ev_float, 65, "button0", &sv_fields.button0},
{ev_float, 66, "button1", &sv_fields.button1},
{ev_float, 67, "button2", &sv_fields.button2},
{ev_float, 68, "impulse", &sv_fields.impulse},
{ev_float, 69, "fixangle", &sv_fields.fixangle},
{ev_vector, 70, "v_angle", &sv_fields.v_angle},
{ev_float, 73, "idealpitch", &sv_fields.idealpitch},
{ev_string, 74, "netname", &sv_fields.netname},
{ev_entity, 75, "enemy", &sv_fields.enemy},
{ev_float, 76, "flags", &sv_fields.flags},
{ev_float, 77, "colormap", &sv_fields.colormap},
{ev_float, 78, "team", &sv_fields.team},
{ev_float, 80, "teleport_time", &sv_fields.teleport_time},
{ev_float, 82, "armorvalue", &sv_fields.armorvalue},
{ev_float, 83, "waterlevel", &sv_fields.waterlevel},
{ev_float, 84, "watertype", &sv_fields.watertype},
{ev_float, 85, "ideal_yaw", &sv_fields.ideal_yaw},
{ev_float, 86, "yaw_speed", &sv_fields.yaw_speed},
{ev_entity, 88, "goalentity", &sv_fields.goalentity},
{ev_float, 89, "spawnflags", &sv_fields.spawnflags},
{ev_float, 92, "dmg_take", &sv_fields.dmg_take},
{ev_float, 93, "dmg_save", &sv_fields.dmg_save},
{ev_entity, 94, "dmg_inflictor", &sv_fields.dmg_inflictor},
{ev_entity, 95, "owner", &sv_fields.owner},
{ev_vector, 96, "movedir", &sv_fields.movedir},
{ev_string, 99, "message", &sv_fields.message},
{ev_float, 100, "sounds", &sv_fields.sounds},
{ev_void, 0, 0},
};
static sv_def_t nq_opt_defs[] = {
{ev_entity, 0, "newmis", &sv_globals.newmis},
{ev_void, 0, 0},
};
static sv_def_t nq_opt_funcs[] = {
{ev_func, 0, "EndFrame", &sv_funcs.EndFrame},
{ev_void, 0, 0},
};
static sv_def_t nq_opt_fields[] = {
{ev_int, 0, "rotated_bbox", &sv_fields.rotated_bbox},
{ev_float, 0, "alpha", &sv_fields.alpha},
{ev_float, 0, "gravity", &sv_fields.gravity},
{ev_float, 0, "items2", &sv_fields.items2},
{ev_float, 0, "lastruntime", &sv_fields.lastruntime},
{ev_void, 0, 0},
};
static const pr_uint_t nq_crc = 5927;
static void
set_address (sv_def_t *def, void *address)
{
switch (def->type) {
case ev_void:
case ev_short:
case ev_ushort:
case ev_invalid:
case ev_type_count:
break;
case ev_float:
case ev_vector:
case ev_quaternion:
*(float **)def->field = (float *) address;
break;
case ev_double:
*(double **)def->field = (double *) address;
break;
case ev_string:
case ev_entity:
case ev_field:
case ev_func:
case ev_ptr:
case ev_int:
case ev_uint:
*(pr_int_t **)def->field = (pr_int_t *) address;
break;
case ev_long:
case ev_ulong:
*(pr_long_t **)def->field = (pr_long_t *) address;
break;
}
}
static int
resolve_globals (progs_t *pr, sv_def_t *def, int mode)
{
pr_def_t *ddef;
int ret = 1;
if (mode == 2) {
for (; def->name; def++)
set_address (def, &G_FLOAT (pr, def->offset));
return 1;
}
for (; def->name; def++) {
ddef = PR_FindGlobal (pr, def->name);
if (ddef) {
set_address (def, &G_FLOAT(pr, ddef->ofs));
} else if (mode) {
PR_Undefined (pr, "global", def->name);
ret = 0;
}
}
return ret;
}
static int
resolve_functions (progs_t *pr, sv_def_t *def, int mode)
{
dfunction_t *dfunc;
int ret = 1;
if (mode == 2) {
for (; def->name; def++)
*(pr_func_t *) def->field = G_FUNCTION (pr, def->offset);
return 1;
}
for (; def->name; def++) {
dfunc = PR_FindFunction (pr, def->name);
if (dfunc) {
*(pr_func_t *) def->field = dfunc - pr->pr_functions;
} else if (mode) {
PR_Undefined (pr, "function", def->name);
ret = 0;
}
}
return ret;
}
static int
resolve_fields (progs_t *pr, sv_def_t *def, int mode)
{
pr_def_t *ddef;
int ret = 1;
if (mode == 2) {
for (; def->name; def++)
*(int *) def->field = def->offset;
return 1;
}
for (; def->name; def++) {
*(int *)def->field = -1;
ddef = PR_FindField (pr, def->name);
if (ddef) {
*(int *)def->field = ddef->ofs;
} else if (mode) {
PR_Undefined (pr, "field", def->name);
ret = 0;
}
}
return ret;
}
static int
resolve (progs_t *pr)
{
int ret = 1;
if (pr->progs->crc == nq_crc) {
resolve_globals (pr, nq_self, 2);
resolve_globals (pr, nq_defs, 2);
resolve_functions (pr, nq_funcs, 2);
resolve_fields (pr, nq_fields, 2);
} else {
if (!resolve_globals (pr, nq_self, 0))
ret = 0;
if (!resolve_globals (pr, nq_defs, 1))
ret = 0;
if (!resolve_functions (pr, nq_funcs, 1))
ret = 0;
if (!resolve_fields (pr, nq_fields, 1))
ret = 0;
}
resolve_globals (pr, nq_opt_defs, 0);
resolve_functions (pr, nq_opt_funcs, 0);
resolve_fields (pr, nq_opt_fields, 0);
// progs engine needs these globals anyway
sv_pr_state.globals.self = sv_globals.self;
sv_pr_state.globals.ftime = sv_globals.time;//FIXME double time
return ret;
}
static int
sv_init_edicts (progs_t *pr)
{
int i;
memset (sv_edicts, 0, sizeof (sv_edicts));
memset (sv_data, 0, sizeof (sv_data));
// init the data field of the edicts
for (i = 0; i < sv.max_edicts; i++) {
edict_t *ent = EDICT_NUM (&sv_pr_state, i);
ent->pr = &sv_pr_state;
ent->entnum = i;
ent->edict = EDICT_TO_PROG (&sv_pr_state, ent);
ent->edata = &sv_data[i];
SVdata (ent)->edict = ent;
}
return 1;
}
void
SV_LoadProgs (void)
{
const char *progs_name = "progs.dat";
const char *range;
if (strequal (sv_progs_ext, "qf")) {
sv_range = PR_RANGE_QF;
range = "QF";
} else if (strequal (sv_progs_ext, "id")) {
sv_range = PR_RANGE_ID;
range = "ID";
} else {
sv_range = PR_RANGE_NONE;
range = "None";
}
Sys_MaskPrintf (SYS_dev, "Using %s builtin extention mapping\n", range);
memset (&sv_globals, 0, sizeof (sv_funcs));
memset (&sv_funcs, 0, sizeof (sv_funcs));
if (qfs_gamedir->gamecode && *qfs_gamedir->gamecode)
progs_name = qfs_gamedir->gamecode;
if (*sv_progs)
progs_name = sv_progs;
sv_pr_state.max_edicts = sv.max_edicts;
sv_pr_state.zone_size = sv_progs_zone * 1024;
sv_pr_state.stack_size = sv_progs_stack * 1024;
sv.edicts = sv_edicts;
PR_LoadProgs (&sv_pr_state, progs_name);
if (!sv_pr_state.progs)
Host_Error ("SV_LoadProgs: couldn't load %s", progs_name);
}
void
SV_Progs_Init (void)
{
SV_Progs_Init_Cvars ();
pr_gametype = "netquake";
sv_pr_state.pr_edicts = &sv.edicts;
sv_pr_state.num_edicts = &sv.num_edicts;
sv_pr_state.reserved_edicts = &svs.maxclients;
sv_pr_state.unlink = SV_UnlinkEdict;
sv_pr_state.parse_field = parse_field;
sv_pr_state.prune_edict = prune_edict;
sv_pr_state.bi_map = bi_map;
sv_pr_state.resolve = resolve;
PR_AddLoadFunc (&sv_pr_state, sv_init_edicts);
PR_Init (&sv_pr_state);
SV_PR_Cmds_Init ();
Cmd_AddCommand ("edict", ED_PrintEdict_f, "Report information on a given "
"edict in the game. (edict (edict number))");
Cmd_AddCommand ("edicts", ED_PrintEdicts_f,
"Display information on all edicts in the game.");
Cmd_AddCommand ("edictcount", ED_Count_f,
"Display summary information on the edicts in the game.");
Cmd_AddCommand ("profile", PR_Profile_f, "FIXME: Report information about "
"QuakeC Stuff (\?\?\?) No Description");
Cmd_AddCommand ("watch", watch_f, "set watchpoint");
Cmd_AddCommand ("print", print_f, "print value at location");
}
void
SV_Progs_Init_Cvars (void)
{
PR_Init_Cvars ();
Cvar_Register (&sv_progs_cvar, 0, 0);
Cvar_Register (&sv_progs_zone_cvar, 0, 0);
Cvar_Register (&sv_progs_stack_cvar, 0, 0);
Cvar_Register (&sv_progs_ext_cvar, 0, 0);
Cvar_Register (&pr_checkextensions_cvar, 0, 0);
Cvar_Register (&nomonsters_cvar, 0, 0);
Cvar_Register (&gamecfg_cvar, 0, 0);
Cvar_Register (&scratch1_cvar, 0, 0);
Cvar_Register (&scratch2_cvar, 0, 0);
Cvar_Register (&scratch3_cvar, 0, 0);
Cvar_Register (&scratch4_cvar, 0, 0);
Cvar_Register (&savedgamecfg_cvar, 0, 0);
Cvar_Register (&saved1_cvar, 0, 0);
Cvar_Register (&saved2_cvar, 0, 0);
Cvar_Register (&saved3_cvar, 0, 0);
Cvar_Register (&saved4_cvar, 0, 0);
}