mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-30 12:40:42 +00:00
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:
parent
886b766295
commit
ce745c8078
11 changed files with 218 additions and 127 deletions
|
@ -427,8 +427,6 @@ extern QFile *sv_fraglogfile;
|
|||
extern double sv_frametime;
|
||||
extern double realtime;
|
||||
|
||||
extern struct progs_s sv_pr_state;
|
||||
|
||||
extern const char *client_info_filters[];
|
||||
|
||||
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));
|
||||
int SV_RemoveUserCommand (void *cmd);
|
||||
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
|
||||
//
|
||||
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_EndRedirect (void);
|
||||
extern redirect_t sv_redirected;
|
||||
|
@ -562,6 +568,7 @@ extern redirect_t sv_redirected;
|
|||
//
|
||||
void SV_Status_f (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
|
||||
//
|
||||
int ClientReliableCheckSize (client_t *cl, int maxsize, int minsize);
|
||||
void ClientReliableCheckBlock(client_t *cl, int maxsize);
|
||||
void ClientReliable_FinishWrite(client_t *cl);
|
||||
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_Short(client_t *cl, int c);
|
||||
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_CoordV(client_t *cl, const vec3_t v);
|
||||
|
||||
|
|
|
@ -102,6 +102,7 @@ typedef struct {
|
|||
byte *mfile;
|
||||
byte buffer[20 * MAX_MSGLEN];
|
||||
int bufsize;
|
||||
int forceFrame;
|
||||
} demo_t;
|
||||
|
||||
extern demo_t demo;
|
||||
|
|
|
@ -79,6 +79,15 @@ typedef struct {
|
|||
func_t ClientDisconnect;
|
||||
func_t SetNewParms;
|
||||
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;
|
||||
|
||||
extern sv_funcs_t sv_funcs;
|
||||
|
@ -159,10 +168,14 @@ typedef struct
|
|||
|
||||
int gravity;
|
||||
int maxspeed;
|
||||
|
||||
int team_str; //string
|
||||
} sv_fields_t;
|
||||
|
||||
extern sv_fields_t sv_fields;
|
||||
|
||||
extern struct progs_s sv_pr_state;
|
||||
|
||||
#if TYPECHECK_PROGS
|
||||
#define SVFIELD(e,f,t) E_var (e, PR_AccessField (&sv_pr_state, #f, ev_##t, __FILE__, __LINE__), t)
|
||||
#else
|
||||
|
@ -178,12 +191,6 @@ extern sv_fields_t sv_fields;
|
|||
|
||||
#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
|
||||
sv_pr_touch (edict_t *self, edict_t *other)
|
||||
{
|
||||
|
|
|
@ -481,7 +481,7 @@ SV_Status_f (void)
|
|||
SV_Printf ("packets/frame : %5.2f\n", pak);
|
||||
|
||||
// min fps lat drp
|
||||
if (sv_redirected != RD_NONE) {
|
||||
if (sv_redirected != RD_NONE && sv_redirected != RD_MOD) {
|
||||
// most remote clients are 40 columns
|
||||
// 0123456789012345678901234567890123456789
|
||||
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
|
||||
|
||||
|
@ -866,11 +892,7 @@ SV_Localinfo_f (void)
|
|||
SV_Printf ("Star variables cannot be changed.\n");
|
||||
return;
|
||||
}
|
||||
if (*Cmd_Argv (2))
|
||||
Info_SetValueForKey (localinfo, Cmd_Argv (1), Cmd_Argv (2),
|
||||
!sv_highchars->int_val);
|
||||
else
|
||||
Info_RemoveKey (localinfo, Cmd_Argv (1));
|
||||
SV_SetLocalinfo (Cmd_Argv (1), Cmd_Argv (2));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -344,11 +344,11 @@ SV_DropClient (client_t *drop)
|
|||
// this will set the body to a dead frame, among other things
|
||||
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, drop->edict);
|
||||
PR_ExecuteProgram (&sv_pr_state, sv_funcs.ClientDisconnect);
|
||||
} else if (SpectatorDisconnect) {
|
||||
} else if (sv_funcs.SpectatorDisconnect) {
|
||||
// call the prog function for removing a client
|
||||
// this will set the body to a dead frame, among other things
|
||||
*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)
|
||||
|
|
|
@ -54,14 +54,33 @@ PushBackbuf (client_t *cl)
|
|||
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
|
||||
void
|
||||
ClientReliableCheckBlock (client_t *cl, int maxsize)
|
||||
{
|
||||
if (cl->num_backbuf ||
|
||||
cl->netchan.message.cursize > cl->netchan.message.maxsize - maxsize -
|
||||
1) {
|
||||
sizebuf_t *msg = &cl->netchan.message;
|
||||
|
||||
if (cl->num_backbuf || msg->cursize > msg->maxsize - maxsize - 1) {
|
||||
// we would probably overflow the buffer, save it for next
|
||||
if (!cl->num_backbuf) {
|
||||
PushBackbuf (cl);
|
||||
|
@ -72,8 +91,7 @@ ClientReliableCheckBlock (client_t *cl, int maxsize)
|
|||
SV_Printf ("WARNING: MAX_BACK_BUFFERS for %s\n", cl->name);
|
||||
cl->backbuf.cursize = 0; // don't overflow without
|
||||
// allowoverflow set
|
||||
cl->netchan.message.overflowed = true; // this will drop the
|
||||
// client
|
||||
msg->overflowed = true; // this will drop the client
|
||||
return;
|
||||
}
|
||||
PushBackbuf (cl);
|
||||
|
@ -195,7 +213,7 @@ ClientReliableWrite_String (client_t *cl, const char *s)
|
|||
}
|
||||
|
||||
void
|
||||
ClientReliableWrite_SZ (client_t *cl, void *data, int len)
|
||||
ClientReliableWrite_SZ (client_t *cl, const void *data, int len)
|
||||
{
|
||||
if (cl->num_backbuf) {
|
||||
SZ_Write (&cl->backbuf, data, len);
|
||||
|
|
|
@ -813,11 +813,11 @@ SV_Physics (void)
|
|||
if (*sv_globals.force_retouch)
|
||||
(*sv_globals.force_retouch)--;
|
||||
|
||||
if (EndFrame) {
|
||||
if (sv_funcs.EndFrame) {
|
||||
// let the progs know that the frame has ended
|
||||
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv.edicts);
|
||||
*sv_globals.other = EDICT_TO_PROG (&sv_pr_state, sv.edicts);
|
||||
*sv_globals.time = sv.time;
|
||||
PR_ExecuteProgram (&sv_pr_state, EndFrame);
|
||||
PR_ExecuteProgram (&sv_pr_state, sv_funcs.EndFrame);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1468,8 +1468,7 @@ PF_multicast (progs_t *pr)
|
|||
static void
|
||||
PF_cfopen (progs_t *pr)
|
||||
{
|
||||
R_FLOAT (pr) = CF_Open (P_GSTRING (pr, 0),
|
||||
P_GSTRING (pr, 1));
|
||||
R_FLOAT (pr) = CF_Open (P_GSTRING (pr, 0), P_GSTRING (pr, 1));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1534,41 +1533,11 @@ PF_setinfokey (progs_t *pr)
|
|||
int e1 = NUM_FOR_EDICT (pr, edict);
|
||||
const char *key = P_GSTRING (pr, 1);
|
||||
const char *value = P_GSTRING (pr, 2);
|
||||
char *oldval = 0;
|
||||
|
||||
if (e1 == 0) {
|
||||
if (*value)
|
||||
Info_SetValueForKey (localinfo, key, value,
|
||||
!sv_highchars->int_val);
|
||||
else
|
||||
Info_RemoveKey (localinfo, key);
|
||||
SV_SetLocalinfo (key, value);
|
||||
} else if (e1 <= MAX_CLIENTS) {
|
||||
if (sv_setinfo_e->func)
|
||||
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));
|
||||
|
||||
}
|
||||
SV_SetUserinfo (&svs.clients[e1 - 1], key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,12 +57,6 @@ cvar_t *pr_checkextensions;
|
|||
cvar_t *sv_old_entity_free;
|
||||
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 void
|
||||
|
@ -169,6 +163,8 @@ SV_LoadProgs (void)
|
|||
dfunction_t *f;
|
||||
const char *progs_name = "qwprogs.dat";
|
||||
|
||||
memset (&sv_funcs, 0, sizeof (sv_funcs));
|
||||
|
||||
if (qfs_gamedir->gamecode && *qfs_gamedir->gamecode)
|
||||
progs_name = qfs_gamedir->gamecode;
|
||||
if (*sv_progs->string)
|
||||
|
@ -313,21 +309,18 @@ SV_LoadProgs (void)
|
|||
sv_fields.rotated_bbox = ED_GetFieldIndex (&sv_pr_state, "rotated_bbox");
|
||||
|
||||
// Zoid, find the spectator functions
|
||||
SpectatorConnect = SpectatorThink = SpectatorDisconnect = 0;
|
||||
EndFrame = UserInfoCallback = 0;
|
||||
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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
|
||||
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.scale = ED_GetFieldIndex (&sv_pr_state, "scale");
|
||||
|
|
|
@ -44,6 +44,7 @@ static __attribute__ ((unused)) const char rcsid[] =
|
|||
|
||||
#include "QF/console.h"
|
||||
#include "QF/cvar.h"
|
||||
#include "QF/dstring.h"
|
||||
#include "QF/msg.h"
|
||||
#include "QF/sound.h" // FIXME: DEFAULT_SOUND_PACKET_*
|
||||
#include "QF/sys.h"
|
||||
|
@ -62,7 +63,7 @@ static __attribute__ ((unused)) const char rcsid[] =
|
|||
|
||||
/* SV_Printf redirection */
|
||||
|
||||
char outputbuf[8000];
|
||||
dstring_t outputbuf;
|
||||
int con_printf_no_log;
|
||||
redirect_t sv_redirected;
|
||||
|
||||
|
@ -71,24 +72,62 @@ static void
|
|||
SV_FlushRedirect (void)
|
||||
{
|
||||
char send[8000 + 6];
|
||||
int count;
|
||||
int bytes;
|
||||
const char *p;
|
||||
|
||||
if (!outputbuf.size)
|
||||
return;
|
||||
|
||||
count = strlen (outputbuf.str);
|
||||
if (sv_redirected == RD_PACKET) {
|
||||
|
||||
send[0] = 0xff;
|
||||
send[1] = 0xff;
|
||||
send[2] = 0xff;
|
||||
send[3] = 0xff;
|
||||
send[4] = A2C_PRINT;
|
||||
memcpy (send + 5, outputbuf, strlen (outputbuf) + 1);
|
||||
|
||||
Netchan_SendPacket (strlen (send) + 1, send, net_from);
|
||||
} else if (sv_redirected == RD_CLIENT) {
|
||||
ClientReliableWrite_Begin (host_client, svc_print,
|
||||
strlen (outputbuf) + 3);
|
||||
ClientReliableWrite_Byte (host_client, PRINT_HIGH);
|
||||
ClientReliableWrite_String (host_client, outputbuf);
|
||||
p = outputbuf.str;
|
||||
while (count) {
|
||||
bytes = min (count, sizeof (send) - 5);
|
||||
memcpy (send + 5, p, bytes);
|
||||
send[5 + bytes] = 0;
|
||||
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
|
||||
outputbuf[0] = 0;
|
||||
dstring_clear (&outputbuf);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -101,7 +140,7 @@ void
|
|||
SV_BeginRedirect (redirect_t rd)
|
||||
{
|
||||
sv_redirected = rd;
|
||||
outputbuf[0] = 0;
|
||||
dstring_clear (&outputbuf);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -171,9 +210,7 @@ SV_Print (const char *fmt, va_list args)
|
|||
*out = '\0';
|
||||
|
||||
if (sv_redirected) { // Add to redirected message
|
||||
if (strlen (msg) + strlen (outputbuf) > sizeof (outputbuf) - 1)
|
||||
SV_FlushRedirect ();
|
||||
strncat (outputbuf, msg, sizeof (outputbuf) - strlen (outputbuf));
|
||||
dstring_appendstr (&outputbuf, msg);
|
||||
}
|
||||
if (!con_printf_no_log) {
|
||||
// We want to output to console and maybe logfile
|
||||
|
@ -820,8 +857,9 @@ SV_SendDemoMessage (void)
|
|||
min_fps = sv_demofps->value;
|
||||
|
||||
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;
|
||||
demo.forceFrame = 0;
|
||||
|
||||
for (i = 0, c = svs.clients ; i<MAX_CLIENTS ; i++, c++) {
|
||||
if (c->state != cs_spawned)
|
||||
|
|
|
@ -491,7 +491,7 @@ SV_Begin_f (void *unused)
|
|||
if (host_client->spectator) {
|
||||
SV_SpawnSpectator ();
|
||||
|
||||
if (SpectatorConnect) {
|
||||
if (sv_funcs.SpectatorConnect) {
|
||||
// copy spawn parms out of the client_t
|
||||
for (i = 0; i < NUM_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
|
||||
*sv_globals.time = sv.time;
|
||||
*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 {
|
||||
// copy spawn parms out of the client_t
|
||||
|
@ -787,6 +787,17 @@ SV_Say (qboolean team)
|
|||
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 ();
|
||||
|
||||
if (host_client->spectator && (!sv_spectalk->int_val || team)) {
|
||||
|
@ -1088,6 +1099,49 @@ SV_Msg_f (void *unused)
|
|||
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
|
||||
|
||||
|
@ -1096,7 +1150,8 @@ SV_Msg_f (void *unused)
|
|||
static void
|
||||
SV_SetInfo_f (void *unused)
|
||||
{
|
||||
char *oldval;
|
||||
const char *key;
|
||||
const char *value;
|
||||
|
||||
if (Cmd_Argc () == 1) {
|
||||
SV_Printf ("User info settings:\n");
|
||||
|
@ -1112,41 +1167,18 @@ SV_SetInfo_f (void *unused)
|
|||
if (Cmd_Argv (1)[0] == '*')
|
||||
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);
|
||||
P_STRING (&sv_pr_state, 0) = PR_SetString (&sv_pr_state, Cmd_Argv (1));
|
||||
P_STRING (&sv_pr_state, 1) = PR_SetString (&sv_pr_state, Cmd_Argv (2));
|
||||
PR_ExecuteProgram (&sv_pr_state, UserInfoCallback);
|
||||
P_STRING (&sv_pr_state, 0) = PR_SetString (&sv_pr_state, key);
|
||||
P_STRING (&sv_pr_state, 1) = PR_SetString (&sv_pr_state, value);
|
||||
PR_ExecuteProgram (&sv_pr_state, sv_funcs.UserInfoCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
oldval = strdup (Info_ValueForKey (host_client->userinfo, Cmd_Argv (1)));
|
||||
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)));
|
||||
}
|
||||
SV_SetUserinfo (host_client, key, value);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1205,6 +1237,7 @@ ucmd_t ucmds[] = {
|
|||
};
|
||||
|
||||
static hashtab_t *ucmd_table;
|
||||
int (*ucmd_unknown)(void);
|
||||
|
||||
static void
|
||||
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);
|
||||
|
||||
if (!u) {
|
||||
if (ucmd_unknown && !ucmd_unknown ()) {
|
||||
SV_BeginRedirect (RD_CLIENT);
|
||||
SV_Printf ("Bad user command: %s\n", sv_args->argv[0]->str);
|
||||
SV_EndRedirect ();
|
||||
}
|
||||
} else {
|
||||
if (!u->no_redirect)
|
||||
SV_BeginRedirect (RD_CLIENT);
|
||||
|
@ -1705,10 +1740,10 @@ SV_PostRunCmd (void)
|
|||
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
|
||||
PR_ExecuteProgram (&sv_pr_state, sv_funcs.PlayerPostThink);
|
||||
SV_RunNewmis ();
|
||||
} else if (SpectatorThink) {
|
||||
} else if (sv_funcs.SpectatorThink) {
|
||||
*sv_globals.time = sv.time;
|
||||
*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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue