diff options
Diffstat (limited to 'src/con_.h')
| -rw-r--r-- | src/con_.h | 259 |
1 files changed, 203 insertions, 56 deletions
@@ -21,7 +21,7 @@ #include "intdefs.h" #if defined(__GNUC__) || defined(__clang__) -#define _CON_PRINTF(x, y) __attribute__((format(printf, (x), (y)))) +#define _CON_PRINTF(x, y) __attribute((format(printf, (x), (y)))) #else #define _CON_PRINTF(x, y) #endif @@ -41,35 +41,44 @@ struct con_cmdargs { #define CON_CMD_MAXCOMPLETE 64 #define CON_CMD_MAXCOMPLLEN 64 -/* ConVar/ConCommand flag bits - undocumented ones are probably not useful... */ +/* ConVar/ConCommand flag bits stable across engines */ enum { - CON_UNREG = 1, - CON_DEVONLY = 1 << 1, /* hide unless developer 1 is set */ + _CON_NE_DEVONLY = 1 << 1, /* hide entirely and disallow usage. NE only. */ CON_SERVERSIDE = 1 << 2, /* set con_cmdclient and run on server side */ - CON_CLIENTDLL = 1 << 3, - CON_HIDDEN = 1 << 4, /* hide completely, often useful to remove! */ + _CON_NE_HIDDEN = 1 << 4, /* don't autocomplete. NE only; use con_hide() */ CON_PROTECTED = 1 << 5, /* don't send to clients (example: password) */ - CON_SPONLY = 1 << 6, - CON_ARCHIVE = 1 << 7, /* save in config - plugin would need a VDF! */ + CON_ARCHIVE = 1 << 7, /* save in config.cfg. needs VDF autoload. */ CON_NOTIFY = 1 << 8, /* announce changes in game chat */ - CON_USERINFO = 1 << 9, - CON_PRINTABLE = 1 << 10, /* do not allow non-printable values */ - CON_UNLOGGED = 1 << 11, - CON_NOPRINT = 1 << 12, /* do not attempt to print, contains junk! */ - CON_REPLICATE = 1 << 13, /* client will use server's value */ - CON_CHEAT = 1 << 14, /* require sv_cheats 1 to change from default */ - CON_DEMO = 1 << 16, /* record value at the start of a demo */ + CON_PRINTABLE = 1 << 10, /* do not allow non-printable characters */ + CON_NOPRINT = 1 << 12, /* contains junk; do not attempt to print */ + CON_REPLICATE = 1 << 13, /* client will value from server */ + CON_CHEAT = 1 << 14, /* require sv_cheats 1 to change (or run) */ + CON_DEMO = 1 << 16, /* record cvar value at the start of a demo */ CON_NORECORD = 1 << 17, /* don't record the command to a demo, ever */ - CON_NOTCONN = 1 << 22, /* cannot be changed while in-game */ - CON_SRVEXEC = 1 << 28, /* server can make clients run the command */ - CON_NOSRVQUERY = 1 << 29, /* server cannot query the clientside value */ - CON_CCMDEXEC = 1 << 30 /* ClientCmd() function may run the command */ + CON_NOTCONN = 1 << 22, /* cannot be changed while in a server */ + _CON_NE_CCMDEXEC = 1 << 30 /* ClientCmd() can run on client. NE only. */ }; -/* A callback function invoked to execute a command. */ -typedef void (*con_cmdcb)(const struct con_cmdargs *cmd); +/* + * Placeholder flags for DEF_* usage. Mapped to correct runtime flags at + * registration time (see con_regvar(), con_regcmd()). + */ +enum { + /* + * Causes a command or variable to be registered as hidden on NE. Currently + * does nothing on OE. Cannot be used to hide/unhide something after + * registration. Use con_hide() or con_unhide() for that. + */ + CON_INIT_HIDDEN = 1 << 29 +}; + +/* A callback function invoked by SST to execute its own commands. */ +typedef void (*con_cmdcb)(int argc, const char **argv); + +/* A callback function used by most commands in most versions of the engine. */ +typedef void (*con_cmdcbv2)(struct con_cmdargs *cmd); -/* Obsolete callback; not used by SST, but might still exist in the engine. */ +/* An older style of callback function used by some old commands, and in OE. */ typedef void (*con_cmdcbv1)(); /* @@ -101,9 +110,11 @@ struct con_cmdbase { // ConCommandBase in engine struct con_cmd { // ConCommand in engine struct con_cmdbase base; union { + con_cmdcb cb; // N.B.: only used by *our* commands! con_cmdcbv1 cb_v1; - con_cmdcb cb; - /*ICommandCallback*/ void *cb_iface; // does source even use this? + con_cmdcbv2 cb_v2; + const uchar *cb_insns; // for the sake of instruction-scanning and such + /*ICommandCallback*/ void *cb_iface; // what in Source even uses this? }; union { con_complcb complcb; @@ -112,9 +123,7 @@ struct con_cmd { // ConCommand in engine bool has_complcb : 1, use_newcb : 1, use_newcmdiface : 1; }; -struct con_var { // ConVar in engine - struct con_cmdbase base; - void **vtable_iconvar; // IConVar in engine (pure virtual) +struct con_var_common { struct con_var *parent; const char *defaultval; char *strval; @@ -126,8 +135,19 @@ struct con_var { // ConVar in engine float minval; bool hasmax; // just sticking to sdk position float maxval; +}; + +struct con_var { // ConVar in engine + struct con_cmdbase base; + union { + struct con_var_common v1; // OE + struct { + void **vtable_iconvar; // IConVar in engine (pure virtual) + struct con_var_common v2; + }; + }; /* - * Our quickly-chucked-in optional callback - doesn't match the engine!! + * Our quickly-chucked-in optional callback - doesn't match the engine ABI! * Also has to be manually set in code, although that's probably fine anyway * as it's common to only want a cvar to do something if the feature * succesfully init-ed. @@ -138,13 +158,24 @@ struct con_var { // ConVar in engine /* The change callback used in most branches of Source. Takes an IConVar :) */ typedef void (*con_varcb)(void *v, const char *, float); +/* Returns a registered variable with the given name, or null if not found. */ +struct con_var *con_findvar(const char *name); + +/* Returns a registered command with the given name, or null if not found. */ +struct con_cmd *con_findcmd(const char *name); + +/* + * Returns a pointer to the common (i.e. middle) part of a ConVar struct, the + * offset of which varies by engine version. This sub-struct contains + * essentially all the actual cvar-specific data. + */ +struct con_var_common *con_getvarcommon(const struct con_var *v); + /* * These functions get and set the values of console variables in a * neatly-abstracted manner. Note: cvar values are always strings internally - * numerical values are just interpretations of the underlying value. */ -struct con_var *con_findvar(const char *name); -struct con_cmd *con_findcmd(const char *name); const char *con_getvarstr(const struct con_var *v); float con_getvarf(const struct con_var *v); int con_getvari(const struct con_var *v); @@ -160,7 +191,7 @@ void con_setvari(struct con_var *v, int i); * callback being requested. If this is already known, consider just grabbing * the member directly to avoid the small amount of unnecessary work. */ -con_cmdcb con_getcmdcb(const struct con_cmd *cmd); +con_cmdcbv2 con_getcmdcbv2(const struct con_cmd *cmd); con_cmdcbv1 con_getcmdcbv1(const struct con_cmd *cmd); /* @@ -168,30 +199,47 @@ con_cmdcbv1 con_getcmdcbv1(const struct con_cmd *cmd); * respectively. They are aliases to direct tier0 calls, so they work early on * even before anything else is initialised. */ -#if defined(__GNUC__) || defined(__clang__) #ifdef _WIN32 -#define __asm__(x) __asm__("_" x) // stupid mangling meme, only on windows! -#endif -void con_msg(const char *fmt, ...) _CON_PRINTF(1, 2) __asm__("Msg"); -void con_warn(const char *fmt, ...) _CON_PRINTF(1, 2) __asm__("Warning"); -#undef __asm__ +void con_msg(const char *fmt, ...) _CON_PRINTF(1, 2) __asm("_Msg"); +void con_warn(const char *fmt, ...) _CON_PRINTF(1, 2) __asm("_Warning"); #else -#error Need an equivalent of asm names for your compiler! +void con_msg(const char *fmt, ...) _CON_PRINTF(1, 2) __asm("Msg"); +void con_warn(const char *fmt, ...) _CON_PRINTF(1, 2) __asm("Warning"); #endif struct rgba; // in engineapi.h - forward declare here to avoid warnings struct ICvar; // " -extern struct ICvar *_con_iface; -extern void (*_con_colourmsgf)(struct ICvar *this, const struct rgba *c, - const char *fmt, ...) _CON_PRINTF(3, 4); +// DO NOT CALL THIS DIRECTLY UNDER ANY CIRCUMSTANCES. +void _con_colourmsg(void *dummy, const struct rgba *c, const char *fmt, ...) + _CON_PRINTF(3, 4); + /* * This provides the same functionality as ConColorMsg which was removed from * tier0 in the L4D engine branch - specifically, it allows printing a message * with an arbitrary RGBA colour. It must only be used after a successful * con_init() call. */ -#define con_colourmsg(c, ...) _con_colourmsgf(_con_iface, c, __VA_ARGS__) +#define con_colourmsg(/*c, fmt, */...) do { \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma("GCC diagnostic ignored \"-Wunused\"") \ + /* intentionally uninitialised value allows the compiler to just create a + hole in the stack without actually writing anything. this has been + confirmed by looking at the asm, because I'm that type of weirdo :^) */ \ + void *_dummy; \ + /* we also have to reserve EBX as a register that our wrapper can clobber + but the callee (engine function) won't (as it's normally callee-save). + the way we do this is by marking the register as clobbered both before + and after the call and tying both to the lifetime of a dummy variable. + this ensures anything that'd otherwise get put in ebx is spilled + elsewhere until after the call has returned. */ \ + register uint _ebx __asm("ebx"); \ + __asm volatile ("" : "=r" (_ebx)); \ + _con_colourmsg(_dummy, __VA_ARGS__); \ + __asm volatile ("" : "=r" (_ebx)); \ + _Pragma("GCC diagnostic pop") \ +} while (0) /* * The index of the client responsible for the currently executing command, @@ -228,16 +276,22 @@ extern struct _con_vtab_iconvar_wrap { static struct con_var _cvar_##name_ = { \ .base = { \ .vtable = _con_vtab_var, \ - .name = "" #name_, .help = "" desc, .flags = (flags_) \ + .name = "" #name_, \ + /* n.b. redundant cast to avoid warnings */ \ + .help = (const char *)("** unsupported ** " desc) + 18, \ + .flags = (flags_) \ }, \ .vtable_iconvar = _con_vtab_iconvar, \ - .parent = &_cvar_##name_, /* bizarre, but how the engine does it */ \ - .defaultval = _Generic(value, char *: value, int: #value, \ - double: #value), \ - .strlen = sizeof(_Generic(value, char *: value, default: #value)), \ - .fval = _Generic(value, char *: 0, int: value, double: value), \ - .ival = _Generic(value, char *: 0, int: value, double: (int)value), \ - .hasmin = hasmin_, .minval = (min), .hasmax = hasmax_, .maxval = (max) \ + .v2 = { \ + .parent = &_cvar_##name_, /* bizarre, but how the engine does it */ \ + .defaultval = _Generic(value, char *: value, int: #value, \ + double: #value), \ + .strlen = sizeof(_Generic(value, char *: value, default: #value)), \ + .fval = _Generic(value, char *: 0, int: value, double: value), \ + .ival = _Generic(value, char *: 0, int: value, double: (int)value), \ + .hasmin = hasmin_, .minval = (min), \ + .hasmax = hasmax_, .maxval = (max) \ + } \ }; \ struct con_var *name_ = &_cvar_##name_; @@ -261,10 +315,12 @@ extern struct _con_vtab_iconvar_wrap { static struct con_cmd _ccmd_##varname = { \ .base = { \ .vtable = _con_vtab_cmd, \ - .name = "" #name_, .help = "" desc, .flags = (flags_) \ + .name = "" #name_, \ + /* n.b. redundant cast to avoid warnings */ \ + .help = (const char *)("** unsupported ** " desc) + 18, \ + .flags = (flags_) \ }, \ .cb = &func, \ - .use_newcb = true \ }; \ struct con_cmd *varname = &_ccmd_##varname; @@ -282,13 +338,13 @@ extern struct _con_vtab_iconvar_wrap { /* * Defines a console command with the handler function body immediately - * following the macro (like in Source itself). The function takes the argument - * `struct con_cmdargs *cmd` for command arguments. + * following the macro (like in Source itself). The function takes the implicit + * arguments `int argc` and `const char **argv` for command arguments. */ #define DEF_CCMD_HERE(name, desc, flags) \ - static void _cmdf_##name(const struct con_cmdargs *cmd); \ + static void _cmdf_##name(int argc, const char **argv); \ _DEF_CCMD(name, name, desc, _cmdf_##name, flags) \ - static void _cmdf_##name(const struct con_cmdargs *cmd) \ + static void _cmdf_##name(int argc, const char **argv) \ /* { body here } */ /* @@ -331,6 +387,81 @@ extern struct _con_vtab_iconvar_wrap { #define DEF_CCMD_PLUSMINUS_UNREG DEF_CCMD_PLUSMINUS /* + * Defines a hook function in-place to hook a command callback, factoring in + * different callback ABIs used by different commands and/or different engine + * branches. Defines a hook_##name##_cb function to install the hook and an + * unhook_##name##_cb function to remove it. + * + * The hook function has the implicit arguments argc and argv, just like a + * command handler defined with DEF_CCMD_HERE. Calling the original command + * handler can be done using orig_##name##_cb, passing through argc and argv. + * + * In some cases, a command will be defined to take no arguments, in which case + * argc will be zero and argv will be null. In these cases, the parameters + * should still be passed through to the orig_ function, as this ensures + * compatibility with other game/engine versions. + */ +#define DEF_CCMD_COMPAT_HOOK(name) \ + static union { \ + con_cmdcbv1 v1; \ + con_cmdcbv2 v2; \ + } _orig_##name##_cb; \ + static void _orig_##name##_cbv1(int argc, const char **argv) { \ + extern int *_con_argc; \ + extern const char **_con_argv; \ + int _orig_argc = *_con_argc; \ + *_con_argc = argc; \ + if (argv != _con_argv) { \ + /* args can be passed through as-is, or modified in place, however + here we have a whole different array, so we have to copy it out + and back to avoid confusing side effects for the caller. */ \ + /* XXX: not bothering with the null term here; should we be? */ \ + const char *_orig_argv[80]; \ + memcpy(_orig_argv, _con_argv, _orig_argc * sizeof(*argv)); \ + memcpy(_con_argv, argv, argc * sizeof(*argv)); \ + _orig_##name##_cb.v1(); \ + memcpy(_con_argv, _orig_argv, _orig_argc * sizeof(*argv)); \ + } \ + else { \ + _orig_##name##_cb.v1(); \ + } \ + *_con_argc = _orig_argc; \ + } \ + static void _orig_##name##_cbv2(int argc, const char **argv) { \ + struct con_cmdargs args; \ + args.argc = argc; \ + /* XXX: having to copy argv sucks, but can't see how to avoid without + ruining the interface? */ \ + for (int i = 0; i < argc; ++i) args.argv[i] = argv[i]; \ + _orig_##name##_cb.v2(&args); \ + } \ + static void (*orig_##name##_cb)(int argc, const char **argv); \ + static void _hook_##name##_cb(int argc, const char **argv); \ + static void _hook_##name##_cbv1() { \ + extern int *_con_argc; \ + extern const char **_con_argv; \ + _hook_##name##_cb(*_con_argc, _con_argv); \ + } \ + static void _hook_##name##_cbv2(struct con_cmdargs *args) { \ + _hook_##name##_cb(args->argc, args->argv); \ + } \ + static void hook_##name##_cb(struct con_cmd *cmd) { \ + _orig_##name##_cb.v1 = cmd->cb_v1; \ + if (cmd->use_newcb) { \ + cmd->cb_v2 = &_hook_##name##_cbv2; \ + orig_##name##_cb = &_orig_##name##_cbv2; \ + } \ + else { \ + cmd->cb_v1 = _hook_##name##_cbv1; \ + orig_##name##_cb = &_orig_##name##_cbv1; \ + } \ + } \ + static void unhook_##name##_cb(struct con_cmd *cmd) { \ + cmd->cb_v1 = _orig_##name##_cb.v1; \ + } \ + static void _hook_##name##_cb(int argc, const char **argv) /* ... */ + +/* * These functions register a command or variable, respectively, defined with * the _UNREG variants of the above macros. These can be used to conditionally * register things. Wherever possible, it is advised to use the DEF_FEAT_* @@ -340,6 +471,22 @@ extern struct _con_vtab_iconvar_wrap { void con_regvar(struct con_var *v); void con_regcmd(struct con_cmd *c); +/* + * These functions cause a command or variable to be hidden or unhidden from + * autocompletion and command listing results, on engine branches which support + * doing so. In practice this means anything that's not OE. On OE, these + * functions currently just do nothing, although it would be possible in theory + * to patch in command-hiding support if deemed important enough. + * + * Note: con_hide() will not work on an unregistered command or variable with + * CON_INIT_HIDDEN; this includes any of a feature's commands/variables during + * feature initialisation, except those that are manually registered first. + * In cases where a variable/command is to be registered automatically, the + * CON_INIT_HIDDEN flag can be removed using bitwise ops. + */ +void con_hide(struct con_cmdbase *b); +void con_unhide(struct con_cmdbase *b); + #endif // vi: sw=4 ts=4 noet tw=80 cc=80 |
