A modern C++17 cvar and command system in the Quake III Arena–style console API tradition, packaged as a lightweight, header-friendly library. If you know what a cvar is, you know what this does.
veCVar— Console variables with full flag support (archive, latch, ROM, cheat-protected, userinfo/serverinfo/systeminfo propagation, range validation)veCmd— Command buffer with tokenization, deferred execution (wait), glob filtering, and tab-completion hooksveIVar— Key/value info strings with Q3-style\key\valueserialization/parsing helpers- Default commands wired up via
veCmd_InitDefaultFunctions():alias,exec,set,seta,setu,sets,reset,toggle,print,cvarlist,cvar_modified,cvar_restart,echo,wait,cmdlist
- C++17
- CMake 3.20+
- The header provides default
dinfo(...),derr(...), andderr_fatal(...)fallbacks that write tostderrand abort on fatal errors - To override logging, define
dinfo,derr, andderr_fatalmacros before includingvvar.h, or defineVVAR_NO_DEFAULT_LOGGINGand provide your own implementations
cmake -B build
cmake --build build
ctest --test-dir buildNinja and clang-cl are both supported.
Include vvar_impl.h in one translation unit. Include vvar.h everywhere else.
// In one .cpp file only:
#include "vvar_impl.h"
// Elsewhere:
#include "vvar.h"If you want custom logging, define your hooks before including the header:
#define dinfo(...) my_info_logger(__VA_ARGS__)
#define derr(...) my_error_logger(__VA_ARGS__)
#define derr_fatal(...) my_fatal_logger(__VA_ARGS__)
#include "vvar.h"// Register (creates if absent, ORs flags if already exists)
veCVar* r_fullscreen = veCVar::get("r_fullscreen", "1", VE_CVAR_ARCHIVE);
// Read
int fs = veCVar::variableIntegerValue("r_fullscreen");
// Write
veCVar::set("r_fullscreen", "0");veCVarRef resolves on first use and caches the pointer after that:
veCVarRef r_mode("r_mode", "3", VE_CVAR_ARCHIVE | VE_CVAR_LATCH);
int mode = r_mode->getInteger();Call veCVar::init() before first dereference. If you resolve a ref before init(), init() will clear the table and invalidate the cached pointer.
veCmd_InitDefaultFunctions(); // wire up built-ins
veGetCmd().addCommand("quit", []() {
// handle quit
});
veGetCmd().executeString("set r_fullscreen 0; quit");
veGetCmd().execute(VE_CMD_EXEC_NOW);Aliases are expanded before the command table, so Q3-style command chains work as expected:
veGetCmd().executeString("alias jump \"+moveup; wait; -moveup\"");
veGetCmd().executeString("jump");exec <filename> reads a script file and inserts it into the command buffer.
Changes are staged and applied the next time veCVar::get() is called for that variable — useful for settings that require a restart to take effect.
veCVar* vid_restart = veCVar::get("r_mode", "3", VE_CVAR_LATCH | VE_CVAR_ARCHIVE);veFileData f;
veCVar::writeVariables(f); // emits "set <name> <value>" lines for all VE_CVAR_ARCHIVE varsveIVar stores named info-string maps and can round-trip the Quake-style wire format:
veIVar::fromString("userinfo", "\\name\\Player\\rate\\25000");
const char* info = veIVar::toString("userinfo");VE_CVAR_ALLOW_SET_INTEGER opts a cvar into explicit numeric back-buffer sync. If you mutate a cvar through getInteger() or getValue(), call veCVar::updateFromIntegerFloatValues() once per frame (or at your chosen sync point) to push those numeric changes back into the string form.
| Flag | Description |
|---|---|
VE_CVAR_ARCHIVE |
Saved to config on writeVariables |
VE_CVAR_LATCH |
Change takes effect on next get() call |
VE_CVAR_ROM |
Display only, not user-settable |
VE_CVAR_INIT |
Set from command line only |
VE_CVAR_CHEAT |
Locked when cheats are disabled |
VE_CVAR_PROTECTED |
Cannot be set from VMs or remote |
VE_CVAR_USERINFO |
Sent to server on connect/change |
VE_CVAR_SERVERINFO |
Sent in response to frontend requests |
VE_CVAR_SYSTEMINFO |
Replicated to all clients |
VE_CVAR_ALLOW_SET_INTEGER |
Allows explicit getInteger() / getValue() writes to be synchronized back into the string form via updateFromIntegerFloatValues() |
resetrespects normal cvar protections and latch behavior;forceResetbypasses them.veq3_va()now uses a thread-local ring buffer, so it is safe across threads and still supports a small amount of nesting.
MIT. See LICENSE for the full terms. Historical commits at or before the gpl-final tag are the prior GPLv2 implementation and remain GPLv2.