mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-14 17:01:22 +00:00
12c84046f3
This is an extremely extensive patch as it hits every cvar, and every usage of the cvars. Cvars no longer store the value they control, instead, they use a cexpr value object to reference the value and specify the value's type (currently, a null type is used for strings). Non-string cvars are passed through cexpr, allowing expressions in the cvars' settings. Also, cvars have returned to an enhanced version of the original (id quake) registration scheme. As a minor benefit, relevant code having direct access to the cvar-controlled variables is probably a slight optimization as it removed a pointer dereference, and the variables can be located for data locality. The static cvar descriptors are made private as an additional safety layer, though there's nothing stopping external modification via Cvar_FindVar (which is needed for adding listeners). While not used yet (partly due to working out the design), cvars can have a validation function. Registering a cvar allows a primary listener (and its data) to be specified: it will always be called first when the cvar is modified. The combination of proper listeners and direct access to the controlled variable greatly simplifies the more complex cvar interactions as much less null checking is required, and there's no need for one cvar's callback to call another's. nq-x11 is known to work at least well enough for the demos. More testing will come.
560 lines
11 KiB
C
560 lines
11 KiB
C
#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 <stdlib.h>
|
|
|
|
#include "QF/cbuf.h"
|
|
#include "QF/cmd.h"
|
|
#include "QF/console.h"
|
|
#include "QF/cvar.h"
|
|
#include "QF/idparse.h"
|
|
#include "QF/mathlib.h"
|
|
#include "QF/msg.h"
|
|
#include "QF/qargs.h"
|
|
#include "QF/qendian.h"
|
|
#include "QF/sizebuf.h"
|
|
#include "QF/sys.h"
|
|
|
|
#include "QF/plugin/console.h"
|
|
|
|
#include "compat.h"
|
|
#include "netchan.h"
|
|
|
|
#define SV_TIMEOUT 450
|
|
|
|
#define PORT_MASTER 26900
|
|
#define PORT_SERVER 26950
|
|
|
|
// M = master, S = server, C = client, A = any
|
|
// the second character will allways be \n if the message isn't a single
|
|
// byte long (?? not true anymore?)
|
|
|
|
#define S2C_CHALLENGE 'c'
|
|
#define A2A_PING 'k'
|
|
|
|
#define S2M_HEARTBEAT 'a' // + serverinfo + userlist + fraglist
|
|
#define S2M_SHUTDOWN 'C'
|
|
|
|
typedef struct filter_s {
|
|
netadr_t from;
|
|
netadr_t to;
|
|
struct filter_s *next;
|
|
struct filter_s *previous;
|
|
} filter_t;
|
|
|
|
typedef struct server_s {
|
|
netadr_t ip;
|
|
struct server_s *next;
|
|
struct server_s *previous;
|
|
double timeout;
|
|
} server_t;
|
|
|
|
static char *sv_console_plugin;
|
|
static cvar_t sv_console_plugin_cvar = {
|
|
.name = "sv_console_plugin",
|
|
.description =
|
|
"Plugin used for the console",
|
|
.default_value = "server",
|
|
.flags = CVAR_ROM,
|
|
.value = { .type = 0, .value = &sv_console_plugin },
|
|
};
|
|
SERVER_PLUGIN_PROTOS
|
|
static plugin_list_t server_plugin_list[] = {
|
|
SERVER_PLUGIN_LIST
|
|
};
|
|
|
|
qboolean is_server = true;
|
|
|
|
static cbuf_t *mst_cbuf;
|
|
|
|
static server_t *sv_list = NULL;
|
|
static filter_t *filter_list = NULL;
|
|
|
|
static void
|
|
FL_Remove (filter_t * filter)
|
|
{
|
|
if (filter->previous)
|
|
filter->previous->next = filter->next;
|
|
if (filter->next)
|
|
filter->next->previous = filter->previous;
|
|
filter->next = NULL;
|
|
filter->previous = NULL;
|
|
if (filter_list == filter)
|
|
filter_list = NULL;
|
|
}
|
|
|
|
static void
|
|
FL_Clear (void)
|
|
{
|
|
filter_t *filter;
|
|
|
|
for (filter = filter_list; filter;) {
|
|
if (filter) {
|
|
filter_t *next = filter->next;
|
|
|
|
FL_Remove (filter);
|
|
free (filter);
|
|
filter = next;
|
|
}
|
|
}
|
|
filter_list = NULL;
|
|
}
|
|
|
|
static filter_t *
|
|
FL_New (netadr_t *adr1, netadr_t *adr2)
|
|
{
|
|
filter_t *filter;
|
|
|
|
filter = (filter_t *) calloc (1, sizeof (filter_t));
|
|
if (adr1)
|
|
filter->from = *adr1;
|
|
if (adr2)
|
|
filter->to = *adr2;
|
|
return filter;
|
|
}
|
|
|
|
static void
|
|
FL_Add (filter_t * filter)
|
|
{
|
|
filter->next = filter_list;
|
|
filter->previous = NULL;
|
|
if (filter_list)
|
|
filter_list->previous = filter;
|
|
filter_list = filter;
|
|
}
|
|
|
|
static __attribute__((pure)) filter_t *
|
|
FL_Find (netadr_t adr)
|
|
{
|
|
filter_t *filter;
|
|
|
|
for (filter = filter_list; filter; filter = filter->next) {
|
|
if (NET_CompareBaseAdr (filter->from, adr))
|
|
return filter;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
Filter (void)
|
|
{
|
|
int hold_port;
|
|
netadr_t filter_adr;
|
|
filter_t *filter;
|
|
|
|
hold_port = net_from.port;
|
|
NET_StringToAdr ("127.0.0.1:26950", &filter_adr);
|
|
if (NET_CompareBaseAdr (net_from, filter_adr)) {
|
|
NET_StringToAdr ("0.0.0.0:26950", &filter_adr);
|
|
if (!NET_CompareBaseAdr (net_local_adr, filter_adr)) {
|
|
net_from = net_local_adr;
|
|
net_from.port = hold_port;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// if no compare with filter list
|
|
if ((filter = FL_Find (net_from))) {
|
|
net_from = filter->to;
|
|
net_from.port = hold_port;
|
|
}
|
|
}
|
|
|
|
static void
|
|
SVL_Remove (server_t *sv)
|
|
{
|
|
if (sv_list == sv)
|
|
sv_list = sv->next;
|
|
if (sv->previous)
|
|
sv->previous->next = sv->next;
|
|
if (sv->next)
|
|
sv->next->previous = sv->previous;
|
|
sv->next = NULL;
|
|
sv->previous = NULL;
|
|
}
|
|
|
|
static void
|
|
SVL_Clear (void)
|
|
{
|
|
server_t *sv;
|
|
|
|
for (sv = sv_list; sv;) {
|
|
if (sv) {
|
|
server_t *next = sv->next;
|
|
|
|
SVL_Remove (sv);
|
|
free (sv);
|
|
sv = next;
|
|
}
|
|
}
|
|
sv_list = NULL;
|
|
}
|
|
|
|
static server_t *
|
|
SVL_New (netadr_t *adr)
|
|
{
|
|
server_t *sv;
|
|
|
|
sv = (server_t *) calloc (1, sizeof (server_t));
|
|
if (adr)
|
|
sv->ip = *adr;
|
|
return sv;
|
|
}
|
|
|
|
static void
|
|
SVL_Add (server_t *sv)
|
|
{
|
|
sv->next = sv_list;
|
|
sv->previous = NULL;
|
|
if (sv_list)
|
|
sv_list->previous = sv;
|
|
sv_list = sv;
|
|
}
|
|
|
|
static __attribute__((pure)) server_t *
|
|
SVL_Find (netadr_t adr)
|
|
{
|
|
server_t *sv;
|
|
|
|
for (sv = sv_list; sv; sv = sv->next) {
|
|
if (NET_CompareAdr (sv->ip, adr))
|
|
return sv;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
SV_InitNet (void)
|
|
{
|
|
const char *str;
|
|
int port, p;
|
|
QFile *filters;
|
|
|
|
port = PORT_MASTER;
|
|
p = COM_CheckParm ("-port");
|
|
if (p && p < com_argc) {
|
|
port = atoi (com_argv[p + 1]);
|
|
Sys_Printf ("Port: %i\n", port);
|
|
}
|
|
NET_Init (port);
|
|
|
|
// Add filters
|
|
if ((filters = Qopen ("filters.ini", "rt"))) {
|
|
while ((str = Qgetline (filters))) {
|
|
Cbuf_AddText (mst_cbuf, "filter add ");
|
|
Cbuf_AddText (mst_cbuf, str);
|
|
Cbuf_AddText (mst_cbuf, "\n");
|
|
}
|
|
Qclose (filters);
|
|
}
|
|
}
|
|
|
|
static void
|
|
AnalysePacket (void)
|
|
{
|
|
byte buf[16];
|
|
byte *p, *data;
|
|
int i, size, rsize;
|
|
|
|
Sys_Printf ("%s >> unknown packet:\n", NET_AdrToString (net_from));
|
|
|
|
data = net_message->message->data;
|
|
size = net_message->message->cursize;
|
|
|
|
for (p = data; (rsize = min (size - (p - data), 16)); p += rsize) {
|
|
Sys_Printf ("%04X:", (unsigned) (p - data));
|
|
memcpy (buf, p, rsize);
|
|
for (i = 0; i < rsize; i++) {
|
|
Sys_Printf (" %02X", buf[i]);
|
|
if (buf[i] < ' ' || buf [i] > '~')
|
|
buf[i] = '.';
|
|
}
|
|
Sys_Printf ("%*.*s\n", 1 + (16 - rsize) * 3 + rsize, rsize, buf);
|
|
}
|
|
}
|
|
|
|
static void
|
|
Mst_SendList (void)
|
|
{
|
|
byte buf[MAX_DATAGRAM];
|
|
sizebuf_t msg;
|
|
server_t *sv;
|
|
short int sv_num = 0;
|
|
|
|
msg.data = buf;
|
|
msg.maxsize = sizeof (buf);
|
|
msg.cursize = 0;
|
|
msg.allowoverflow = true;
|
|
msg.overflowed = false;
|
|
|
|
// number of servers:
|
|
for (sv = sv_list; sv; sv = sv->next)
|
|
sv_num++;
|
|
MSG_WriteByte (&msg, 255);
|
|
MSG_WriteByte (&msg, 255);
|
|
MSG_WriteByte (&msg, 255);
|
|
MSG_WriteByte (&msg, 255);
|
|
MSG_WriteByte (&msg, 255);
|
|
MSG_WriteByte (&msg, 'd');
|
|
MSG_WriteByte (&msg, '\n');
|
|
if (sv_num > 0)
|
|
for (sv = sv_list; sv; sv = sv->next) {
|
|
MSG_WriteByte (&msg, sv->ip.ip[0]);
|
|
MSG_WriteByte (&msg, sv->ip.ip[1]);
|
|
MSG_WriteByte (&msg, sv->ip.ip[2]);
|
|
MSG_WriteByte (&msg, sv->ip.ip[3]);
|
|
MSG_WriteShort (&msg, sv->ip.port);
|
|
}
|
|
NET_SendPacket (msg.cursize, msg.data, net_from);
|
|
}
|
|
|
|
static void
|
|
Mst_Packet (void)
|
|
{
|
|
char msg;
|
|
server_t *sv;
|
|
|
|
// Filter ();
|
|
msg = net_message->message->data[1];
|
|
if (msg == A2A_PING) {
|
|
Filter ();
|
|
Sys_Printf ("%s >> A2A_PING\n", NET_AdrToString (net_from));
|
|
if (!(sv = SVL_Find (net_from))) {
|
|
sv = SVL_New (&net_from);
|
|
SVL_Add (sv);
|
|
}
|
|
sv->timeout = Sys_DoubleTime ();
|
|
} else if (msg == S2M_HEARTBEAT) {
|
|
Filter ();
|
|
Sys_Printf ("%s >> S2M_HEARTBEAT\n", NET_AdrToString (net_from));
|
|
if (!(sv = SVL_Find (net_from))) {
|
|
sv = SVL_New (&net_from);
|
|
SVL_Add (sv);
|
|
}
|
|
sv->timeout = Sys_DoubleTime ();
|
|
} else if (msg == S2M_SHUTDOWN) {
|
|
Filter ();
|
|
Sys_Printf ("%s >> S2M_SHUTDOWN\n", NET_AdrToString (net_from));
|
|
if ((sv = SVL_Find (net_from))) {
|
|
SVL_Remove (sv);
|
|
free (sv);
|
|
}
|
|
} else if (msg == S2C_CHALLENGE) {
|
|
Sys_Printf ("%s >> ", NET_AdrToString (net_from));
|
|
Sys_Printf ("Gamespy server list request\n");
|
|
Mst_SendList ();
|
|
} else {
|
|
byte *p;
|
|
|
|
p = net_message->message->data;
|
|
if (p[0] == 0 && p[1] == 'y') {
|
|
Sys_Printf ("%s >> ", NET_AdrToString (net_from));
|
|
Sys_Printf ("Pingtool server list request\n");
|
|
Mst_SendList ();
|
|
} else {
|
|
AnalysePacket ();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
SV_ReadPackets (void)
|
|
{
|
|
while (NET_GetPacket ()) {
|
|
Mst_Packet ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
FilterAdd (int arg)
|
|
{
|
|
filter_t *filter;
|
|
netadr_t to, from;
|
|
|
|
if (Cmd_Argc () - arg != 2) {
|
|
Sys_Printf ("Invalid command parameters. "
|
|
"Usage:\nfilter add x.x.x.x:port x.x.x.x:port\n\n");
|
|
return;
|
|
}
|
|
NET_StringToAdr (Cmd_Argv (arg), &from);
|
|
NET_StringToAdr (Cmd_Argv (arg + 1), &to);
|
|
if (to.port == 0)
|
|
from.port = BigShort (PORT_SERVER);
|
|
if (from.port == 0)
|
|
from.port = BigShort (PORT_SERVER);
|
|
if (!(filter = FL_Find (from))) {
|
|
Sys_Printf ("Added filter %s\t\t%s\n", Cmd_Argv (arg),
|
|
Cmd_Argv (arg + 1));
|
|
filter = FL_New (&from, &to);
|
|
FL_Add (filter);
|
|
} else
|
|
Sys_Printf ("%s already defined\n\n", Cmd_Argv (arg));
|
|
}
|
|
|
|
static void
|
|
FilterRemove (int arg)
|
|
{
|
|
filter_t *filter;
|
|
netadr_t from;
|
|
|
|
if (Cmd_Argc () - arg != 1) {
|
|
Sys_Printf ("Invalid command parameters. Usage:\n"
|
|
"filter remove x.x.x.x:port\n\n");
|
|
return;
|
|
}
|
|
NET_StringToAdr (Cmd_Argv (arg), &from);
|
|
if ((filter = FL_Find (from))) {
|
|
Sys_Printf ("Removed %s\n\n", Cmd_Argv (arg));
|
|
FL_Remove (filter);
|
|
free (filter);
|
|
} else
|
|
Sys_Printf ("Cannot find %s\n\n", Cmd_Argv (arg));
|
|
}
|
|
|
|
static void
|
|
FilterList (void)
|
|
{
|
|
filter_t *filter;
|
|
|
|
for (filter = filter_list; filter; filter = filter->next) {
|
|
Sys_Printf ("%s", NET_AdrToString (filter->from));
|
|
Sys_Printf ("\t\t%s\n", NET_AdrToString (filter->to));
|
|
}
|
|
if (filter_list == NULL)
|
|
Sys_Printf ("No filter\n");
|
|
Sys_Printf ("\n");
|
|
}
|
|
|
|
static void
|
|
FilterClear (void)
|
|
{
|
|
Sys_Printf ("Removed all filters\n\n");
|
|
FL_Clear ();
|
|
}
|
|
|
|
static void
|
|
Filter_f (void)
|
|
{
|
|
if (!strcmp (Cmd_Argv (1), "add"))
|
|
FilterAdd (2);
|
|
else if (!strcmp (Cmd_Argv (1), "remove"))
|
|
FilterRemove (2);
|
|
else if (!strcmp (Cmd_Argv (1), "clear"))
|
|
FilterClear ();
|
|
else if (Cmd_Argc () == 3) {
|
|
FilterAdd (1);
|
|
} else if (Cmd_Argc () == 2) {
|
|
FilterRemove (1);
|
|
} else
|
|
FilterList ();
|
|
}
|
|
|
|
static void
|
|
SV_WriteFilterList (void)
|
|
{
|
|
QFile *filters;
|
|
|
|
if ((filters = Qopen ("filters.ini", "wt"))) {
|
|
filter_t *filter;
|
|
|
|
if (filter_list == NULL) {
|
|
Qclose (filters);
|
|
return;
|
|
}
|
|
|
|
for (filter = filter_list; filter; filter = filter->next) {
|
|
Qprintf (filters, "%s", NET_AdrToString (filter->from));
|
|
Qprintf (filters, " %s\n", NET_AdrToString (filter->to));
|
|
}
|
|
Qclose (filters);
|
|
}
|
|
}
|
|
|
|
static void
|
|
SV_Shutdown (void *data)
|
|
{
|
|
// write filter list
|
|
SV_WriteFilterList ();
|
|
}
|
|
|
|
static void
|
|
SV_TimeOut (void)
|
|
{
|
|
// Remove listed severs that havnt sent a heartbeat for some time
|
|
double time = Sys_DoubleTime ();
|
|
server_t *sv;
|
|
server_t *next;
|
|
|
|
if (sv_list == NULL)
|
|
return;
|
|
|
|
for (sv = sv_list; sv;) {
|
|
if (sv->timeout + SV_TIMEOUT < time) {
|
|
next = sv->next;
|
|
Sys_Printf ("%s timed out\n", NET_AdrToString (sv->ip));
|
|
SVL_Remove (sv);
|
|
free (sv);
|
|
sv = next;
|
|
} else
|
|
sv = sv->next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
SV_Frame (void)
|
|
{
|
|
Sys_CheckInput (1, net_socket);
|
|
Con_ProcessInput ();
|
|
Cbuf_Execute_Stack (mst_cbuf);
|
|
SV_TimeOut ();
|
|
SV_ReadPackets ();
|
|
}
|
|
|
|
static void
|
|
MST_Quit_f (void)
|
|
{
|
|
Sys_Printf ("HW master shutdown\n");
|
|
Sys_Quit ();
|
|
}
|
|
|
|
int
|
|
main (int argc, const char **argv)
|
|
{
|
|
COM_InitArgv (argc, argv);
|
|
|
|
mst_cbuf = Cbuf_New (&id_interp);
|
|
|
|
Sys_RegisterShutdown (SV_Shutdown, 0);
|
|
|
|
Sys_Init ();
|
|
|
|
Cmd_AddCommand ("quit", MST_Quit_f, "Shut down the master server");
|
|
Cmd_AddCommand ("clear", SVL_Clear, "Clear the server list");
|
|
Cmd_AddCommand ("filter", Filter_f, "Manipulate filtering");
|
|
|
|
Cmd_StuffCmds (mst_cbuf);
|
|
Cbuf_Execute_Sets (mst_cbuf);
|
|
|
|
PI_Init ();
|
|
|
|
Cvar_Register (&sv_console_plugin_cvar, 0, 0);
|
|
PI_RegisterPlugins (server_plugin_list);
|
|
Con_Init (sv_console_plugin);
|
|
if (con_module)
|
|
con_module->data->console->cbuf = mst_cbuf;
|
|
con_list_print = Sys_Printf;
|
|
|
|
SV_InitNet ();
|
|
Sys_Printf ("Exe: " __TIME__ " " __DATE__ "\n");
|
|
Sys_Printf ("======== HW master initialized ========\n\n");
|
|
while (1) {
|
|
SV_Frame ();
|
|
}
|
|
return 0;
|
|
}
|