- Added CVARINFO lump support. This is a lump for defining mod-specific cvars and takes entries

of the form:
    <scope> [noarchive] <type> <name> [= <defaultvalue>];
  Where <scope> is one of:
  * server: This cvar is shared by all players, and in network games, only select players can
    change it.
  * user: Each player has their own copy of this cvar, which they can change independently.
  To prevent the cvar from being written to the config file, add noarchive to its definition.
  <Type> is one of:
  * int: An integral value. Defaults to 0.
  * float: A value that can include a fraction. Defaults to 0.0.
  * color: A color value. Default to black ("00 00 00").
  * bool: A boolean value that can hold either true or false. Defaults to false.
  * string: A string value. It's not too useful for mods but is included for completeness. Defaults to "".
  <Name> is the cvar's name and must begin with a letter and may only include alphanumeric
  characters and the underscore character.
  If you wish a non-standard default add an = character after the cvar's name followed by the
  default value you want to use. Example:
    server int mymod_coolness = 10;
- Fixed: FStringCVar::SetGenericRepDefault() did not make a copy of the input string.

SVN r4280 (trunk)
This commit is contained in:
Randy Heit 2013-05-25 16:34:23 +00:00
parent 2767f31a92
commit 787b84ef91
8 changed files with 214 additions and 40 deletions

View file

@ -135,7 +135,7 @@ FBaseCVar::~FBaseCVar ()
void FBaseCVar::ForceSet (UCVarValue value, ECVarType type)
{
DoSet (value, type);
if (Flags & CVAR_USERINFO)
if ((Flags & CVAR_USERINFO) && !(Flags & CVAR_NOSEND))
D_UserInfoChanged (this);
if (m_UseCallback)
Callback ();
@ -849,9 +849,7 @@ UCVarValue FStringCVar::GetFavoriteRepDefault (ECVarType *type) const
void FStringCVar::SetGenericRepDefault (UCVarValue value, ECVarType type)
{
if (DefaultValue)
delete[] DefaultValue;
DefaultValue = ToString (value, type);
ReplaceString(&DefaultValue, ToString(value, type));
if (Flags & CVAR_ISDEFAULT)
{
SetGenericRep (value, type);
@ -1277,7 +1275,7 @@ void FilterCompactCVars (TArray<FBaseCVar *> &cvars, DWORD filter)
// Accumulate all cvars that match the filter flags.
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{
if (cvar->Flags & filter)
if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_NOSEND))
cvars.Push(cvar);
}
// Now sort them, so they're in a deterministic order and not whatever
@ -1316,7 +1314,7 @@ FString C_GetMassCVarString (DWORD filter, bool compact)
{
for (cvar = CVars; cvar != NULL; cvar = cvar->m_Next)
{
if ((cvar->Flags & filter) && !(cvar->Flags & CVAR_NOSAVE))
if ((cvar->Flags & filter) && !(cvar->Flags & (CVAR_NOSAVE|CVAR_NOSEND)))
{
UCVarValue val = cvar->GetGenericRep(CVAR_String);
dump << '\\' << cvar->GetName() << '\\' << val.String;
@ -1489,6 +1487,7 @@ FBaseCVar *FindCVarSub (const char *var_name, int namelen)
FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags)
{
assert(FindCVar(var_name, NULL) == NULL);
flags |= CVAR_AUTO;
switch (var_type)
{
case CVAR_Bool: return new FBoolCVar(var_name, 0, flags);
@ -1540,7 +1539,7 @@ void C_ArchiveCVars (FConfigFile *f, uint32 filter)
while (cvar)
{
if ((cvar->Flags &
(CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_AUTO|CVAR_USERINFO|CVAR_SERVERINFO|CVAR_NOSAVE))
(CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_MODARCHIVE|CVAR_AUTO|CVAR_USERINFO|CVAR_SERVERINFO|CVAR_NOSAVE))
== filter)
{
UCVarValue val;
@ -1671,14 +1670,16 @@ void FBaseCVar::ListVars (const char *filter, bool plain)
else
{
++count;
Printf ("%c%c%c %s : :%s\n",
flags & CVAR_ARCHIVE ? 'A' : ' ',
Printf ("%c%c%c%c %s = %s\n",
flags & CVAR_ARCHIVE ? 'A' :
flags & CVAR_MODARCHIVE ? 'M' : ' ',
flags & CVAR_USERINFO ? 'U' :
flags & CVAR_SERVERINFO ? 'S' :
flags & CVAR_AUTO ? 'C' : ' ',
flags & CVAR_SERVERINFO ? 'S' :
flags & CVAR_AUTO ? 'C' : ' ',
flags & CVAR_NOSET ? '-' :
flags & CVAR_LATCH ? 'L' :
flags & CVAR_UNSETTABLE ? '*' : ' ',
flags & CVAR_LATCH ? 'L' :
flags & CVAR_UNSETTABLE ? '*' : ' ',
flags & CVAR_NOSEND ? 'X' : ' ',
var->GetName(),
var->GetGenericRep (CVAR_String).String);
}

View file

@ -61,6 +61,8 @@ enum
CVAR_GLOBALCONFIG = 1024, // cvar is saved to global config section
CVAR_VIDEOCONFIG = 2048, // cvar is saved to video config section (not implemented)
CVAR_NOSAVE = 4096, // when used with CVAR_SERVERINFO, do not save var to savegame
CVAR_MODARCHIVE = 8192, // cvar will be saved to a mod-specific section of the ini
CVAR_NOSEND = 16384,// do not send cvar across the network (dummy mod cvar)
};
union UCVarValue

View file

@ -1358,6 +1358,136 @@ CCMD (endgame)
}
}
//==========================================================================
//
// ParseCVarInfo
//
//==========================================================================
void ParseCVarInfo()
{
int lump, lastlump = 0;
bool addedcvars = false;
while ((lump = Wads.FindLump("CVARINFO", &lastlump)) != -1)
{
FScanner sc(lump);
sc.SetCMode(true);
while (sc.GetToken())
{
FString cvarname;
char *cvardefault = NULL;
ECVarType cvartype = CVAR_Dummy;
int cvarflags = CVAR_MODARCHIVE;
FBaseCVar *cvar;
// Check for flag tokens.
while (sc.TokenType == TK_Identifier)
{
if (stricmp(sc.String, "server") == 0)
{
cvarflags |= CVAR_SERVERINFO;
}
else if (stricmp(sc.String, "user") == 0)
{
cvarflags |= CVAR_USERINFO;
}
else if (stricmp(sc.String, "noarchive") == 0)
{
cvarflags &= ~CVAR_MODARCHIVE;
}
else
{
sc.ScriptError("Unknown cvar attribute '%s'", sc.String);
}
sc.MustGetAnyToken();
}
// Do some sanity checks.
if ((cvarflags & (CVAR_SERVERINFO|CVAR_USERINFO)) == 0 ||
(cvarflags & (CVAR_SERVERINFO|CVAR_USERINFO)) == (CVAR_SERVERINFO|CVAR_USERINFO))
{
sc.ScriptError("One of 'server' or 'user' must be specified");
}
// The next token must be the cvar type.
if (sc.TokenType == TK_Bool)
{
cvartype = CVAR_Bool;
}
else if (sc.TokenType == TK_Int)
{
cvartype = CVAR_Int;
}
else if (sc.TokenType == TK_Float)
{
cvartype = CVAR_Float;
}
else if (sc.TokenType == TK_Color)
{
cvartype = CVAR_Color;
}
else if (sc.TokenType == TK_String)
{
cvartype = CVAR_String;
}
else
{
sc.ScriptError("Bad cvar type '%s'", sc.String);
}
// The next token must be the cvar name.
sc.MustGetToken(TK_Identifier);
if (FindCVar(sc.String, NULL) != NULL)
{
sc.ScriptError("cvar '%s' already exists", sc.String);
}
cvarname = sc.String;
// A default value is optional and signalled by a '=' token.
if (sc.CheckToken('='))
{
switch (cvartype)
{
case CVAR_Bool:
if (!sc.CheckToken(TK_True) && !sc.CheckToken(TK_False))
{
sc.ScriptError("Expected true or false");
}
cvardefault = sc.String;
break;
case CVAR_Int:
sc.MustGetNumber();
cvardefault = sc.String;
break;
case CVAR_Float:
sc.MustGetFloat();
cvardefault = sc.String;
break;
default:
sc.MustGetString();
cvardefault = sc.String;
break;
}
}
// Now create the cvar.
cvar = C_CreateCVar(cvarname, cvartype, cvarflags);
if (cvardefault != NULL)
{
UCVarValue val;
val.String = cvardefault;
cvar->SetGenericRepDefault(val, CVAR_String);
}
// To be like C and ACS, require a semicolon after everything.
sc.MustGetToken(';');
addedcvars = true;
}
}
// Only load mod cvars from the config if we defined some, so we don't
// clutter up the cvar space when not playing mods with custom cvars.
if (addedcvars)
{
GameConfig->DoModSetup (gameinfo.ConfigName);
}
}
//==========================================================================
//
// D_AddFile
@ -2164,7 +2294,10 @@ void D_DoomMain (void)
allwads.Clear();
allwads.ShrinkToFit();
SetMapxxFlag();
// Now that wads are loaded, define mod-specific cvars.
ParseCVarInfo();
// [RH] Initialize localizable strings.
GStrings.LoadStrings (false);

View file

@ -92,22 +92,6 @@ enum
const char *GenderNames[3] = { "male", "female", "other" };
static const char *UserInfoStrings[] =
{
"name",
"autoaim",
"color",
"skin",
"team",
"gender",
"neverswitchonpickup",
"movebob",
"stillbob",
"playerclass",
"colorset",
NULL
};
// Replace \ with %/ and % with %%
FString D_EscapeUserInfo (const char *str)
{
@ -394,7 +378,7 @@ void D_SetupUserInfo ()
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->GetNext())
{
if (cvar->GetFlags() & CVAR_USERINFO)
if ((cvar->GetFlags() & (CVAR_USERINFO|CVAR_NOSEND)) == CVAR_USERINFO)
{
FBaseCVar **newcvar;
FName cvarname(cvar->GetName());
@ -432,7 +416,7 @@ void userinfo_t::Reset()
// Create userinfo vars for this player, initialized to their defaults.
for (FBaseCVar *cvar = CVars; cvar != NULL; cvar = cvar->GetNext())
{
if (cvar->GetFlags() & CVAR_USERINFO)
if ((cvar->GetFlags() & (CVAR_USERINFO|CVAR_NOSEND)) == CVAR_USERINFO)
{
ECVarType type;
FName cvarname(cvar->GetName());

View file

@ -86,6 +86,7 @@ FGameConfigFile::FGameConfigFile ()
OkayToWrite = false; // Do not allow saving of the config before DoGameSetup()
bMigrating = false;
bModSetup = false;
pathname = GetConfigPath (true);
ChangePathName (pathname);
LoadConfigFile (MigrateStub, NULL);
@ -376,8 +377,8 @@ void FGameConfigFile::DoGameSetup (const char *gamename)
SetRavenDefaults (gameinfo.gametype == GAME_Hexen);
}
// The NetServerInfo section will be read when it's determined that
// a netgame is being played.
// The NetServerInfo section will be read and override anything loaded
// here when it's determined that a netgame is being played.
strncpy (subsection, "LocalServerInfo", sublen);
if (SetSection (section))
{
@ -445,6 +446,24 @@ void FGameConfigFile::DoGameSetup (const char *gamename)
OkayToWrite = true;
}
// Like DoGameSetup(), but for mod-specific cvars.
// Called after CVARINFO has been parsed.
void FGameConfigFile::DoModSetup(const char *gamename)
{
mysnprintf(section, countof(section), "%s.Player.Mod", gamename);
if (SetSection(section))
{
ReadCVars(CVAR_MODARCHIVE|CVAR_USERINFO|CVAR_NOSEND);
}
mysnprintf(section, countof(section), "%s.LocalServerInfo.Mod", gamename);
if (SetSection (section))
{
ReadCVars (CVAR_MODARCHIVE|CVAR_SERVERINFO|CVAR_NOSEND);
}
// Signal that these sections should be rewritten when saving the config.
bModSetup = true;
}
void FGameConfigFile::ReadNetVars ()
{
strncpy (subsection, "NetServerInfo", sublen);
@ -452,21 +471,35 @@ void FGameConfigFile::ReadNetVars ()
{
ReadCVars (0);
}
if (bModSetup)
{
mysnprintf(subsection, sublen, "NetServerInfo.Mod");
if (SetSection(section))
{
ReadCVars(CVAR_MODARCHIVE|CVAR_SERVERINFO|CVAR_NOSEND);
}
}
}
// Read cvars from a cvar section of the ini. Flags are the flags to give
// to newly-created cvars that were not already defined.
void FGameConfigFile::ReadCVars (DWORD flags)
{
const char *key, *value;
FBaseCVar *cvar;
UCVarValue val;
if (!(flags & CVAR_MODARCHIVE))
{
flags |= CVAR_ARCHIVE|CVAR_UNSETTABLE;
}
flags |= CVAR_AUTO;
while (NextInSection (key, value))
{
cvar = FindCVar (key, NULL);
if (cvar == NULL)
{
cvar = new FStringCVar (key, NULL,
CVAR_AUTO|CVAR_UNSETTABLE|CVAR_ARCHIVE|flags);
cvar = new FStringCVar (key, NULL, flags);
}
val.String = const_cast<char *>(value);
cvar->SetGenericRep (val, CVAR_String);
@ -485,18 +518,35 @@ void FGameConfigFile::ArchiveGameData (const char *gamename)
ClearCurrentSection ();
C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_USERINFO);
if (bModSetup)
{
strncpy (subsection + 6, ".Mod", sublen - 6);
SetSection (section, true);
ClearCurrentSection ();
C_ArchiveCVars (this, CVAR_MODARCHIVE|CVAR_AUTO|CVAR_USERINFO);
}
strncpy (subsection, "ConsoleVariables", sublen);
SetSection (section, true);
ClearCurrentSection ();
C_ArchiveCVars (this, CVAR_ARCHIVE);
strncpy (subsection, netgame ? "NetServerInfo" : "LocalServerInfo", sublen);
// Do not overwrite the serverinfo section if playing a netgame, and
// this machine was not the initial host.
if (!netgame || consoleplayer == 0)
{ // Do not overwrite this section if playing a netgame, and
// this machine was not the initial host.
{
strncpy (subsection, netgame ? "NetServerInfo" : "LocalServerInfo", sublen);
SetSection (section, true);
ClearCurrentSection ();
C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_SERVERINFO);
if (bModSetup)
{
strncpy (subsection, netgame ? "NetServerInfo.Mod" : "LocalServerInfo.Mod", sublen);
SetSection (section, true);
ClearCurrentSection ();
C_ArchiveCVars (this, CVAR_MODARCHIVE|CVAR_AUTO|CVAR_SERVERINFO);
}
}
strncpy (subsection, "UnknownConsoleVariables", sublen);

View file

@ -47,6 +47,7 @@ public:
void DoGlobalSetup ();
void DoGameSetup (const char *gamename);
void DoModSetup (const char *gamename);
void ArchiveGlobalData ();
void ArchiveGameData (const char *gamename);
void AddAutoexec (DArgs *list, const char *gamename);
@ -65,6 +66,7 @@ private:
void ReadCVars (DWORD flags);
bool bMigrating;
bool bModSetup;
char section[64];
char *subsection;

View file

@ -957,5 +957,6 @@ CCMD(reset2saved)
{
GameConfig->DoGlobalSetup ();
GameConfig->DoGameSetup (gameinfo.ConfigName);
GameConfig->DoModSetup (gameinfo.ConfigName);
R_SetViewSize (screenblocks);
}

View file

@ -6885,7 +6885,8 @@ scriptwait:
case PCD_GETCVAR:
{
FBaseCVar *cvar = FindCVar (FBehavior::StaticLookupString (STACK(1)), NULL);
if (cvar == NULL)
// Either the cvar doesn't exist, or it's for a mod that isn't loaded, so return 0.
if (cvar == NULL || (cvar->GetFlags() & CVAR_NOSEND))
{
STACK(1) = 0;
}