Runtime save console commands as user settings?

Does anyone know how to best handle this? I’m trying to implement a graphic option that lets users change anti aliasing method, bloom, ambient occlusion, turn off lumen, etc.. None of these have GetGameUserSettings values. I can retrieve and set the values using console commands, which works fine, but that has no functionality for saving. So how and where would I save these command results?

So for example “r.AntiAliasingMethod 0” would turn off anti aliasing, but how would I go about saving that so that on restart it still is applied.

If you are making custom settings on the frontend side, you can simply save the selected values yourself.

For example, save AA method, bloom, Lumen on/off, etc. into a SaveGame object. On game/session start, load that SaveGame object and re-apply those settings with console commands.

So when the player changes the option AntiAliasing you execute the command, save 0 as the selected AA method, then apply it again on next startup.

Also this below can help just in case sending.

It is also possible to read/write config/ini values and parse those into your settings menu, but for Blueprint/frontend settings, SaveGame + reapplying console commands is usually the simpler route.

I was doing that method in my last work on an options menu, it was working nice, read write ini with userconfig sharing this if helps you or somebody else in the future.

FString UMGSaveGameSubsystem::GetConfigIni(EMGIniConfigType IniType)
{
	FString ConfigFile;
	
	switch (IniType)
	{
	case EMGIniConfigType::EGameIni:
		ConfigFile = GGameIni;
		break;
	case EMGIniConfigType::EGameUserIni:
		ConfigFile = GGameUserSettingsIni;
		break; 
	case EMGIniConfigType::EEngineIni:
		ConfigFile = GEngineIni;
		break;
	case EMGIniConfigType::EInputIni:
		ConfigFile = GInputIni;
		break;
	case EMGIniConfigType::ESaveGameIni:
	
		ConfigFile = FConfigCacheIni::NormalizeConfigIniPath(FPaths::ProjectConfigDir() + TEXT("/DefaultGame.ini"));
		break;
	default: break; 
	}
	
	return ConfigFile;
}

FString UMGSaveGameSubsystem::ReadIniValue(EMGIniConfigType IniType, const FString SectionName, const FString PropertyName)
{
	FString Value;
	GConfig->GetString(*SectionName, *PropertyName, Value, GetConfigIni(IniType));
	return Value;
}

void UMGSaveGameSubsystem::WriteIniValue(EMGIniConfigType IniType, const FString SectionName, const FString PropertyName, const FString Value)
{
	GConfig->SetString(*SectionName, *PropertyName, *Value, GetConfigIni(IniType));
}

void UMGSaveGameSubsystem::RemoveIniValue(EMGIniConfigType IniType, const FString SectionName, const FString PropertyName, const FString Value)
{
	GConfig->RemoveFromSection(*SectionName, *PropertyName, *Value, GetConfigIni(IniType));
}
TArray<FString> UMGSaveGameSubsystem::ReadIniSection(EMGIniConfigType IniType, const FString SectionName)
{
	TArray<FString> SectionArray;
	FString Config = GetConfigIni(IniType);
	GConfig->GetSection(*SectionName, SectionArray, Config);

	//Maybe we can write a parser ? but this time values can vary in many terms?
	return SectionArray;
}

The GameUserSettings class is meant to be derived from so that you can add project specific settings to it.

So you create your own class derived from UGameUserSettings and there is a project setting (very similar to how you configure the GameInstance type your project should create when run).

Extending GameUserSettings seams like the cleaner solution, but how does it re-apply the console commands? I would still have to do that manually at session start right? Doesn’t look like I can extend GameUserSettings from blueprint either so may just go with SaveGame object I suppose.

It doesn’t reapply them exactly, it’d save the values and give you a way to reapply the value when the settings are loaded.

Oh, that sucks. Some of the things not exposed to blueprint are a little crazy. Add it to the list of the small set of things that can make things much nicer by having just a little C++. And not just for the developer, settings in an ini/text editable file are more user-friendly.

I think if there’s a way to save to the DefaultEngine.ini that’d allow these commands to be auto applied on start up like GameUserSettings. Has anyone tried that before?

Ok, from what I can find GameUserSettings.ini can hold DefaultEngine.ini or Engine.ini commands and apply them at startup fine. So it’s just a matter of extending it to support those commands. Looks like they’d go in the [SystemSettings] section. So a custom C++ extension of UGameUserSettings would be most ideal for implementing this. Might see if I can make an engine plugin that exposes it to BP and see if I can get BP variables to save into the ini file. That’d be handy.

Try this plugin maybe?

It makes editing ini files from blueprints rather painless, and it will stick a link in default ini to load your custom ini. If you add any settings that aren’t recognized by engine, you could just get them onbeginplay in game instance, then run any console/blueprint commands needed to apply the setting.

I’ll take a look, thank you. Currently though I want to explore extending GameUserSettings as that’s where i feel custom game user settings truly belong.

So I took a look at what I did before on this, just to refresh and sanity check the system.

It’s not bad. Seems I took a similar GameUserSettings approach, with the option to directly read/write ini when necessary.

Ok, actually managed to get this working. Extended the UGameUserSettings class. Exposes it to blueprint. Implemented Reflection so it loops through the blueprint variables. Then handles saving and loading them during ApplySettings and LoadSettings. The GameUserSettings.ini updates fine and the blueprint variables re-load on play fine.

So now that that’s working I need to figure out how to get it to execute commands on load. So for example if I save “r.BloomQuality=10” that doesn’t get set when the INI file loads. Need to get that figured out.

It also seams to duplicate the defaults and the custom BP class. Not sure if that’s intended behavior, but it might be.

SystemSettings is where the custom blueprint variables were added (spaces in variable names are converted to periods since periods aren’t allowed). Will probably need to extend it further to handle running command lines and specifying which variables are commands. Probably see about custom variable sections as well instead of defaulting to SystemSettings.

If anyone has any experience with having this ini file apply command configuration I’d appreciate any insight. If there’s no way to just have unreal engine handle that automatically I suppose extending LoadSettings to run the variables as commands would be ideal.

Once this is all done and fully working will probably throw it up on github if anyone is interested.

Fixed the double settings issue. Cleaned it up substantially so that it works exactly like C++ settings during LoadSettings, SaveSettings, and ResetToDefaults (resets the blueprint variables!).

Still working on having it automatically re-run command lines. Since command settings basically always have a period in them I’ll probably just automatically treat saved settings with periods as commands and run them during ApplySettings and ApplyNonResolutionSettings.

I also want to add some delegates for further extensibility.

Could use section names in the ini and some string matching and switch on string nodes or selects to append appropriate prefix to a key name. So if you have a part of ini like

[My Render Settings]
AntialiasingMethod=1

check if section name as string contains Render Settings and append r. to antialiasingmethod and feed it to console command node on load (along with value appended to end, obvs).

Code might start looking like a bowl of ramen tho, and might be better to edit engine source and add some of the special transient r. vars to the default ini.

I’ve already got it saving r values fine. I treat space as . in blueprint variable names so “r AntialiasingMethod” becomes “r.AntialiasingMethod” in the INI. What I’ll be implementing next is during ApplySettings for it to check for blueprint variables containing a “.” to then treat them as commands and execute them.

This should completely automate the entire process and all anyone would need to do is add variables to the blueprint GameUserSettings class, set that class as the GameUserSettings class in your project settings, and tada you’re done you can just access settings like you normally would. You’d just cast to your class, set your variables, call the SaveSettings function and all the magic happens.

So far this is saving and loading perfectly. I don’t have the command execution nor the delegates that I’d like to implement implemented yet as I’ve been busy past few days. Probably won’t get around to it until tomorrow or this weekend.

Once it’s all said and done it’ll be a 100% blueprint configurable GameUserSettings.

Ok, command application of blueprint variables is fully functional. Implemented same way vsync is handled with IConsoleManager. This ensures only valid cvars get set. Spaces are converted to periods since ini files don’t allow spaces and blueprint variables don’t allow periods. During ApplyNonResolutionSettings and LoadSettings it’ll run the commands.

Still looking into adding some delegates for more extensibility and maybe some blueprint exposed functions for more customization (e.g. be able to override from blueprint side the cvar key).

I’ve also made it so only public variables are handled. Protected and Private are ignored so you can add non-stored variables to the blueprint safely.

Hopefully have the github repo (it’ll be MIT license so anyone can do whatever they want with it) up sometime early next week. Probably be too busy this weekend to do much more with this.

Managed to make some progress over the weekend. SetToDefaults, ResetToCurrentSettings, and IsDirty calls (also added an IsBlueprintDirty) are all supported now and work with the blueprint variables.

Added Name and Enum variable support. Working on adding Byte, Integer64, Text, Vector, Rotator, Transform, and Struct types. I’m going to give arrays a try, but I don’t know if that’ll be worth it atm so might skip arrays.

Command line application is no longer implicit, but now explicit. There’s a new GetConsoleVariable function that lets you use a switch statement or whatever you like to convert blueprint variable name into a CVar name. I wasn’t happy with the “.” implying command line.

There’s also a new GetVariableSection that lets you specify a custom section to save blueprint variables to on a per-variable basis (blueprint variable name is passed to it).

Again, just doing this in my free time. So it’ll take me a few more days.

Ok, it’s finally done.

Supports well.. everything. Every blueprint variable type is allowed. They’ll all properly serialize and save into GameUserSettings.ini. Objects only save class pointers though. All of these have been tested.

There’s a Console Variables variable that lets you map blueprint variables to console variables. The function I originally implemented is now removed.

There’s events for load, apply, and save so the logic can be extended from your blueprint.

Don’t need C++ anymore for custom GameUserSettings now.