Saving input settings

I have been trying to figure out how to get the player inputs to save for a while now. I searched for solution, but none of the things mentioned on other places helped.

Here’s the code I am using to try save it:



//=================================================================
// 
//=================================================================
void AI_Am_HumanPlayerController::SaveInputSettings()
{
	GetMutableDefault<UInputSettings>()->SaveKeyMappings();

	for (TObjectIterator<UPlayerInput> It; It; ++It)
	{
		It->ForceRebuildingKeyMaps();
	}

	PlayerInput->ForceRebuildingKeyMaps();
	PlayerInput->ActionMappings.Sort();
	PlayerInput->AxisMappings.Sort();
	//PlayerInput->AxisConfig.Sort();
	PlayerInput->GetKeysForAction(TEXT(""));
	PlayerInput->SaveConfig();
}


I am calling ForceRebuildKeyMaps in both the iterator and directly from the pointer because right now I am just trying anything to get it work before I start removing the redundant commands.

When I call the function it does seem to create Input.ini in “Saved\Config\Windows” but the contents are only this:



[/Script/Engine.InputSettings]
DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown

[/Script/Engine.PlayerInput]
DebugExecBindings=(Key=Pause,Command="Pause",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F11,Command="LevelEditor.ToggleImmersive",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F11,Command="MainFrame.ToggleFullscreen",Control=False,Shift=True,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F1,Command="ShowMouseCursor",Control=False,Shift=True,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F1,Command="viewmode wireframe",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=True,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F2,Command="viewmode unlit",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F3,Command="viewmode lit",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F4,Command="viewmode detaillighting",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F5,Command="viewmode shadercomplexity",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=F9,Command="shot showui",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=Period,Command="RECOMPILESHADERS CHANGED",Control=True,Shift=True,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=Comma,Command="PROFILEGPU",Control=True,Shift=True,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=Escape,Command="CloseEditorViewport",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=Tab,Command="FocusNextPIEWindow",Control=True,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=Tab,Command="FocusLastPIEWindow",Control=True,Shift=True,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)
DebugExecBindings=(Key=Apostrophe,Command="EnableGDT",Control=False,Shift=False,Alt=False,Cmd=False,bIgnoreCtrl=False,bIgnoreShift=False,bIgnoreAlt=False,bIgnoreCmd=False)


So, I am a bit confused about how saving config files works in general. When I was doing saving the usersettings at one point I noticed that it would not save any of the settings if they were still the default ones. Not 100% about this, though, but it might be that configs are only saved if they are non-default? So, either I am doing this completely incorrectly or the game thinks the inputs are still defaults and wont save them because of that?

Here’s how I change my inputs:



//=================================================================
// 
//=================================================================
void AI_Am_HumanPlayerController::SetActionKey(const FName ActionName, FKey Key, bool GamepadKey)
{
	const FKey &OldKey = FindActionKey(ActionName, GamepadKey);
	if (OldKey != EKeys::AnyKey)
	{
		ClearMappings(OldKey);
	}

	ClearMappings(Key);

	if (Key != EKeys::AnyKey)
	{
		FInputActionKeyMapping mapping;
		mapping.ActionName = ActionName;
		mapping.Key = Key;
		PlayerInput->AddActionMapping(mapping);
	}
}

//=================================================================
// 
//=================================================================
void AI_Am_HumanPlayerController::SetAxisKey(const FName AxisName, FKey Key, float Scale, bool GamepadKey)
{
	const FKey &OldKey = FindAxisKey(AxisName, Scale, GamepadKey);
	if (OldKey != EKeys::AnyKey)
	{
		ClearMappings(OldKey);
	}

	ClearMappings(Key);
		
	if (Key != EKeys::AnyKey)
	{
		FInputAxisKeyMapping mapping;
		mapping.AxisName = AxisName;
		mapping.Key = Key;
		mapping.Scale = Scale;
		PlayerInput->AddAxisMapping(mapping);
	}
}

//=================================================================
// 
//=================================================================
void AI_Am_HumanPlayerController::ClearMappings(FKey Key)
{
	//Remove any actio mappings that already use this key
	for (int32 i = PlayerInput->ActionMappings.Num() - 1; i >= 0; i--)
	{
		if (PlayerInput->ActionMappings*.Key == Key)
		{
			PlayerInput->RemoveActionMapping(PlayerInput->ActionMappings*);
		}
	}

	for (int32 i = PlayerInput->AxisMappings.Num() - 1; i >= 0; i--)
	{
		if (PlayerInput->AxisMappings*.Key == Key)
		{
			PlayerInput->RemoveAxisMapping(PlayerInput->AxisMappings*);
		}
	}
}


Going back to SaveInputSettings()



GetMutableDefault<UInputSettings>()->SaveKeyMappings();

for (TObjectIterator<UPlayerInput> It; It; ++It)
{
	It->ForceRebuildingKeyMaps();
}


This part is from the code I found from the internet, but the difference is that the code to used GetDefault(). GetDefault() was const pointer though so I switched it to GetMutableDefault() (I am not familiar how const_cast is supposed to work).



PlayerInput->ActionMappings.Sort();
PlayerInput->AxisMappings.Sort();


This code was from another thread, but it didn’t help me either.



PlayerInput->GetKeysForAction(TEXT(""));


This is what I tried doing originally myself before searching for help. I noticed that in ForceRebuildingKeyMaps() it doesn’t really rebuild them yet? It only marks them as having to be rebuilt, if that is what the Reset() function does for the KeyMaps. So I thought I might need to call ConditionalBuildKeyMappings() except that it’s private. So, that is what PlayerInput GetKeysForAction is for, since it calls ConditionalBuildKeyMappings(). But even in ConditionalBuildKeyMappings() the keys are only rebuilt if the size of the KeyMaps is zero, and I am not sure if it actually does it. I am not sure if the KeyMaps size is set to zero from Reset().

Anyway, I am not sure if I should just give up on this whole thing and save the inputs on a separate file from Input.ini. I just thought that Input.ini was the right way to do it, as it would feel like it would be.

I seem to have it working correctly at least to save the input settings. I have no idea if this is the correct way to do this, though.

I created a custom temporary UObject class



#pragma once

#include "Classes/GameFramework/PlayerInput.h"
#include "SavingInput.generated.h"

struct FInputAxisConfigEntry;
struct FInputActionKeyMapping;
struct FInputAxisKeyMapping;

UCLASS(config = Input)
class I_AM_HUMAN_API UInputSaving : public UObject
{
	GENERATED_BODY()

public:

	/** This player's version of the Axis Properties */
	UPROPERTY(config)
	TArray<struct FInputAxisConfigEntry> AxisConfig;

	/** This player's version of the Action Mappings */
	UPROPERTY(config)
	TArray<struct FInputActionKeyMapping> ActionMappings;

	/** This player's version of Axis Mappings */
	UPROPERTY(config)
	TArray<struct FInputAxisKeyMapping> AxisMappings;

	/** List of Axis Mappings that have been inverted */
	UPROPERTY(config)
	TArray<FName> InvertedAxis;
};


In my player controller class:


//=================================================================
// 
//=================================================================
void AI_Am_HumanPlayerController::LoadInputSettings()
{
	UInputSaving *Loading = NewObject<UInputSaving>(this);
	Loading->LoadConfig();

	if (Loading->AxisConfig.Num())
		PlayerInput->AxisConfig = Loading->AxisConfig;

	if (Loading->ActionMappings.Num())
		PlayerInput->ActionMappings = Loading->ActionMappings;

	if (Loading->AxisMappings.Num())
		PlayerInput->AxisMappings = Loading->AxisMappings;

	if (Loading->InvertedAxis.Num())
		PlayerInput->InvertedAxis = Loading->InvertedAxis;
}

//=================================================================
// 
//=================================================================
void AI_Am_HumanPlayerController::SaveInputSettings()
{
	UInputSaving *Saving = NewObject<UInputSaving>(this);
	Saving->AxisConfig = PlayerInput->AxisConfig;
	Saving->ActionMappings = PlayerInput->ActionMappings;
	Saving->AxisMappings = PlayerInput->AxisMappings;
	Saving->InvertedAxis = PlayerInput->InvertedAxis;

	Saving->SaveConfig();
}


I am calling LoadInputSettings from BeginPlay