better command output redirection handling: maplist over rcon should show

all (or most, there are limits still) of the maps on a server with many
maps.

move the optional progs funcs into sv_funcs_t and add UserInfoChanged,
ChatMessage and LocalinfoChanged callback support.

clean up PF_setinfo (and SV_SetInfo_f and SV_Localinfo_f) using shared code
where possible und to use the UserInfoChanged and LocalinfoChanged
callbacks.

add chat message callback to SV_Say. if it returns zero, normal chat
handling is done, otherwise it's assumed to have been handled by the progs.

provide a hook for unkown user commands. non-zero return means it's been
handled.
This commit is contained in:
Bill Currie 2003-11-21 06:09:21 +00:00
parent 886b766295
commit ce745c8078
11 changed files with 218 additions and 127 deletions

View file

@ -427,8 +427,6 @@ extern QFile *sv_fraglogfile;
extern double sv_frametime; extern double sv_frametime;
extern double realtime; extern double realtime;
extern struct progs_s sv_pr_state;
extern const char *client_info_filters[]; extern const char *client_info_filters[];
extern struct cbuf_s *sv_cbuf; extern struct cbuf_s *sv_cbuf;
@ -548,11 +546,19 @@ void *SV_AddUserCommand (const char *name, void (*func) (void *userdata),
void (*on_free) (void *userdata)); void (*on_free) (void *userdata));
int SV_RemoveUserCommand (void *cmd); int SV_RemoveUserCommand (void *cmd);
void SV_Spawn (client_t *client); void SV_Spawn (client_t *client);
void SV_SetUserinfo (client_t *client, const char *key, const char *value);
extern int (*ucmd_unknown)(void);
// //
// svonly.c // svonly.c
// //
typedef enum {RD_NONE, RD_CLIENT, RD_PACKET} redirect_t; typedef enum {
RD_NONE,
RD_CLIENT,
RD_PACKET,
RD_MOD,
} redirect_t;
void SV_BeginRedirect (redirect_t rd); void SV_BeginRedirect (redirect_t rd);
void SV_EndRedirect (void); void SV_EndRedirect (void);
extern redirect_t sv_redirected; extern redirect_t sv_redirected;
@ -562,6 +568,7 @@ extern redirect_t sv_redirected;
// //
void SV_Status_f (void); void SV_Status_f (void);
const char *SV_Current_Map (void); const char *SV_Current_Map (void);
void SV_SetLocalinfo (const char *key, const char *value);
// //
@ -573,6 +580,7 @@ void SV_WriteEntitiesToClient (client_t *client, sizebuf_t *msg,
// //
// sv_nchan.c // sv_nchan.c
// //
int ClientReliableCheckSize (client_t *cl, int maxsize, int minsize);
void ClientReliableCheckBlock(client_t *cl, int maxsize); void ClientReliableCheckBlock(client_t *cl, int maxsize);
void ClientReliable_FinishWrite(client_t *cl); void ClientReliable_FinishWrite(client_t *cl);
void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize); void ClientReliableWrite_Begin(client_t *cl, int c, int maxsize);
@ -585,7 +593,7 @@ void ClientReliableWrite_Coord(client_t *cl, float f);
void ClientReliableWrite_Long(client_t *cl, int c); void ClientReliableWrite_Long(client_t *cl, int c);
void ClientReliableWrite_Short(client_t *cl, int c); void ClientReliableWrite_Short(client_t *cl, int c);
void ClientReliableWrite_String(client_t *cl, const char *s); void ClientReliableWrite_String(client_t *cl, const char *s);
void ClientReliableWrite_SZ(client_t *cl, void *data, int len); void ClientReliableWrite_SZ(client_t *cl, const void *data, int len);
void ClientReliableWrite_AngleV(client_t *cl, const vec3_t v); void ClientReliableWrite_AngleV(client_t *cl, const vec3_t v);
void ClientReliableWrite_CoordV(client_t *cl, const vec3_t v); void ClientReliableWrite_CoordV(client_t *cl, const vec3_t v);

View file

@ -102,6 +102,7 @@ typedef struct {
byte *mfile; byte *mfile;
byte buffer[20 * MAX_MSGLEN]; byte buffer[20 * MAX_MSGLEN];
int bufsize; int bufsize;
int forceFrame;
} demo_t; } demo_t;
extern demo_t demo; extern demo_t demo;

View file

@ -79,6 +79,15 @@ typedef struct {
func_t ClientDisconnect; func_t ClientDisconnect;
func_t SetNewParms; func_t SetNewParms;
func_t SetChangeParms; func_t SetChangeParms;
func_t EndFrame;
func_t SpectatorConnect;
func_t SpectatorThink;
func_t SpectatorDisconnect;
func_t UserInfoCallback;
func_t UserInfoChanged;
func_t ChatMessage;
func_t LocalinfoChanged;
} sv_funcs_t; } sv_funcs_t;
extern sv_funcs_t sv_funcs; extern sv_funcs_t sv_funcs;
@ -159,10 +168,14 @@ typedef struct
int gravity; int gravity;
int maxspeed; int maxspeed;
int team_str; //string
} sv_fields_t; } sv_fields_t;
extern sv_fields_t sv_fields; extern sv_fields_t sv_fields;
extern struct progs_s sv_pr_state;
#if TYPECHECK_PROGS #if TYPECHECK_PROGS
#define SVFIELD(e,f,t) E_var (e, PR_AccessField (&sv_pr_state, #f, ev_##t, __FILE__, __LINE__), t) #define SVFIELD(e,f,t) E_var (e, PR_AccessField (&sv_pr_state, #f, ev_##t, __FILE__, __LINE__), t)
#else #else
@ -178,12 +191,6 @@ extern sv_fields_t sv_fields;
#define PROGHEADER_CRC 54730 #define PROGHEADER_CRC 54730
extern func_t EndFrame;
extern func_t SpectatorConnect;
extern func_t SpectatorThink;
extern func_t SpectatorDisconnect;
extern func_t UserInfoCallback;
static inline void static inline void
sv_pr_touch (edict_t *self, edict_t *other) sv_pr_touch (edict_t *self, edict_t *other)
{ {

View file

@ -481,7 +481,7 @@ SV_Status_f (void)
SV_Printf ("packets/frame : %5.2f\n", pak); SV_Printf ("packets/frame : %5.2f\n", pak);
// min fps lat drp // min fps lat drp
if (sv_redirected != RD_NONE) { if (sv_redirected != RD_NONE && sv_redirected != RD_MOD) {
// most remote clients are 40 columns // most remote clients are 40 columns
// 0123456789012345678901234567890123456789 // 0123456789012345678901234567890123456789
SV_Printf ("name userid frags\n"); SV_Printf ("name userid frags\n");
@ -843,6 +843,32 @@ SV_Serverinfo_f (void)
} }
} }
void
SV_SetLocalinfo (const char *key, const char *value)
{
char *oldvalue = 0;
if (sv_funcs.LocalinfoChanged)
oldvalue = strdup (Info_ValueForKey (localinfo, key));
if (*value)
Info_SetValueForKey (localinfo, key, value, !sv_highchars->int_val);
else
Info_RemoveKey (localinfo, key);
if (sv_funcs.LocalinfoChanged) {
*sv_globals.time = sv.time;
*sv_globals.self = 0;
P_STRING (&sv_pr_state, 0) = PR_SetString (&sv_pr_state, key);
P_STRING (&sv_pr_state, 1) = PR_SetString (&sv_pr_state, oldvalue);
P_STRING (&sv_pr_state, 2) = PR_SetString (&sv_pr_state, value);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.LocalinfoChanged);
}
if (oldvalue)
free (oldvalue);
}
/* /*
SV_Serverinfo_f SV_Serverinfo_f
@ -866,11 +892,7 @@ SV_Localinfo_f (void)
SV_Printf ("Star variables cannot be changed.\n"); SV_Printf ("Star variables cannot be changed.\n");
return; return;
} }
if (*Cmd_Argv (2)) SV_SetLocalinfo (Cmd_Argv (1), Cmd_Argv (2));
Info_SetValueForKey (localinfo, Cmd_Argv (1), Cmd_Argv (2),
!sv_highchars->int_val);
else
Info_RemoveKey (localinfo, Cmd_Argv (1));
} }
/* /*

View file

@ -344,11 +344,11 @@ SV_DropClient (client_t *drop)
// this will set the body to a dead frame, among other things // this will set the body to a dead frame, among other things
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, drop->edict); *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, drop->edict);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.ClientDisconnect); PR_ExecuteProgram (&sv_pr_state, sv_funcs.ClientDisconnect);
} else if (SpectatorDisconnect) { } else if (sv_funcs.SpectatorDisconnect) {
// call the prog function for removing a client // call the prog function for removing a client
// this will set the body to a dead frame, among other things // this will set the body to a dead frame, among other things
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, drop->edict); *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, drop->edict);
PR_ExecuteProgram (&sv_pr_state, SpectatorDisconnect); PR_ExecuteProgram (&sv_pr_state, sv_funcs.SpectatorDisconnect);
} }
} }
if (drop->spectator) if (drop->spectator)

View file

@ -54,14 +54,33 @@ PushBackbuf (client_t *cl)
cl->num_backbuf++; cl->num_backbuf++;
} }
int
ClientReliableCheckSize (client_t *cl, int maxsize, int minsize)
{
sizebuf_t *msg = &cl->netchan.message;
if (cl->num_backbuf)
msg = &cl->backbuf;
if (maxsize <= msg->maxsize - msg->cursize - 1)
return maxsize;
if (minsize <= msg->maxsize - msg->cursize - 1)
return msg->maxsize - msg->cursize - 1;
if (cl->num_backbuf == MAX_BACK_BUFFERS)
return 0;
return cl->backbuf.maxsize;
}
// check to see if client block will fit, if not, rotate buffers // check to see if client block will fit, if not, rotate buffers
void void
ClientReliableCheckBlock (client_t *cl, int maxsize) ClientReliableCheckBlock (client_t *cl, int maxsize)
{ {
if (cl->num_backbuf || sizebuf_t *msg = &cl->netchan.message;
cl->netchan.message.cursize > cl->netchan.message.maxsize - maxsize -
1) { if (cl->num_backbuf || msg->cursize > msg->maxsize - maxsize - 1) {
// we would probably overflow the buffer, save it for next // we would probably overflow the buffer, save it for next
if (!cl->num_backbuf) { if (!cl->num_backbuf) {
PushBackbuf (cl); PushBackbuf (cl);
@ -72,8 +91,7 @@ ClientReliableCheckBlock (client_t *cl, int maxsize)
SV_Printf ("WARNING: MAX_BACK_BUFFERS for %s\n", cl->name); SV_Printf ("WARNING: MAX_BACK_BUFFERS for %s\n", cl->name);
cl->backbuf.cursize = 0; // don't overflow without cl->backbuf.cursize = 0; // don't overflow without
// allowoverflow set // allowoverflow set
cl->netchan.message.overflowed = true; // this will drop the msg->overflowed = true; // this will drop the client
// client
return; return;
} }
PushBackbuf (cl); PushBackbuf (cl);
@ -195,7 +213,7 @@ ClientReliableWrite_String (client_t *cl, const char *s)
} }
void void
ClientReliableWrite_SZ (client_t *cl, void *data, int len) ClientReliableWrite_SZ (client_t *cl, const void *data, int len)
{ {
if (cl->num_backbuf) { if (cl->num_backbuf) {
SZ_Write (&cl->backbuf, data, len); SZ_Write (&cl->backbuf, data, len);

View file

@ -813,11 +813,11 @@ SV_Physics (void)
if (*sv_globals.force_retouch) if (*sv_globals.force_retouch)
(*sv_globals.force_retouch)--; (*sv_globals.force_retouch)--;
if (EndFrame) { if (sv_funcs.EndFrame) {
// let the progs know that the frame has ended // let the progs know that the frame has ended
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv.edicts); *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv.edicts);
*sv_globals.other = EDICT_TO_PROG (&sv_pr_state, sv.edicts); *sv_globals.other = EDICT_TO_PROG (&sv_pr_state, sv.edicts);
*sv_globals.time = sv.time; *sv_globals.time = sv.time;
PR_ExecuteProgram (&sv_pr_state, EndFrame); PR_ExecuteProgram (&sv_pr_state, sv_funcs.EndFrame);
} }
} }

View file

@ -1468,8 +1468,7 @@ PF_multicast (progs_t *pr)
static void static void
PF_cfopen (progs_t *pr) PF_cfopen (progs_t *pr)
{ {
R_FLOAT (pr) = CF_Open (P_GSTRING (pr, 0), R_FLOAT (pr) = CF_Open (P_GSTRING (pr, 0), P_GSTRING (pr, 1));
P_GSTRING (pr, 1));
} }
/* /*
@ -1534,41 +1533,11 @@ PF_setinfokey (progs_t *pr)
int e1 = NUM_FOR_EDICT (pr, edict); int e1 = NUM_FOR_EDICT (pr, edict);
const char *key = P_GSTRING (pr, 1); const char *key = P_GSTRING (pr, 1);
const char *value = P_GSTRING (pr, 2); const char *value = P_GSTRING (pr, 2);
char *oldval = 0;
if (e1 == 0) { if (e1 == 0) {
if (*value) SV_SetLocalinfo (key, value);
Info_SetValueForKey (localinfo, key, value,
!sv_highchars->int_val);
else
Info_RemoveKey (localinfo, key);
} else if (e1 <= MAX_CLIENTS) { } else if (e1 <= MAX_CLIENTS) {
if (sv_setinfo_e->func) SV_SetUserinfo (&svs.clients[e1 - 1], key, value);
oldval = strdup (Info_ValueForKey (svs.clients[e1 - 1].userinfo,
key));
Info_SetValueForKey (svs.clients[e1 - 1].userinfo, key, value,
!sv_highchars->int_val);
SV_ExtractFromUserinfo (&svs.clients[e1 - 1]);
// trigger a GIB event
if (sv_setinfo_e->func)
GIB_Event_Callback (sv_setinfo_e, 4,
va("%d", svs.clients[e1 - 1].userid),
key, oldval,
Info_ValueForKey (svs.clients[e1 - 1].userinfo,
key));
if (oldval)
free (oldval);
if (Info_FilterForKey (key, client_info_filters)) {
MSG_WriteByte (&sv.reliable_datagram, svc_setinfo);
MSG_WriteByte (&sv.reliable_datagram, e1 - 1);
MSG_WriteString (&sv.reliable_datagram, key);
MSG_WriteString (&sv.reliable_datagram,
Info_ValueForKey (svs.clients[e1 - 1].userinfo,
key));
}
} }
} }

View file

@ -57,12 +57,6 @@ cvar_t *pr_checkextensions;
cvar_t *sv_old_entity_free; cvar_t *sv_old_entity_free;
cvar_t *sv_hide_version_info; cvar_t *sv_hide_version_info;
func_t EndFrame;
func_t SpectatorConnect;
func_t SpectatorDisconnect;
func_t SpectatorThink;
func_t UserInfoCallback;
static int reserved_edicts = MAX_CLIENTS; static int reserved_edicts = MAX_CLIENTS;
static void static void
@ -169,6 +163,8 @@ SV_LoadProgs (void)
dfunction_t *f; dfunction_t *f;
const char *progs_name = "qwprogs.dat"; const char *progs_name = "qwprogs.dat";
memset (&sv_funcs, 0, sizeof (sv_funcs));
if (qfs_gamedir->gamecode && *qfs_gamedir->gamecode) if (qfs_gamedir->gamecode && *qfs_gamedir->gamecode)
progs_name = qfs_gamedir->gamecode; progs_name = qfs_gamedir->gamecode;
if (*sv_progs->string) if (*sv_progs->string)
@ -313,21 +309,18 @@ SV_LoadProgs (void)
sv_fields.rotated_bbox = ED_GetFieldIndex (&sv_pr_state, "rotated_bbox"); sv_fields.rotated_bbox = ED_GetFieldIndex (&sv_pr_state, "rotated_bbox");
// Zoid, find the spectator functions // Zoid, find the spectator functions
SpectatorConnect = SpectatorThink = SpectatorDisconnect = 0;
EndFrame = UserInfoCallback = 0;
if ((f = ED_FindFunction (&sv_pr_state, "SpectatorConnect")) != NULL) if ((f = ED_FindFunction (&sv_pr_state, "SpectatorConnect")) != NULL)
SpectatorConnect = (func_t) (f - sv_pr_state.pr_functions); sv_funcs.SpectatorConnect = (func_t) (f - sv_pr_state.pr_functions);
if ((f = ED_FindFunction (&sv_pr_state, "SpectatorThink")) != NULL) if ((f = ED_FindFunction (&sv_pr_state, "SpectatorThink")) != NULL)
SpectatorThink = (func_t) (f - sv_pr_state.pr_functions); sv_funcs.SpectatorThink = (func_t) (f - sv_pr_state.pr_functions);
if ((f = ED_FindFunction (&sv_pr_state, "SpectatorDisconnect")) != NULL) if ((f = ED_FindFunction (&sv_pr_state, "SpectatorDisconnect")) != NULL)
SpectatorDisconnect = (func_t) (f - sv_pr_state.pr_functions); sv_funcs.SpectatorDisconnect = (func_t) (f - sv_pr_state.pr_functions);
if ((f = ED_FindFunction (&sv_pr_state, "UserInfoCallback")) != NULL) if ((f = ED_FindFunction (&sv_pr_state, "UserInfoCallback")) != NULL)
UserInfoCallback = (func_t) (f - sv_pr_state.pr_functions); sv_funcs.UserInfoCallback = (func_t) (f - sv_pr_state.pr_functions);
// 2000-01-02 EndFrame function by Maddes/FrikaC // 2000-01-02 EndFrame function by Maddes/FrikaC
if ((f = ED_FindFunction (&sv_pr_state, "EndFrame")) != NULL) if ((f = ED_FindFunction (&sv_pr_state, "EndFrame")) != NULL)
EndFrame = (func_t) (f - sv_pr_state.pr_functions); sv_funcs.EndFrame = (func_t) (f - sv_pr_state.pr_functions);
sv_fields.alpha = ED_GetFieldIndex (&sv_pr_state, "alpha"); sv_fields.alpha = ED_GetFieldIndex (&sv_pr_state, "alpha");
sv_fields.scale = ED_GetFieldIndex (&sv_pr_state, "scale"); sv_fields.scale = ED_GetFieldIndex (&sv_pr_state, "scale");

View file

@ -44,6 +44,7 @@ static __attribute__ ((unused)) const char rcsid[] =
#include "QF/console.h" #include "QF/console.h"
#include "QF/cvar.h" #include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/msg.h" #include "QF/msg.h"
#include "QF/sound.h" // FIXME: DEFAULT_SOUND_PACKET_* #include "QF/sound.h" // FIXME: DEFAULT_SOUND_PACKET_*
#include "QF/sys.h" #include "QF/sys.h"
@ -62,7 +63,7 @@ static __attribute__ ((unused)) const char rcsid[] =
/* SV_Printf redirection */ /* SV_Printf redirection */
char outputbuf[8000]; dstring_t outputbuf;
int con_printf_no_log; int con_printf_no_log;
redirect_t sv_redirected; redirect_t sv_redirected;
@ -71,24 +72,62 @@ static void
SV_FlushRedirect (void) SV_FlushRedirect (void)
{ {
char send[8000 + 6]; char send[8000 + 6];
int count;
int bytes;
const char *p;
if (!outputbuf.size)
return;
count = strlen (outputbuf.str);
if (sv_redirected == RD_PACKET) { if (sv_redirected == RD_PACKET) {
send[0] = 0xff; send[0] = 0xff;
send[1] = 0xff; send[1] = 0xff;
send[2] = 0xff; send[2] = 0xff;
send[3] = 0xff; send[3] = 0xff;
send[4] = A2C_PRINT; send[4] = A2C_PRINT;
memcpy (send + 5, outputbuf, strlen (outputbuf) + 1);
Netchan_SendPacket (strlen (send) + 1, send, net_from); p = outputbuf.str;
} else if (sv_redirected == RD_CLIENT) { while (count) {
ClientReliableWrite_Begin (host_client, svc_print, bytes = min (count, sizeof (send) - 5);
strlen (outputbuf) + 3); memcpy (send + 5, p, bytes);
ClientReliableWrite_Byte (host_client, PRINT_HIGH); send[5 + bytes] = 0;
ClientReliableWrite_String (host_client, outputbuf); Netchan_SendPacket (bytes + 1, send, net_from);
p += bytes;
count -= bytes;
}
} else if (sv_redirected == RD_CLIENT || sv_redirected > RD_MOD) {
client_t *cl;
if (sv_redirected > RD_MOD) {
cl = svs.clients + sv_redirected - RD_MOD - 1;
if (cl->state != cs_spawned)
count = 0;
} else {
cl = host_client;
}
p = outputbuf.str;
while (count) {
// +/- 3 for svc_print, PRINT_HIGH and nul byte
// min of 4 because we don't want to send an effectively empty
// message
bytes = ClientReliableCheckSize (cl, count + 3, 4) - 3;
// if writing another packet would overflow the client, just drop
// the rest of the data. getting rudely disconnected would be much
// more annoying than losing the tail end of the output
if (bytes <= 0)
break;
ClientReliableWrite_Begin (cl, svc_print, bytes + 3);
ClientReliableWrite_Byte (cl, PRINT_HIGH);
ClientReliableWrite_SZ (cl, p, bytes);
p += bytes;
count -= bytes;
}
} }
// RD_MOD doesn't do anything :)
// clear it // clear it
outputbuf[0] = 0; dstring_clear (&outputbuf);
} }
/* /*
@ -101,7 +140,7 @@ void
SV_BeginRedirect (redirect_t rd) SV_BeginRedirect (redirect_t rd)
{ {
sv_redirected = rd; sv_redirected = rd;
outputbuf[0] = 0; dstring_clear (&outputbuf);
} }
void void
@ -171,9 +210,7 @@ SV_Print (const char *fmt, va_list args)
*out = '\0'; *out = '\0';
if (sv_redirected) { // Add to redirected message if (sv_redirected) { // Add to redirected message
if (strlen (msg) + strlen (outputbuf) > sizeof (outputbuf) - 1) dstring_appendstr (&outputbuf, msg);
SV_FlushRedirect ();
strncat (outputbuf, msg, sizeof (outputbuf) - strlen (outputbuf));
} }
if (!con_printf_no_log) { if (!con_printf_no_log) {
// We want to output to console and maybe logfile // We want to output to console and maybe logfile
@ -820,8 +857,9 @@ SV_SendDemoMessage (void)
min_fps = sv_demofps->value; min_fps = sv_demofps->value;
min_fps = max (4, min_fps); min_fps = max (4, min_fps);
if (sv.time - demo.time < 1.0 / min_fps) if (!demo.forceFrame && sv.time - demo.time < 1.0 / min_fps)
return; return;
demo.forceFrame = 0;
for (i = 0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++) { for (i = 0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++) {
if (c->state != cs_spawned) if (c->state != cs_spawned)

View file

@ -491,7 +491,7 @@ SV_Begin_f (void *unused)
if (host_client->spectator) { if (host_client->spectator) {
SV_SpawnSpectator (); SV_SpawnSpectator ();
if (SpectatorConnect) { if (sv_funcs.SpectatorConnect) {
// copy spawn parms out of the client_t // copy spawn parms out of the client_t
for (i = 0; i < NUM_SPAWN_PARMS; i++) for (i = 0; i < NUM_SPAWN_PARMS; i++)
sv_globals.parms[i] = host_client->spawn_parms[i]; sv_globals.parms[i] = host_client->spawn_parms[i];
@ -499,7 +499,7 @@ SV_Begin_f (void *unused)
// call the spawn function // call the spawn function
*sv_globals.time = sv.time; *sv_globals.time = sv.time;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player); *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
PR_ExecuteProgram (&sv_pr_state, SpectatorConnect); PR_ExecuteProgram (&sv_pr_state, sv_funcs.SpectatorConnect);
} }
} else { } else {
// copy spawn parms out of the client_t // copy spawn parms out of the client_t
@ -787,6 +787,17 @@ SV_Say (qboolean team)
host_client->whensaid[host_client->whensaidhead] = realtime; host_client->whensaid[host_client->whensaidhead] = realtime;
} }
if (sv_funcs.ChatMessage) {
P_STRING (&sv_pr_state, 0) = PR_SetString (&sv_pr_state, p);
G_FLOAT (&sv_pr_state, 1) = (float) team;
*sv_globals.time = sv.time;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.ChatMessage);
if (R_FLOAT (&sv_pr_state))
return;
}
text = dstring_new (); text = dstring_new ();
if (host_client->spectator && (!sv_spectalk->int_val || team)) { if (host_client->spectator && (!sv_spectalk->int_val || team)) {
@ -1088,6 +1099,49 @@ SV_Msg_f (void *unused)
host_client->messagelevel); host_client->messagelevel);
} }
void
SV_SetUserinfo (client_t *client, const char *key, const char *value)
{
char *oldvalue = 0;
if (sv_setinfo_e->func || sv_funcs.UserInfoChanged)
oldvalue = strdup (Info_ValueForKey (client->userinfo, key));
if (!Info_SetValueForKey (client->userinfo, key, value,
!sv_highchars->int_val)) {
// key hasn't changed
if (oldvalue)
free (oldvalue);
return;
}
// process any changed values
SV_ExtractFromUserinfo (client);
// trigger a GIB event
if (sv_setinfo_e->func)
GIB_Event_Callback (sv_setinfo_e, 4, va("%d", client->userid),
key, oldvalue, value);
if (sv_funcs.UserInfoChanged) {
*sv_globals.time = sv.time;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, client->edict);
P_STRING (&sv_pr_state, 0) = PR_SetString (&sv_pr_state, key);
P_STRING (&sv_pr_state, 1) = PR_SetString (&sv_pr_state, oldvalue);
P_STRING (&sv_pr_state, 2) = PR_SetString (&sv_pr_state, value);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.UserInfoChanged);
}
if (oldvalue)
free (oldvalue);
if (Info_FilterForKey (key, client_info_filters)) {
MSG_WriteByte (&sv.reliable_datagram, svc_setinfo);
MSG_WriteByte (&sv.reliable_datagram, client - svs.clients);
MSG_WriteString (&sv.reliable_datagram, key);
MSG_WriteString (&sv.reliable_datagram, value);
}
}
/* /*
SV_SetInfo_f SV_SetInfo_f
@ -1096,7 +1150,8 @@ SV_Msg_f (void *unused)
static void static void
SV_SetInfo_f (void *unused) SV_SetInfo_f (void *unused)
{ {
char *oldval; const char *key;
const char *value;
if (Cmd_Argc () == 1) { if (Cmd_Argc () == 1) {
SV_Printf ("User info settings:\n"); SV_Printf ("User info settings:\n");
@ -1112,41 +1167,18 @@ SV_SetInfo_f (void *unused)
if (Cmd_Argv (1)[0] == '*') if (Cmd_Argv (1)[0] == '*')
return; // don't set priveledged values return; // don't set priveledged values
if (UserInfoCallback) { key = Cmd_Argv (1);
value = Cmd_Argv (2);
if (sv_funcs.UserInfoCallback) {
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player); *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
P_STRING (&sv_pr_state, 0) = PR_SetString (&sv_pr_state, Cmd_Argv (1)); P_STRING (&sv_pr_state, 0) = PR_SetString (&sv_pr_state, key);
P_STRING (&sv_pr_state, 1) = PR_SetString (&sv_pr_state, Cmd_Argv (2)); P_STRING (&sv_pr_state, 1) = PR_SetString (&sv_pr_state, value);
PR_ExecuteProgram (&sv_pr_state, UserInfoCallback); PR_ExecuteProgram (&sv_pr_state, sv_funcs.UserInfoCallback);
return; return;
} }
oldval = strdup (Info_ValueForKey (host_client->userinfo, Cmd_Argv (1))); SV_SetUserinfo (host_client, key, value);
if (!Info_SetValueForKey (host_client->userinfo, Cmd_Argv (1),
Cmd_Argv (2), !sv_highchars->int_val)) {
// key hasn't changed
free (oldval);
return;
}
// process any changed values
SV_ExtractFromUserinfo (host_client);
// trigger a GIB event
if (sv_setinfo_e->func)
GIB_Event_Callback (sv_setinfo_e, 4, va("%d", host_client->userid),
Cmd_Argv (1), oldval,
Info_ValueForKey (host_client->userinfo,
Cmd_Argv (1)));
free (oldval);
if (Info_FilterForKey (Cmd_Argv (1), client_info_filters)) {
MSG_WriteByte (&sv.reliable_datagram, svc_setinfo);
MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
MSG_WriteString (&sv.reliable_datagram, Cmd_Argv (1));
MSG_WriteString (&sv.reliable_datagram,
Info_ValueForKey (host_client->userinfo,
Cmd_Argv (1)));
}
} }
/* /*
@ -1205,6 +1237,7 @@ ucmd_t ucmds[] = {
}; };
static hashtab_t *ucmd_table; static hashtab_t *ucmd_table;
int (*ucmd_unknown)(void);
static void static void
call_qc_hook (void *qc_hook) call_qc_hook (void *qc_hook)
@ -1340,9 +1373,11 @@ SV_ExecuteUserCommand (const char *s)
u = (ucmd_t*) Hash_Find (ucmd_table, sv_args->argv[0]->str); u = (ucmd_t*) Hash_Find (ucmd_table, sv_args->argv[0]->str);
if (!u) { if (!u) {
SV_BeginRedirect (RD_CLIENT); if (ucmd_unknown && !ucmd_unknown ()) {
SV_Printf ("Bad user command: %s\n", sv_args->argv[0]->str); SV_BeginRedirect (RD_CLIENT);
SV_EndRedirect (); SV_Printf ("Bad user command: %s\n", sv_args->argv[0]->str);
SV_EndRedirect ();
}
} else { } else {
if (!u->no_redirect) if (!u->no_redirect)
SV_BeginRedirect (RD_CLIENT); SV_BeginRedirect (RD_CLIENT);
@ -1705,10 +1740,10 @@ SV_PostRunCmd (void)
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player); *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.PlayerPostThink); PR_ExecuteProgram (&sv_pr_state, sv_funcs.PlayerPostThink);
SV_RunNewmis (); SV_RunNewmis ();
} else if (SpectatorThink) { } else if (sv_funcs.SpectatorThink) {
*sv_globals.time = sv.time; *sv_globals.time = sv.time;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player); *sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
PR_ExecuteProgram (&sv_pr_state, SpectatorThink); PR_ExecuteProgram (&sv_pr_state, sv_funcs.SpectatorThink);
} }
} }