What's the best way to save in-game settings?

In my current project, you can edit the keybinds for various things, and for convenience, I want these settings to save so you don’t have to re-bind everything every time you join.

My problem is how one should go about this.

The simple method would be to put all the settings in a table, then send that table in a RemoteEvent for the server to then store in a DataStore. But I feel like this wouldn’t be desirable given the potential for how big this table could get.

My idea for a more optimized way to do this is to fire an event whenever the player changes a setting, then have the server update a value, and lastly save the settings when the player leaves the game.

Any help on this would be greatly appreciated!

9 Likes

You have to use datastores or an external database.

1 Like

So here’s my solution for those who face a similar problem;

Step 1: Set up a folder that holds all your settings. Each setting should be a value object, i.e., IntValue, StringValue, BoolValue, etc.
image
Mine looks like this ^

Step 2: Fire a remote event when changing one of the settings.
Step 3: When this remote is fired, you’ll want to send over the name of the setting, and the value you want to set it to. The server will set the value of the desired setting.
Step 4: Set up a DataStore that will hold all these settings.
Step 5: Converting all the values into a DataStore-savable table. The code below will do this for you,

--[[Variables]]--
local DataStoreService = game:GetService("DataStoreService");
local ReplicatedStorage = game:GetService("ReplicatedStorage");
local Players = game:GetService("Players");

local updateSettingEvent = ReplicatedStorage.UpdateSetting; --Just assign this variable to the event you want to use to update settings.

local DataStore = DataStoreService:GetDataStore("DataStoreName"); --Change this to what your DataStore name is, or don't.

--[[Functions]]--
--Setting Update Functions--
function updateSetting(plr, settingName, settingValue)
	local plrSetting = plr.Settings;
	plrSetting[settingName].Value = settingValue; --Sets the new value.
end
updateSettingEvent.OnServerEvent:Connect(updateSetting);

--Save/Load Functions--
function saveSettings(plr)
	local settingsFolder = plr.Settings;
	local settings = {}; --Make a table to hold our settings.

	for _, setting in next, settingsFolder:GetChildren() do --Go through each setting value
		settings[setting.Name] = setting.Value;
		--This makes a new entry in our settings table, with a value of the setting's value.
	end

	return settings;
end

function loadSettings(plr, settings)
	local settingsFolder = plr.Settings;
	for _, setting in next, settingsFolder:GetChildren() do
		setting.Value = settings[setting.Name]; --Sets the setting's value to what it's key was in the settings table.
	end
end

--Join/Leave Functions--
function onJoin(plr)
	local plrSettings = script.Settings:Clone(); --Makes a copy of the pre-made settings folder.
	plrSettings.Parent = plr; --Parents it to the player.

	local data;
	local success, errorMessage = pcall(function()
		data = DataStore:GetAsync(plr.UserId); --You can make the player's key whatever you want, I just prefer to use UserId's.
	end)

	if success and data then
		local settings = data.settings; --Gets the settings part of our data.
		loadSettings(plr, settings);
	else
		warn(errorMessage); --This will show up in the output if something went wrong.
	end
end
Players.PlayerAdded:Connect(onJoin);

function onLeave(plr)
	local data = {}; --It's better to make it all a single table than to make several requests to your DataStore.
	data.settings = saveSettings(plr); --This adds the 'settings' table from the saveSettings() function.

	local success, message = pcall(function()
		DataStore:SetAsync(plr.UserId, data); --Store the player's data. Once again, you can change what the player's key is.
	end)

	if success then
		print(plr.Name .. "'s data was saved successfully!");
	else
		warn(message); --This will show up in the output if something went wrong.
	end
end
Players.PlayerRemoving:Connect(onLeave);

I’m not exactly the best at teaching, but I hope I got the basic point across. If you have any questions or problem, feel free to ask!

28 Likes

For the updateSettingEvent, how would you update the settings using only a single event?

That’s what this script does? Unless you meant to change multiple settings with one event.

Never mind, i thought the settings data would actually go through the event, not that the event was just a ping for the settings data to update

I am having an issue, however. In output it says on line 24 ‘attempt to index nil with ‘my value’’

What does your code look like?

I pretty much copy-pasted, and my configuration has one boolvalue inside of it
nothing more than that

Then your error message implies that you’re trying to index something with “my value”, and that something is nil. That’s strange considering how on line 24, the only thing you could be indexing is the settings folder. Are you sure the player has a folder named “Settings” parented to it?

1 Like

wait hang on, so wdym by the player’s parent is settings?
do u mean the other way round

I’ve caught an error. I’m not sure if it’s what’s causing your problem, but I had forgotten to pass the plr argument when calling loadSettings() on line 50. I haven’t had the error you’ve been getting unfortunately.

What I had meant was that there needs to be a folder parented to the player named “Settings”
But I forgot that the script basically does it for you, so that’s not the problem.

yeah. something sus is going on for me

I’m getting nil now, from the onJoin function

It’ll warn nil if the player didn’t have any data, which isn’t anything bad. When the player leaves, the data should save and you won’t get the nil warning.

Okay, just when I finally thought I had it, I don’t
line 14, invalid argument #2 (string expected, got Instance).

When you fire the update setting event, you need to specify the name of the setting and the value you want to set it to.

so, :FireServer(Setting, Value)

Correct. Make sure it’s the name of the setting.