allow progs to register user commands (but not override any of the standard

ones (yet))
This commit is contained in:
Bill Currie 2002-07-05 06:43:47 +00:00
parent 0ef4a7513d
commit cb814165f8
4 changed files with 97 additions and 47 deletions

View file

@ -461,6 +461,7 @@ void SV_SaveSpawnparms (void);
void SV_Physics_Client (struct edict_s *ent); void SV_Physics_Client (struct edict_s *ent);
void SV_SetupUserCommands (void);
void SV_ExecuteUserCommand (const char *s); void SV_ExecuteUserCommand (const char *s);
void SV_InitOperatorCommands (void); void SV_InitOperatorCommands (void);

View file

@ -334,6 +334,7 @@ SV_SpawnServer (const char *server)
// load progs to get entity field count which determines how big each // load progs to get entity field count which determines how big each
// edict is // edict is
SV_LoadProgs (); SV_LoadProgs ();
SV_SetupUserCommands ();
Info_SetValueForStarKey (svs.info, "*progs", va ("%i", sv_pr_state.crc), Info_SetValueForStarKey (svs.info, "*progs", va ("%i", sv_pr_state.crc),
!sv_highchars->int_val); !sv_highchars->int_val);

View file

@ -180,7 +180,7 @@ SV_LoadProgs (void)
PR_LoadProgs (&sv_pr_state, sv_progs->string, MAX_EDICTS, PR_LoadProgs (&sv_pr_state, sv_progs->string, MAX_EDICTS,
sv_progs_zone->int_val * 1024); sv_progs_zone->int_val * 1024);
if (!sv_pr_state.progs) if (!sv_pr_state.progs || !PR_RelocateBuiltins (&sv_pr_state))
Sys_Error ("SV_LoadProgs: couldn't load %s", sv_progs->string); Sys_Error ("SV_LoadProgs: couldn't load %s", sv_progs->string);
// progs engine needs these globals anyway // progs engine needs these globals anyway
sv_globals.self = sv_pr_state.globals.self; sv_globals.self = sv_pr_state.globals.self;

View file

@ -46,6 +46,7 @@ static const char rcsid[] =
#include "QF/clip_hull.h" #include "QF/clip_hull.h"
#include "QF/cmd.h" #include "QF/cmd.h"
#include "QF/cvar.h" #include "QF/cvar.h"
#include "QF/hash.h"
#include "QF/msg.h" #include "QF/msg.h"
#include "QF/sys.h" #include "QF/sys.h"
#include "QF/va.h" #include "QF/va.h"
@ -59,6 +60,14 @@ static const char rcsid[] =
#include "sv_progs.h" #include "sv_progs.h"
#include "world.h" #include "world.h"
typedef struct ucmd_s {
const char *name;
void (*func) (struct ucmd_s *cmd);
int no_redirect;
func_t qc_hook;
int freeable;
} ucmd_t;
edict_t *sv_player; edict_t *sv_player;
usercmd_t cmd; usercmd_t cmd;
@ -93,7 +102,7 @@ void SV_FullClientUpdateToClient (client_t *client, client_t *cl);
This will be sent on the initial connection and upon each server load. This will be sent on the initial connection and upon each server load.
*/ */
void void
SV_New_f (void) SV_New_f (ucmd_t *cmd)
{ {
const char *gamedir; const char *gamedir;
int playernum; int playernum;
@ -162,7 +171,7 @@ SV_New_f (void)
SV_Soundlist_f SV_Soundlist_f
*/ */
void void
SV_Soundlist_f (void) SV_Soundlist_f (ucmd_t *cmd)
{ {
const char **s; const char **s;
unsigned n; unsigned n;
@ -174,14 +183,14 @@ SV_Soundlist_f (void)
// handle the case of a level changing while a client was connecting // handle the case of a level changing while a client was connecting
if (atoi (Cmd_Argv (1)) != svs.spawncount) { if (atoi (Cmd_Argv (1)) != svs.spawncount) {
SV_Printf ("SV_Soundlist_f from different level\n"); SV_Printf ("SV_Soundlist_f from different level\n");
SV_New_f (); SV_New_f (0);
return; return;
} }
n = atoi (Cmd_Argv (2)); n = atoi (Cmd_Argv (2));
if (n >= MAX_SOUNDS) { if (n >= MAX_SOUNDS) {
SV_Printf ("SV_Soundlist_f: Invalid soundlist index\n"); SV_Printf ("SV_Soundlist_f: Invalid soundlist index\n");
SV_New_f (); SV_New_f (0);
return; return;
} }
//NOTE: This doesn't go through ClientReliableWrite since it's before the user //NOTE: This doesn't go through ClientReliableWrite since it's before the user
@ -212,7 +221,7 @@ SV_Soundlist_f (void)
SV_Modellist_f SV_Modellist_f
*/ */
void void
SV_Modellist_f (void) SV_Modellist_f (ucmd_t *cmd)
{ {
const char **s; const char **s;
unsigned n; unsigned n;
@ -224,14 +233,14 @@ SV_Modellist_f (void)
// handle the case of a level changing while a client was connecting // handle the case of a level changing while a client was connecting
if (atoi (Cmd_Argv (1)) != svs.spawncount) { if (atoi (Cmd_Argv (1)) != svs.spawncount) {
SV_Printf ("SV_Modellist_f from different level\n"); SV_Printf ("SV_Modellist_f from different level\n");
SV_New_f (); SV_New_f (0);
return; return;
} }
n = atoi (Cmd_Argv (2)); n = atoi (Cmd_Argv (2));
if (n >= MAX_MODELS) { if (n >= MAX_MODELS) {
SV_Printf ("SV_Modellist_f: Invalid modellist index\n"); SV_Printf ("SV_Modellist_f: Invalid modellist index\n");
SV_New_f (); SV_New_f (0);
return; return;
} }
//NOTE: This doesn't go through ClientReliableWrite since it's before the user //NOTE: This doesn't go through ClientReliableWrite since it's before the user
@ -261,7 +270,7 @@ SV_Modellist_f (void)
SV_PreSpawn_f SV_PreSpawn_f
*/ */
void void
SV_PreSpawn_f (void) SV_PreSpawn_f (ucmd_t *cmd)
{ {
unsigned int buf; unsigned int buf;
unsigned int check; unsigned int check;
@ -276,7 +285,7 @@ SV_PreSpawn_f (void)
// handle the case of a level changing while a client was connecting // handle the case of a level changing while a client was connecting
if (atoi (Cmd_Argv (1)) != svs.spawncount) { if (atoi (Cmd_Argv (1)) != svs.spawncount) {
SV_Printf ("SV_PreSpawn_f from different level\n"); SV_Printf ("SV_PreSpawn_f from different level\n");
SV_New_f (); SV_New_f (0);
return; return;
} }
@ -328,7 +337,7 @@ SV_PreSpawn_f (void)
SV_Spawn_f SV_Spawn_f
*/ */
void void
SV_Spawn_f (void) SV_Spawn_f (ucmd_t *cmd)
{ {
int i; int i;
client_t *client; client_t *client;
@ -342,7 +351,7 @@ SV_Spawn_f (void)
// handle the case of a level changing while a client was connecting // handle the case of a level changing while a client was connecting
if (atoi (Cmd_Argv (1)) != svs.spawncount) { if (atoi (Cmd_Argv (1)) != svs.spawncount) {
SV_Printf ("SV_Spawn_f from different level\n"); SV_Printf ("SV_Spawn_f from different level\n");
SV_New_f (); SV_New_f (0);
return; return;
} }
// make sure they're not trying to cheat by spawning without prespawning // make sure they're not trying to cheat by spawning without prespawning
@ -359,7 +368,7 @@ SV_Spawn_f (void)
// make sure n is valid // make sure n is valid
if (n < 0 || n > MAX_CLIENTS) { if (n < 0 || n > MAX_CLIENTS) {
SV_Printf ("SV_Spawn_f invalid client start\n"); SV_Printf ("SV_Spawn_f invalid client start\n");
SV_New_f (); SV_New_f (0);
return; return;
} }
@ -457,7 +466,7 @@ SV_SpawnSpectator (void)
SV_Begin_f SV_Begin_f
*/ */
void void
SV_Begin_f (void) SV_Begin_f (ucmd_t *cmd)
{ {
unsigned int pmodel = 0, emodel = 0; unsigned int pmodel = 0, emodel = 0;
int i; int i;
@ -470,7 +479,7 @@ SV_Begin_f (void)
// handle the case of a level changing while a client was connecting // handle the case of a level changing while a client was connecting
if (atoi (Cmd_Argv (1)) != svs.spawncount) { if (atoi (Cmd_Argv (1)) != svs.spawncount) {
SV_Printf ("SV_Begin_f from different level\n"); SV_Printf ("SV_Begin_f from different level\n");
SV_New_f (); SV_New_f (0);
return; return;
} }
@ -557,7 +566,7 @@ SV_Begin_f (void)
SV_NextDownload_f SV_NextDownload_f
*/ */
void void
SV_NextDownload_f (void) SV_NextDownload_f (ucmd_t *cmd)
{ {
byte buffer[1024]; byte buffer[1024];
int r; int r;
@ -682,7 +691,7 @@ SV_NextUpload (void)
SV_BeginDownload_f SV_BeginDownload_f
*/ */
void void
SV_BeginDownload_f (void) SV_BeginDownload_f (ucmd_t *cmd)
{ {
const char *name; const char *name;
VFile *file; VFile *file;
@ -761,7 +770,7 @@ SV_BeginDownload_f (void)
ClientReliable_FinishWrite (host_client); ClientReliable_FinishWrite (host_client);
} }
SV_NextDownload_f (); SV_NextDownload_f (0);
SV_Printf ("Downloading %s to %s\n", name, host_client->name); SV_Printf ("Downloading %s to %s\n", name, host_client->name);
} }
@ -876,7 +885,7 @@ SV_Say (qboolean team)
SV_Say_f SV_Say_f
*/ */
void void
SV_Say_f (void) SV_Say_f (ucmd_t *cmd)
{ {
SV_Say (false); SV_Say (false);
} }
@ -885,7 +894,7 @@ SV_Say_f (void)
SV_Say_Team_f SV_Say_Team_f
*/ */
void void
SV_Say_Team_f (void) SV_Say_Team_f (ucmd_t *cmd)
{ {
SV_Say (true); SV_Say (true);
} }
@ -901,7 +910,7 @@ SV_Say_Team_f (void)
clients clients
*/ */
void void
SV_Pings_f (void) SV_Pings_f (ucmd_t *cmd)
{ {
client_t *client; client_t *client;
int j; int j;
@ -925,7 +934,7 @@ SV_Pings_f (void)
SV_Kill_f SV_Kill_f
*/ */
void void
SV_Kill_f (void) SV_Kill_f (ucmd_t *cmd)
{ {
if (SVfloat (sv_player, health) <= 0) { if (SVfloat (sv_player, health) <= 0) {
SV_BeginRedirect (RD_CLIENT); SV_BeginRedirect (RD_CLIENT);
@ -968,7 +977,7 @@ SV_TogglePause (const char *msg)
SV_Pause_f SV_Pause_f
*/ */
void void
SV_Pause_f (void) SV_Pause_f (ucmd_t *cmd)
{ {
static double lastpausetime; static double lastpausetime;
double currenttime; double currenttime;
@ -1009,7 +1018,7 @@ SV_Pause_f (void)
The client is going to disconnect, so remove the connection immediately The client is going to disconnect, so remove the connection immediately
*/ */
void void
SV_Drop_f (void) SV_Drop_f (ucmd_t *cmd)
{ {
SV_EndRedirect (); SV_EndRedirect ();
if (!host_client->spectator) if (!host_client->spectator)
@ -1023,7 +1032,7 @@ SV_Drop_f (void)
Change the bandwidth estimate for a client Change the bandwidth estimate for a client
*/ */
void void
SV_PTrack_f (void) SV_PTrack_f (ucmd_t *cmd)
{ {
int i; int i;
edict_t *ent, *tent; edict_t *ent, *tent;
@ -1064,7 +1073,7 @@ SV_PTrack_f (void)
Change the bandwidth estimate for a client Change the bandwidth estimate for a client
*/ */
void void
SV_Rate_f (void) SV_Rate_f (ucmd_t *cmd)
{ {
int rate; int rate;
@ -1092,7 +1101,7 @@ SV_Rate_f (void)
Change the message level for a client Change the message level for a client
*/ */
void void
SV_Msg_f (void) SV_Msg_f (ucmd_t *cmd)
{ {
if (Cmd_Argc () != 2) { if (Cmd_Argc () != 2) {
SV_ClientPrintf (host_client, PRINT_HIGH, "Current msg level is %i\n", SV_ClientPrintf (host_client, PRINT_HIGH, "Current msg level is %i\n",
@ -1112,7 +1121,7 @@ SV_Msg_f (void)
Allow clients to change userinfo Allow clients to change userinfo
*/ */
void void
SV_SetInfo_f (void) SV_SetInfo_f (ucmd_t *cmd)
{ {
if (Cmd_Argc () == 1) { if (Cmd_Argc () == 1) {
SV_Printf ("User info settings:\n"); SV_Printf ("User info settings:\n");
@ -1167,13 +1176,13 @@ SV_SetInfo_f (void)
Dump serverinfo into a string Dump serverinfo into a string
*/ */
void void
SV_ShowServerinfo_f (void) SV_ShowServerinfo_f (ucmd_t *cmd)
{ {
Info_Print (svs.info); Info_Print (svs.info);
} }
void void
SV_NoSnap_f (void) SV_NoSnap_f (ucmd_t *cmd)
{ {
if (*host_client->uploadfn) { if (*host_client->uploadfn) {
*host_client->uploadfn = 0; *host_client->uploadfn = 0;
@ -1182,12 +1191,6 @@ SV_NoSnap_f (void)
} }
} }
typedef struct {
const char *name;
void (*func) (void);
int no_redirect;
} ucmd_t;
ucmd_t ucmds[] = { ucmd_t ucmds[] = {
{"new", SV_New_f}, {"new", SV_New_f},
{"modellist", SV_Modellist_f}, {"modellist", SV_Modellist_f},
@ -1221,12 +1224,60 @@ ucmd_t ucmds[] = {
}; };
static int static hashtab_t *ucmd_table;
ucmds_compare (const void *_a, const void *_b)
static void
call_qc_hook (ucmd_t *cmd)
{
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
PR_ExecuteProgram (&sv_pr_state, cmd->qc_hook);
}
static const char *
ucmds_getkey (void *_a, void *unused)
{ {
ucmd_t *a = (ucmd_t*)_a; ucmd_t *a = (ucmd_t*)_a;
ucmd_t *b = (ucmd_t*)_b; return a->name;
return strcmp (a->name, b->name); }
static void
ucmds_free (void *_c, void *unused)
{
ucmd_t *c = (ucmd_t*)_c;
if (c->freeable) {
free ((char *)c->name);
free (c);
}
}
void
SV_AddUserCommand (progs_t *pr)
{
ucmd_t *cmd;
const char *name = P_STRING (pr, 0);
cmd = Hash_Find (ucmd_table, name);
if (cmd) {
SV_Printf ("%s already a user command\n", name);
return;
}
cmd = malloc (sizeof (ucmd_t));
cmd->freeable = 1;
cmd->name = strdup (name);
cmd->func = call_qc_hook;
cmd->qc_hook = P_FUNCTION (pr, 1);
cmd->no_redirect = P_INT (pr, 2);
Hash_Add (ucmd_table, cmd);
}
void
SV_SetupUserCommands (void)
{
int i;
Hash_FlushTable (ucmd_table);
for (i = 0; i < sizeof (ucmds) / sizeof (ucmds[0]); i++)
Hash_Add (ucmd_table, &ucmds[i]);
} }
/* /*
@ -1238,14 +1289,11 @@ void
SV_ExecuteUserCommand (const char *s) SV_ExecuteUserCommand (const char *s)
{ {
ucmd_t *u; ucmd_t *u;
ucmd_t cmd;
Cmd_TokenizeString (s, true); Cmd_TokenizeString (s, true);
sv_player = host_client->edict; sv_player = host_client->edict;
cmd.name = Cmd_Argv(0);
u = (ucmd_t*) bsearch (&cmd, ucmds, sizeof (ucmds) / sizeof (ucmds[0]), u = (ucmd_t*) Hash_Find (ucmd_table, Cmd_Argv(0));
sizeof (ucmds[0]), ucmds_compare);
if (!u) { if (!u) {
SV_BeginRedirect (RD_CLIENT); SV_BeginRedirect (RD_CLIENT);
@ -1254,7 +1302,7 @@ SV_ExecuteUserCommand (const char *s)
} else { } else {
if (!u->no_redirect) if (!u->no_redirect)
SV_BeginRedirect (RD_CLIENT); SV_BeginRedirect (RD_CLIENT);
u->func (); u->func (u);
if (!u->no_redirect) if (!u->no_redirect)
SV_EndRedirect (); SV_EndRedirect ();
} }
@ -1795,8 +1843,8 @@ SV_ExecuteClientMessage (client_t *cl)
void void
SV_UserInit (void) SV_UserInit (void)
{ {
qsort (ucmds, sizeof (ucmds) / sizeof (ucmds[0]), sizeof (ucmds[0]), ucmd_table = Hash_NewTable (251, ucmds_getkey, ucmds_free, 0);
ucmds_compare); PR_AddBuiltin (&sv_pr_state, "SV_AddUserCommand", SV_AddUserCommand, -1);
cl_rollspeed = Cvar_Get ("cl_rollspeed", "200", CVAR_NONE, NULL, cl_rollspeed = Cvar_Get ("cl_rollspeed", "200", CVAR_NONE, NULL,
"How quickly a player straightens out after strafing"); "How quickly a player straightens out after strafing");
cl_rollangle = Cvar_Get ("cl_rollangle", "2", CVAR_NONE, NULL, cl_rollangle = Cvar_Get ("cl_rollangle", "2", CVAR_NONE, NULL,