mirror of
https://github.com/odamex/odamex
synced 2026-08-19 02:23:09 -04:00
860 lines
18 KiB
C++
860 lines
18 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id$
|
|
//
|
|
// Copyright (C) 1998-2006 by Randy Heit (ZDoom).
|
|
// Copyright (C) 2006-2026 by The Odamex Team.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// DESCRIPTION:
|
|
// Command-line variables
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
#include "odamex.h"
|
|
|
|
#include <cmath>
|
|
#include <exception>
|
|
|
|
#include "cmdlib.h"
|
|
#include "c_console.h"
|
|
#include "c_dispatch.h"
|
|
#include "m_alloc.h"
|
|
|
|
#include "d_netinf.h"
|
|
|
|
#include "i_system.h"
|
|
|
|
bool cvar_t::m_DoNoSet = false;
|
|
bool cvar_t::m_UseCallback = false;
|
|
|
|
// denis - all this class does is delete the cvars during its static destruction
|
|
class ad_t {
|
|
public:
|
|
cvar_t *&GetCVars() { static cvar_t *CVars; return CVars; }
|
|
ad_t() {}
|
|
~ad_t()
|
|
{
|
|
cvar_t *cvar = GetCVars();
|
|
while (cvar)
|
|
{
|
|
cvar_t *next = cvar->GetNext();
|
|
if(cvar->m_Flags & CVAR_AUTO)
|
|
delete cvar;
|
|
cvar = next;
|
|
}
|
|
}
|
|
} ad;
|
|
|
|
cvar_t* GetFirstCvar(void)
|
|
{
|
|
return ad.GetCVars();
|
|
}
|
|
|
|
int cvar_defflags;
|
|
|
|
cvar_t::cvar_t(const char* var_name, const char* def, const char* help, cvartype_t type,
|
|
uint32_t flags, float minval, float maxval)
|
|
{
|
|
InitSelf(var_name, def, help, type, flags, NULL, minval, maxval);
|
|
}
|
|
|
|
cvar_t::cvar_t(const char* var_name, const char* def, const char* help, cvartype_t type,
|
|
uint32_t flags, void (*callback)(cvar_t &), float minval, float maxval)
|
|
{
|
|
InitSelf(var_name, def, help, type, flags, callback, minval, maxval);
|
|
}
|
|
|
|
void cvar_t::InitSelf(const char* var_name, const char* def, const char* help, cvartype_t type,
|
|
uint32_t var_flags, void (*callback)(cvar_t &), float minval, float maxval)
|
|
{
|
|
cvar_t* dummy;
|
|
cvar_t* var = FindCVar(var_name, &dummy);
|
|
|
|
m_Callback = callback;
|
|
m_String = "";
|
|
m_Value = 0.0f;
|
|
m_Flags = 0;
|
|
m_LatchedString = "";
|
|
m_HelpText = help;
|
|
m_Type = type;
|
|
|
|
if (var_flags & CVAR_NOENABLEDISABLE)
|
|
{
|
|
m_MinValue = minval;
|
|
m_MaxValue = maxval;
|
|
}
|
|
else
|
|
{
|
|
m_MinValue = 0.0f;
|
|
m_MaxValue = 1.0f;
|
|
}
|
|
|
|
if (def)
|
|
m_Default = def;
|
|
else
|
|
m_Default = "";
|
|
|
|
if (var_name)
|
|
{
|
|
C_AddTabCommand(var_name);
|
|
m_Name = var_name;
|
|
m_Next = ad.GetCVars();
|
|
ad.GetCVars() = this;
|
|
}
|
|
else
|
|
m_Name = "";
|
|
|
|
if (var)
|
|
{
|
|
ForceSet(var->m_String.c_str());
|
|
if (var->m_Flags & CVAR_AUTO)
|
|
delete var;
|
|
else
|
|
var->~cvar_t();
|
|
}
|
|
else if (def)
|
|
ForceSet(def);
|
|
|
|
m_Flags = var_flags | CVAR_ISDEFAULT;
|
|
}
|
|
|
|
cvar_t::~cvar_t ()
|
|
{
|
|
if (m_Name.length())
|
|
{
|
|
cvar_t *var, *dummy = NULL;
|
|
|
|
var = FindCVar (m_Name.c_str(), &dummy);
|
|
|
|
if (var == this)
|
|
{
|
|
if (dummy)
|
|
dummy->m_Next = m_Next;
|
|
else
|
|
ad.GetCVars() = m_Next;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cvar_t::ForceSet(const char* valstr)
|
|
{
|
|
// [SL] 2013-04-16 - Latched CVARs do not change values until the next map.
|
|
// Servers and single-player games should abide by this behavior but
|
|
// multiplayer clients should just do what the server tells them.
|
|
if (m_Flags & CVAR_LATCH && serverside &&
|
|
(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
|
|
{
|
|
m_Flags |= CVAR_MODIFIED;
|
|
if (valstr)
|
|
m_LatchedString = valstr;
|
|
else
|
|
m_LatchedString.clear();
|
|
}
|
|
else
|
|
{
|
|
m_Flags |= CVAR_MODIFIED;
|
|
|
|
bool numerical_value = IsRealNum(valstr);
|
|
bool integral_type = m_Type == CVARTYPE_BOOL || m_Type == CVARTYPE_BYTE ||
|
|
m_Type == CVARTYPE_WORD || m_Type == CVARTYPE_INT;
|
|
bool floating_type = m_Type == CVARTYPE_FLOAT;
|
|
float valf = numerical_value ? atof(valstr) : 0.0f;
|
|
|
|
// perform rounding to nearest integer for integral types
|
|
if (integral_type)
|
|
valf = std::round(valf);
|
|
|
|
valf = std::clamp(valf, m_MinValue, m_MaxValue);
|
|
|
|
if (numerical_value || integral_type || floating_type)
|
|
{
|
|
// generate m_String based on the clamped valf value
|
|
m_String = fmt::format("{:g}", valf);
|
|
}
|
|
else
|
|
{
|
|
// just set m_String to valstr
|
|
if (valstr)
|
|
m_String = valstr;
|
|
else
|
|
m_String.clear();
|
|
}
|
|
|
|
m_Value = valf;
|
|
|
|
if (m_UseCallback)
|
|
Callback();
|
|
|
|
if (m_Flags & CVAR_USERINFO)
|
|
D_UserInfoChanged(this);
|
|
if (m_Flags & CVAR_SERVERINFO)
|
|
D_SendServerInfoChange(this, m_String.c_str());
|
|
}
|
|
|
|
m_Flags &= ~CVAR_ISDEFAULT;
|
|
}
|
|
|
|
|
|
void cvar_t::ForceSet(float val)
|
|
{
|
|
char string[32];
|
|
snprintf(string, 32, "%g", val);
|
|
ForceSet(string);
|
|
}
|
|
|
|
void cvar_t::Set (const char *val)
|
|
{
|
|
if (!(m_Flags & CVAR_NOSET) || !m_DoNoSet)
|
|
ForceSet (val);
|
|
}
|
|
|
|
void cvar_t::Set (float val)
|
|
{
|
|
if (!(m_Flags & CVAR_NOSET) || !m_DoNoSet)
|
|
ForceSet (val);
|
|
}
|
|
|
|
void cvar_t::SetDefault (const char *val)
|
|
{
|
|
if(val)
|
|
m_Default = val;
|
|
else
|
|
m_Default = "";
|
|
|
|
if (m_Flags & CVAR_ISDEFAULT)
|
|
{
|
|
Set (val);
|
|
m_Flags |= CVAR_ISDEFAULT;
|
|
}
|
|
}
|
|
|
|
void cvar_t::RestoreDefault ()
|
|
{
|
|
Set(m_Default.c_str());
|
|
m_Flags |= CVAR_ISDEFAULT;
|
|
}
|
|
|
|
//
|
|
// cvar_t::Transfer
|
|
//
|
|
// Copies the value from one cvar to another and then removes the source cvar
|
|
//
|
|
void cvar_t::Transfer(const char *fromname, const char *toname)
|
|
{
|
|
cvar_t *from, *to, *dummy;
|
|
|
|
from = FindCVar(fromname, &dummy);
|
|
to = FindCVar(toname, &dummy);
|
|
|
|
if (from && to)
|
|
{
|
|
to->ForceSet(from->m_Value);
|
|
to->ForceSet(from->m_String.c_str());
|
|
|
|
// remove the old cvar
|
|
cvar_t *cur = ad.GetCVars();
|
|
while (cur->m_Next != from)
|
|
cur = cur->m_Next;
|
|
|
|
cur->m_Next = from->m_Next;
|
|
}
|
|
}
|
|
|
|
cvar_t *cvar_t::cvar_set (const char *var_name, const char *val)
|
|
{
|
|
cvar_t *var, *dummy;
|
|
|
|
if ( (var = FindCVar (var_name, &dummy)) )
|
|
var->Set (val);
|
|
|
|
return var;
|
|
}
|
|
|
|
cvar_t *cvar_t::cvar_forceset (const char *var_name, const char *val)
|
|
{
|
|
cvar_t *var, *dummy;
|
|
|
|
if ( (var = FindCVar (var_name, &dummy)) )
|
|
var->ForceSet (val);
|
|
|
|
return var;
|
|
}
|
|
|
|
void cvar_t::EnableNoSet ()
|
|
{
|
|
m_DoNoSet = true;
|
|
}
|
|
|
|
void cvar_t::EnableCallbacks ()
|
|
{
|
|
m_UseCallback = true;
|
|
cvar_t *cvar = ad.GetCVars();
|
|
|
|
while (cvar)
|
|
{
|
|
cvar->Callback ();
|
|
cvar = cvar->m_Next;
|
|
}
|
|
}
|
|
|
|
void cvar_t::FilterCompactCVars (std::vector<cvar_t *> &cvars, uint32_t filter)
|
|
{
|
|
cvar_t *cvar = ad.GetCVars();
|
|
while (cvar)
|
|
{
|
|
if (cvar->m_Flags & filter)
|
|
cvars.push_back(cvar);
|
|
cvar = cvar->m_Next;
|
|
}
|
|
std::sort(cvars.begin(), cvars.end(), [](const cvar_t* a, const cvar_t* b){ return a->name().compare(b->name()); });
|
|
}
|
|
|
|
// Uses snprintf's return value (number of chars written) to advance
|
|
// a pointer of an array of chars to write out a packed byte array
|
|
// of cvars, subtracting the base array size from the total after
|
|
// each advancement.
|
|
void cvar_t::C_WriteCVars (byte **demo_p, uint32_t filter, size_t array_size, bool compact)
|
|
{
|
|
if (array_size <= 0)
|
|
return;
|
|
|
|
byte *ptr = *demo_p;
|
|
int chars;
|
|
|
|
if (compact)
|
|
{
|
|
std::vector<cvar_t *> cvars;
|
|
chars = snprintf((char*)ptr, array_size, "\\\\%ux", (unsigned int)filter);
|
|
|
|
ptr += chars;
|
|
array_size -= chars;
|
|
|
|
FilterCompactCVars(cvars, filter);
|
|
for (const cvar_t* cvar : cvars)
|
|
{
|
|
if (array_size <= 0)
|
|
{
|
|
PrintFmt(PRINT_WARNING, "Warning: Saved Cvars exceed {} bytes, no more cvars will be written.\n", array_size);
|
|
return;
|
|
}
|
|
|
|
chars = snprintf ((char *)ptr, array_size, "\\%s", cvar->cstring());
|
|
|
|
ptr += chars;
|
|
array_size -= chars;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cvar_t *cvar = ad.GetCVars();
|
|
while (cvar)
|
|
{
|
|
if (cvar->m_Flags & filter)
|
|
{
|
|
if (array_size <= 0)
|
|
{
|
|
PrintFmt(PRINT_WARNING, "Saved Cvars exceed {} bytes, no more "
|
|
"cvars will be written.\n",
|
|
array_size);
|
|
return;
|
|
}
|
|
|
|
chars = snprintf((char*)ptr, array_size, "\\%s\\%s",
|
|
cvar->name().c_str(), cvar->cstring());
|
|
|
|
ptr += chars;
|
|
array_size -= chars;
|
|
}
|
|
cvar = cvar->m_Next;
|
|
}
|
|
}
|
|
|
|
*demo_p = ptr + 1;
|
|
}
|
|
|
|
void cvar_t::C_ReadCVars (byte **demo_p)
|
|
{
|
|
char *ptr = *((char **)demo_p);
|
|
char *breakpt;
|
|
|
|
if (*ptr++ != '\\')
|
|
return;
|
|
|
|
if (*ptr == '\\')
|
|
{ // compact mode
|
|
std::vector<cvar_t *> cvars;
|
|
uint32_t filter;
|
|
|
|
ptr++;
|
|
breakpt = strchr (ptr, '\\');
|
|
*breakpt = 0;
|
|
filter = strtoul (ptr, NULL, 16);
|
|
*breakpt = '\\';
|
|
ptr = breakpt + 1;
|
|
|
|
FilterCompactCVars (cvars, filter);
|
|
|
|
for (cvar_t* cvar : cvars)
|
|
{
|
|
breakpt = strchr (ptr, '\\');
|
|
if (breakpt)
|
|
*breakpt = 0;
|
|
cvar->Set (ptr);
|
|
if (breakpt)
|
|
{
|
|
*breakpt = '\\';
|
|
ptr = breakpt + 1;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char *value;
|
|
|
|
while ( (breakpt = strchr (ptr, '\\')) )
|
|
{
|
|
*breakpt = 0;
|
|
value = breakpt + 1;
|
|
if ( (breakpt = strchr (value, '\\')) )
|
|
*breakpt = 0;
|
|
|
|
cvar_set (ptr, value);
|
|
|
|
*(value - 1) = '\\';
|
|
if (breakpt)
|
|
{
|
|
*breakpt = '\\';
|
|
ptr = breakpt + 1;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
*demo_p += strlen (*((char **)demo_p)) + 1;
|
|
}
|
|
|
|
static struct backup_s
|
|
{
|
|
std::string name, string;
|
|
} CVarBackups[MAX_BACKUPCVARS];
|
|
|
|
static int numbackedup = 0;
|
|
|
|
//
|
|
// C_BackupCVars
|
|
//
|
|
// Backup cvars for restoration later. Called before connecting to a server
|
|
// or a demo starts playing to save all cvars which could be changed while
|
|
// by the server or by playing a demo.
|
|
// [SL] bitflag can be used to filter which cvars are set to default.
|
|
// The default value for bitflag is 0xFFFFFFFF, which effectively disables
|
|
// the filtering.
|
|
//
|
|
void cvar_t::C_BackupCVars (unsigned int bitflag)
|
|
{
|
|
struct backup_s *backup = CVarBackups;
|
|
cvar_t *cvar = ad.GetCVars();
|
|
|
|
while (cvar)
|
|
{
|
|
if (cvar->m_Flags & bitflag)
|
|
{
|
|
if (backup == &CVarBackups[MAX_BACKUPCVARS])
|
|
I_Error("C_BackupDemoCVars: Too many cvars to save ({})", MAX_BACKUPCVARS);
|
|
backup->name = cvar->m_Name;
|
|
backup->string = cvar->m_String;
|
|
backup++;
|
|
}
|
|
cvar = cvar->m_Next;
|
|
}
|
|
numbackedup = backup - CVarBackups;
|
|
}
|
|
|
|
void cvar_t::C_RestoreCVars (void)
|
|
{
|
|
struct backup_s *backup = CVarBackups;
|
|
int i;
|
|
|
|
for (i = numbackedup; i; i--, backup++)
|
|
{
|
|
cvar_set (backup->name.c_str(), backup->string.c_str());
|
|
backup->name = backup->string = "";
|
|
}
|
|
numbackedup = 0;
|
|
UnlatchCVars();
|
|
}
|
|
|
|
cvar_t *cvar_t::FindCVar (std::string_view var_name, cvar_t **prev)
|
|
{
|
|
cvar_t *var;
|
|
|
|
if (var_name.empty())
|
|
return nullptr;
|
|
|
|
var = ad.GetCVars();
|
|
*prev = nullptr;
|
|
while (var)
|
|
{
|
|
if (iequals(var->m_Name, var_name))
|
|
break;
|
|
*prev = var;
|
|
var = var->m_Next;
|
|
}
|
|
return var;
|
|
}
|
|
|
|
void cvar_t::UnlatchCVars (void)
|
|
{
|
|
cvar_t *var;
|
|
|
|
var = ad.GetCVars();
|
|
while (var)
|
|
{
|
|
if (var->m_Flags & (CVAR_MODIFIED | CVAR_LATCH))
|
|
{
|
|
unsigned oldflags = var->m_Flags & ~CVAR_MODIFIED;
|
|
var->m_Flags &= ~(CVAR_LATCH);
|
|
if (var->m_LatchedString.length())
|
|
{
|
|
var->Set (var->m_LatchedString.c_str());
|
|
var->m_LatchedString = "";
|
|
}
|
|
var->m_Flags = oldflags;
|
|
}
|
|
var = var->m_Next;
|
|
}
|
|
}
|
|
|
|
//
|
|
// C_SetCvarsToDefault
|
|
//
|
|
// Initialize cvars to default values after they are created.
|
|
// [SL] bitflag can be used to filter which cvars are set to default.
|
|
// The default value for bitflag is 0xFFFFFFFF, which effectively disables
|
|
// the filtering.
|
|
//
|
|
void cvar_t::C_SetCVarsToDefaults (unsigned int bitflag)
|
|
{
|
|
cvar_t *cvar = ad.GetCVars();
|
|
|
|
while (cvar)
|
|
{
|
|
if (cvar->m_Flags & bitflag)
|
|
{
|
|
if (cvar->m_Default.length())
|
|
cvar->Set (cvar->m_Default.c_str());
|
|
}
|
|
|
|
cvar = cvar->m_Next;
|
|
}
|
|
}
|
|
|
|
void cvar_t::C_ArchiveCVars (void *f)
|
|
{
|
|
cvar_t *cvar = ad.GetCVars();
|
|
|
|
while (cvar)
|
|
{
|
|
if ((baseapp == client && (cvar->m_Flags & CVAR_CLIENTARCHIVE))
|
|
|| (baseapp == server && (cvar->m_Flags & CVAR_SERVERARCHIVE)))
|
|
{
|
|
fmt::print((FILE *)f, "// {}\n", cvar->helptext());
|
|
fmt::print((FILE *)f, "set {} {}\n\n", C_QuoteString(cvar->name()), C_QuoteString(cvar->str()));
|
|
}
|
|
cvar = cvar->m_Next;
|
|
}
|
|
}
|
|
|
|
void cvar_t::cvarlist()
|
|
{
|
|
cvar_t *var = ad.GetCVars();
|
|
int count = 0;
|
|
|
|
while (var)
|
|
{
|
|
unsigned flags = var->m_Flags;
|
|
|
|
count++;
|
|
PrintFmt(PRINT_HIGH, "{:c}{:c}{:c}{:c} {} \"{}\"\n",
|
|
flags & CVAR_ARCHIVE ? 'A' :
|
|
flags & CVAR_CLIENTARCHIVE ? 'C' :
|
|
flags & CVAR_SERVERARCHIVE ? 'S' : ' ',
|
|
flags & CVAR_USERINFO ? 'U' : ' ',
|
|
flags & CVAR_SERVERINFO ? 'S' : ' ',
|
|
flags & CVAR_NOSET ? '-' :
|
|
flags & CVAR_LATCH ? 'L' :
|
|
flags & CVAR_UNSETTABLE ? '*' : ' ',
|
|
var->name(),
|
|
var->str());
|
|
var = var->m_Next;
|
|
}
|
|
PrintFmt(PRINT_HIGH, "{} cvars\n", count);
|
|
}
|
|
|
|
|
|
static std::string C_GetValueString(const cvar_t* var)
|
|
{
|
|
if (!var)
|
|
return "unset";
|
|
|
|
if (var->flags() & CVAR_NOENABLEDISABLE)
|
|
return '"' + var->str() + '"';
|
|
|
|
if (atof(var->cstring()) == 0.0f)
|
|
return "disabled";
|
|
else
|
|
return "enabled";
|
|
}
|
|
|
|
static std::string C_GetLatchedValueString(const cvar_t* var)
|
|
{
|
|
if (!var)
|
|
return "unset";
|
|
|
|
if (!(var->flags() & CVAR_LATCH))
|
|
return C_GetValueString(var);
|
|
|
|
if (var->flags() & CVAR_NOENABLEDISABLE)
|
|
{
|
|
return fmt::sprintf("\"%s\"", var->latched());
|
|
}
|
|
|
|
if (atof(var->latched()) == 0.0f)
|
|
return "disabled";
|
|
else
|
|
return "enabled";
|
|
}
|
|
|
|
BEGIN_COMMAND (set)
|
|
{
|
|
if (argc != 3)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "usage: set <variable> <value>\n");
|
|
}
|
|
else
|
|
{
|
|
cvar_t *var, *prev;
|
|
|
|
var = cvar_t::FindCVar (argv[1], &prev);
|
|
if (!var)
|
|
{
|
|
const std::string description = "Unsupported in Odamex v" + std::string(NiceVersion());
|
|
var = new cvar_t(argv[1], argv[2], description.c_str(), CVARTYPE_NONE,
|
|
CVAR_NOENABLEDISABLE | // If we got here due to LoadDefaults, make sure we save the value back out as-is.
|
|
CVAR_AUTO |
|
|
CVAR_UNSETTABLE |
|
|
cvar_defflags);
|
|
}
|
|
|
|
if (var->flags() & CVAR_NOSET)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "{} is write protected.\n", argv[1]);
|
|
return;
|
|
}
|
|
else if (multiplayer && baseapp == client && (var->flags() & CVAR_SERVERINFO))
|
|
{
|
|
PrintFmt(PRINT_HIGH, "{} is under server control.\n", argv[1]);
|
|
return;
|
|
}
|
|
|
|
// [Russell] - Allow the user to specify either 'enable' and 'disable',
|
|
// this will get converted to either 1 or 0
|
|
// [AM] Introduce zdoom-standard "true" and "false"
|
|
if (!(var->flags() & CVAR_NOENABLEDISABLE))
|
|
{
|
|
if (strcmp("enabled", argv[2]) == 0 ||
|
|
strcmp("true", argv[2]) == 0)
|
|
{
|
|
argv[2] = (char *)"1";
|
|
}
|
|
else if (strcmp("disabled", argv[2]) == 0 ||
|
|
strcmp("false", argv[2]) == 0)
|
|
{
|
|
argv[2] = (char *)"0";
|
|
}
|
|
}
|
|
|
|
if (var->flags() & CVAR_LATCH)
|
|
{
|
|
// if new value is different from current value and latched value
|
|
if (strcmp(var->cstring(), argv[2]) && strcmp(var->latched(), argv[2]) && gamestate == GS_LEVEL)
|
|
PrintFmt(PRINT_HIGH, "{} will be changed for next game.\n", argv[1]);
|
|
}
|
|
|
|
var->Set(argv[2]);
|
|
}
|
|
}
|
|
END_COMMAND (set)
|
|
|
|
BEGIN_COMMAND (get)
|
|
{
|
|
cvar_t *prev;
|
|
cvar_t *var;
|
|
|
|
if (argc < 2)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "usage: get <variable>\n");
|
|
return;
|
|
}
|
|
|
|
var = cvar_t::FindCVar (argv[1], &prev);
|
|
|
|
if (var)
|
|
{
|
|
// [AM] Determine whose control the cvar is under
|
|
std::string control;
|
|
if (multiplayer && baseapp == client && (var->flags() & CVAR_SERVERINFO))
|
|
control = " (server)";
|
|
|
|
// [Russell] - Don't make the user feel inadequate, tell
|
|
// them its either enabled, disabled or its other value
|
|
PrintFmt(PRINT_HIGH, "\"{}\" is {}{}.\n",
|
|
var->name(), C_GetValueString(var), control);
|
|
|
|
if (var->flags() & CVAR_LATCH && var->flags() & CVAR_MODIFIED)
|
|
PrintFmt(PRINT_HIGH, "\"{}\" will be changed to {}.\n",
|
|
var->name(), C_GetLatchedValueString(var));
|
|
}
|
|
else
|
|
{
|
|
PrintFmt(PRINT_HIGH, "\"{}\" is unset.\n", argv[1]);
|
|
}
|
|
}
|
|
END_COMMAND (get)
|
|
|
|
BEGIN_COMMAND (toggle)
|
|
{
|
|
cvar_t *prev;
|
|
cvar_t *var;
|
|
|
|
if (argc < 2)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "usage: toggle <variable>\n");
|
|
return;
|
|
}
|
|
|
|
var = cvar_t::FindCVar (argv[1], &prev);
|
|
|
|
if (!var)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "\"{}\" is unset.\n", argv[1]);
|
|
}
|
|
else if (var->flags() & CVAR_NOENABLEDISABLE)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "\"{}\" cannot be toggled.\n", argv[1]);
|
|
}
|
|
else if (var->flags() & CVAR_NOSET)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "{} is write protected.\n", argv[1]);
|
|
return;
|
|
}
|
|
else if (multiplayer && baseapp == client && (var->flags() & CVAR_SERVERINFO))
|
|
{
|
|
PrintFmt(PRINT_HIGH, "{} is under server control.\n", argv[1]);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (var->flags() & CVAR_LATCH && var->flags() & CVAR_MODIFIED)
|
|
var->Set(!atof(var->latched()));
|
|
else
|
|
var->Set(!var->value());
|
|
|
|
// [Russell] - Don't make the user feel inadequate, tell
|
|
// them its either enabled, disabled or its other value
|
|
PrintFmt(PRINT_HIGH, "\"{}\" is {}.\n",
|
|
var->name(), C_GetValueString(var));
|
|
|
|
if (var->flags() & CVAR_LATCH && var->flags() & CVAR_MODIFIED)
|
|
PrintFmt(PRINT_HIGH, "\"{}\" will be changed to {}.\n",
|
|
var->name(), C_GetLatchedValueString(var));
|
|
}
|
|
}
|
|
END_COMMAND (toggle)
|
|
|
|
BEGIN_COMMAND (cvarlist)
|
|
{
|
|
cvar_t::cvarlist();
|
|
}
|
|
END_COMMAND (cvarlist)
|
|
|
|
BEGIN_COMMAND (help)
|
|
{
|
|
cvar_t *prev;
|
|
cvar_t *var;
|
|
|
|
if (argc < 2)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "usage: help <variable>\n");
|
|
return;
|
|
}
|
|
|
|
var = cvar_t::FindCVar (argv[1], &prev);
|
|
|
|
if (!var)
|
|
{
|
|
PrintFmt(PRINT_HIGH, "\"{}\" is unset.\n", argv[1]);
|
|
return;
|
|
}
|
|
|
|
PrintFmt(PRINT_HIGH, "Help: {} - {}\n", var->name(), var->helptext());
|
|
}
|
|
END_COMMAND (help)
|
|
|
|
BEGIN_COMMAND(errorout)
|
|
{
|
|
I_Error("errorout was run from the console");
|
|
}
|
|
END_COMMAND(errorout)
|
|
|
|
BEGIN_COMMAND(fatalout)
|
|
{
|
|
I_FatalError("fatalout was run from the console");
|
|
}
|
|
END_COMMAND(fatalout)
|
|
|
|
BEGIN_COMMAND(exceptout)
|
|
{
|
|
std::string crashma = "What's crashma?";
|
|
fmt::sprintf("%crashma game, lmao.\n", crashma.at(std::string::npos));
|
|
}
|
|
END_COMMAND(exceptout)
|
|
|
|
#if defined _WIN32
|
|
|
|
BEGIN_COMMAND(crashout)
|
|
{
|
|
std::terminate();
|
|
}
|
|
END_COMMAND(crashout)
|
|
|
|
#elif defined UNIX
|
|
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
|
|
BEGIN_COMMAND(crashout)
|
|
{
|
|
kill(getpid(), SIGSEGV);
|
|
}
|
|
END_COMMAND(crashout)
|
|
|
|
#endif
|
|
|
|
VERSION_CONTROL (c_cvars_cpp, "$Id$")
|