How would i add a feature to save tables to this datastore script?

My friend made this a long time ago and i need help with saving tables. Id like to have a function like “createTable” or something, like the “createValue” function. How would i edit this to support that?

Code:

local players = game:GetService("Players")
local dataStoreService = game:GetService("DataStoreService")
local serverStorage = game:GetService("ServerStorage")
local replicatedStorage = game:GetService("ReplicatedStorage")

local updateValueServer = serverStorage.UpdateValue
local requestValueServer = serverStorage.RequestValue

local updateValueClient = replicatedStorage:WaitForChild("DatastoreUpdateValue")
local requestValueClient = replicatedStorage.RequestValue

local dataStore = dataStoreService:GetDataStore("PlayerData")

function getAsync(player : Player)
	
	local success = false
	local attempts = 0
	local data = nil
	
	while success == false and attempts < 8 do
		
		attempts += 1
		
		success = pcall(function()
			data = dataStore:GetAsync(tostring(player.UserId))
		end)
		
		task.wait(0.1)
		
	end
	
	return data, success
	
end

function setAsync(player : Player, valueName : string, value : string | number | boolean)
	
	local success = false
	local attempts = 0
	
	while success == false and attempts < 8 do
		
		attempts += 1
		
		success = pcall(function()
			local storedData = getAsync(player)
			storedData[valueName] = value
			dataStore:SetAsync(tostring(player.UserId), storedData, {player.UserId})
		end)
		
		task.wait(0.1)
		
	end
	
end

function createPlayerData(player : Player)
	
	local success = false
	local attempts = 0
	
	while success == false and attempts < 8 do
		
		attempts += 1
		
		success = pcall(function()
			dataStore:SetAsync(tostring(player.UserId), {}, {player.UserId})
		end)
		
		task.wait(0.1)
		
	end
	
end

function createValue(player : Player, valueName : string, defaultValue : string | number | boolean, valueLocation)
	
	local newValue = nil
	
	if type(defaultValue) == "string" then
		
		newValue = Instance.new("StringValue")
		
	elseif type(defaultValue) == "number" then
		
		newValue = Instance.new("NumberValue")
		
	elseif type(defaultValue) == "boolean" then
		
		newValue = Instance.new("BoolValue")
		
	end
	
	newValue.Name = valueName
	newValue.Parent = valueLocation
	
	local storedData, success = getAsync(player)
	
	if storedData == nil and success == true then
		storedData = {}
		createPlayerData(player)
	end
	
	local storedValue = storedData[valueName]
	if storedValue ~= nil then
		newValue.Value = storedValue
	else
		setAsync(player, valueName, defaultValue)
		newValue.Value = defaultValue
	end
	
end

function savePlayerData(player)
	
	print("saved ".. tostring(player) .. "'s Data!")
	--full system works
	--saves every 10 seconds + when they leave
	local leaderstats = player:FindFirstChild("leaderstats")
	local settingsFolder = player:FindFirstChild("SettingsFolder")
	
	for _, value : StringValue | NumberValue | BoolValue in pairs(leaderstats:GetChildren()) do
		
		setAsync(player, value.Name, value.Value)
		
	end
	
	for _, value : StringValue | NumberValue | BoolValue in pairs(settingsFolder:GetChildren()) do
		
		setAsync(player, value.Name, value.Value)
		
	end
	
end

function onInvoke(player : Player, valueName : string)
	
	local leaderstats = player:FindFirstChild("leaderstats")
	local settingsFolder = player:FindFirstChild("SettingsFolder")
	
	local checkValue : StringValue | NumberValue | BoolValue = leaderstats:FindFirstChild(valueName) or settingsFolder:FindFirstChild(valueName)
	
	if checkValue then
		
		return checkValue.Value
		
	end
	
	
end

function onServerInvoke(player : Player, valueName : string)
	
	local leaderstats = player:FindFirstChild("leaderstats")
	local settingsFolder = player:FindFirstChild("SettingsFolder")
	
	local checkValue : StringValue | NumberValue | BoolValue = leaderstats:FindFirstChild(valueName) or settingsFolder:FindFirstChild(valueName)
	
	if checkValue then
		
		if checkValue.Parent == settingsFolder then
			
			return checkValue.Value
			
		else
			
			warn("Cannot access server-side data from client!")
			return nil
			
		end
		
	end
	
end

requestValueServer.OnInvoke = onInvoke
requestValueClient.OnServerInvoke = onServerInvoke

players.PlayerAdded:Connect(function(player : Player)
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local settingsFolder = Instance.new("Folder")
	settingsFolder.Name = "SettingsFolder"
	settingsFolder.Parent = player
	

	createValue(player, "MN:Kills", 0, settingsFolder)
	createValue(player, "MN:Deaths", 0, settingsFolder)
	createValue(player, "KB:Sprint", "LeftControl", settingsFolder)
	createValue(player, "OT:WeaponUnlocks", "", settingsFolder)
	
end)

players.PlayerRemoving:Connect(function(player)
	
	savePlayerData(player)
	
end)

updateValueServer.Event:Connect(function(player : Player, valueName : string, value : string | number | boolean)
	
	local leaderstats = player:FindFirstChild("leaderstats")
	local settingsFolder = player:FindFirstChild("SettingsFolder")
	
	local checkValue : StringValue | NumberValue | BoolValue = leaderstats:FindFirstChild(valueName) or settingsFolder:FindFirstChild(valueName)
	
	if checkValue then
		
		checkValue.Value = value
		
	end
	
end)

updateValueClient.OnServerEvent:Connect(function(player : Player, valueName : string, value : string | number | boolean)
	
	local leaderstats = player:FindFirstChild("leaderstats")
	local settingsFolder = player:FindFirstChild("SettingsFolder")
	
	local checkValue : StringValue | NumberValue | BoolValue = leaderstats:FindFirstChild(valueName) or settingsFolder:FindFirstChild(valueName)

	if checkValue then
		
		if checkValue.Parent == settingsFolder then
			
			checkValue.Value = value
			
		else
			
			warn("Cannot access server-side data from client!")
			
		end
		
	end
	
end)

while task.wait(10) do
	
	for _, player in pairs(players:GetPlayers()) do
		
		savePlayerData(player)
		
	end
	
end

If you need anymore info, please tell me. I dont know anything about datastores.

There is no direct way to store a table using an Instance. Either you can use a StringValue with HttpService:JSONEncode to turn the table into a string, or create a structure of many Instances to form a table like Fruits below:

playerDataFolder
|--- Kills: IntValue (.Value = 5)
|--- Deaths: IntValue (.Value = 500)
|--- Fruits: Folder
     |--- 1: StringValue (.Value = "Apple")
     |--- 2: StringValue (.Value = "Orange")
|--- Revives: IntValue (.Value = 2)

, representing:

local playerData = {
  Kills = 5,
  Deaths = 500,
  Fruits = { "Apple", "Orange" },
  Revives = 2
}

Also, this code overall is sub-optimal. It is reduntantly calling :GetAsync for every stored value in the player’s saved data, even though it only needs to be called once.

I think that representing the player’s data with Instances isn’t a good idea anyway. I think it’'s better to just continue storing it as a table and instead store it within a ModuleScript, as that can be easily shared between all other Scripts (the variables of a ModuleScript are shared regardless of which Script require’d it, as long as it’s on the same side of the network).

alright, i thought of the whole JSONencode thing but didnt now if i was reliable. Thanks for the help