aboutsummaryrefslogtreecommitdiff
path: root/src/con_.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/con_.h')
-rw-r--r--src/con_.h261
1 files changed, 204 insertions, 57 deletions
diff --git a/src/con_.h b/src/con_.h
index 0dd50a5..eb03642 100644
--- a/src/con_.h
+++ b/src/con_.h
@@ -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,12 +315,14 @@ 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 = (struct con_cmd *)&_ccmd_##varname;
+ struct con_cmd *varname = &_ccmd_##varname;
/* Defines a command with a given function as its handler. */
#define DEF_CCMD(name, desc, func, flags) \
@@ -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