From 787b84ef91c33d26237639e3cdd292d22a9f5468 Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sat, 25 May 2013 16:34:23 +0000 Subject: [PATCH] - Added CVARINFO lump support. This is a lump for defining mod-specific cvars and takes entries of the form: [noarchive] [= ]; Where 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. 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 "". 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) --- src/c_cvars.cpp | 27 +++++---- src/c_cvars.h | 2 + src/d_main.cpp | 135 ++++++++++++++++++++++++++++++++++++++++- src/d_netinfo.cpp | 20 +----- src/gameconfigfile.cpp | 64 ++++++++++++++++--- src/gameconfigfile.h | 2 + src/menu/menu.cpp | 1 + src/p_acs.cpp | 3 +- 8 files changed, 214 insertions(+), 40 deletions(-) diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index 9681f3ece..81ba01135 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -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 &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); } diff --git a/src/c_cvars.h b/src/c_cvars.h index d7b767f33..744643d82 100644 --- a/src/c_cvars.h +++ b/src/c_cvars.h @@ -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 diff --git a/src/d_main.cpp b/src/d_main.cpp index aa4abd366..0f66f68a6 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -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); diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 2f4d7c9b9..2f75d8372 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -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()); diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index 56a8a2252..ddb331cfa 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -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(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); diff --git a/src/gameconfigfile.h b/src/gameconfigfile.h index 440d900da..74376b583 100644 --- a/src/gameconfigfile.h +++ b/src/gameconfigfile.h @@ -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; diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index 4b4b0c5e8..dc1b8ccd8 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -957,5 +957,6 @@ CCMD(reset2saved) { GameConfig->DoGlobalSetup (); GameConfig->DoGameSetup (gameinfo.ConfigName); + GameConfig->DoModSetup (gameinfo.ConfigName); R_SetViewSize (screenblocks); } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 2696863e1..a8469f46a 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -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; }