Datastore not Saving

Another day, another topic… (again), and this time it has to do with another more complicated problem: Datastoring.

I’m trying to make a simulator game that saves the data of the player, but for some reason, the data is returning nil no matter what. Whenever I print the table it just prints one specific part of the multi-table.

I’ve looked along the DevForum’s but haven’t seen anything helpful so far, and I’ve tried solving the issue myself.

Here is the dialogue module script:

local DataModule = {}

local DataStoreService = game:GetService("DataStoreService")
local PlayerStats = DataStoreService:GetDataStore("PlayerStats")
local PlayerInventoryData = DataStoreService:GetDataStore("PlayerInventory")
local PlayerSettingsData = DataStoreService:GetDataStore("PlayerSettingsData")
local PlayerMappingData = DataStoreService:GetDataStore("PlayerMapping")

local toolConfig = require(game.ReplicatedStorage.Modules:WaitForChild("Data"):WaitForChild("Tools"))
local attacktoolConfig = require(game.ReplicatedStorage.Modules:WaitForChild("Data"):WaitForChild("AttackTools"))

function DataModule.giveTool(player)
	local tool = player.inventory.EquippedTool.Value

	repeat task.wait() until player.Character

	local toolClone = game.ServerStorage:FindFirstChild("Tools"):FindFirstChild(tool):Clone()
	toolClone.Parent = player.Backpack
end

function DataModule.SaveData(player)
	local playerDataToSave = {}
	local playerToolsToSave = {}
	local playerSettingsToSave = {}
	local playerMappingToSave = {}
	
	local playerData = {
		Cheese = player.leaderstats.Cheese.Value,
		Money = player.leaderstats.Money.Value,
		Rebirths = player.leaderstats.Rebirths.Value,
	}
	playerDataToSave["PlayerData"] = playerData
	print(playerDataToSave["PlayerData"])
	
	local ownedToolsTable = {}
	for _, tool in ipairs(player.inventory.OwnedTools:GetChildren()) do
		ownedToolsTable[tool.Name] = tool.Value
	end

	local ownedAttackToolsTable = {}
	for _, tool in ipairs(player.inventory.OwnedAttackTools:GetChildren()) do
		ownedAttackToolsTable[tool.Name] = tool.Value
	end
	
	local inventory = {
		EquippedTool = player.inventory.EquippedTool.Value,
		OwnedTools = ownedToolsTable,
		EquippedAttackTool = player.inventory.EquippedAttackTool.Value,
		OwnedAttackTools = ownedAttackToolsTable,
	}
	
	playerToolsToSave["Inventory"] = inventory
	print(playerToolsToSave["Inventory"])
	
	local settingsData = {
		Shadows = player:WaitForChild("PlayerGui").MenuUI.Settings:WaitForChild("Settings").Shadows.Value,
		Music = player:WaitForChild("PlayerGui").MenuUI.Settings:WaitForChild("Settings").Music.Value,
		Sound = player:WaitForChild("PlayerGui").MenuUI.Settings:WaitForChild("Settings").Sound.Value,
	}
	
	playerSettingsToSave["Settings"] = settingsData
	
	local teleports = {}
	for _, teleport in ipairs(player.values:GetChildren()) do
		teleports[teleport.Name] = teleport.Value
	end
	playerMappingToSave["Teleports"] = teleports
	
	local success, errorMessage = pcall(function()
		PlayerStats:SetAsync(player.UserId, playerDataToSave)
		PlayerInventoryData:SetAsync(player.UserId, playerToolsToSave)
		PlayerSettingsData:SetAsync(player.UserId, playerSettingsToSave)
		PlayerMappingData:SetAsync(player.UserId, playerMappingToSave)
	end)
	
	if not success then
		warn("⚠️ "..errorMessage)
	else
		print("✅ Player's data has successfully saved.")
	end
end

function DataModule.LoadData(player)
	local playerData
	local playerInventory
	local playerSettings
	local playerMapping

	local success, errorMessage = pcall(function()
		playerData = PlayerStats:GetAsync(player.UserId)
		playerInventory = PlayerInventoryData:GetAsync(player.UserId)
		playerSettings = PlayerSettingsData:GetAsync(player.UserId)
		playerMapping = PlayerMappingData:GetAsync(player.UserId)
	end)
	
	if success then
		print(playerData)
		if playerData then
			print("✅ Data for player has successfully loaded.")
			
			-- Load cheese, money and rebirths
			player.leaderstats:WaitForChild("Cheese").Value = playerData.Cheese
			player.leaderstats.Money.Value = playerData.Money
			player.leaderstats.Rebirths.Value = playerData.Rebirths
		else
			print("⚠️ No data found for player "..player.Name..". Initializing data.")
			
			-- Initialize cheese, money, and rebirths
			local initialSuccess, initialError = pcall(function()
				player.leaderstats.Cheese.Value = 0
				player.leaderstats.Money.Value = 0
				player.leaderstats.Rebirths.Value = 0
			end)
			
			if not initialSuccess then
				warn("⚠️ Error initializing data: "..initialError)
				player:Kick("⚠️ An error occurred while initializing your data. Please rejoin the game later. ⚠️")
			end
		end
		
		if playerInventory then
			print(playerInventory)
			print("✅ Inventory data for player "..player.Name.." has successfully loaded.")
			print(playerInventory["Inventory"].EquippedTool)
			print(playerInventory["Inventory"])
			player.inventory.EquippedTool.Value = playerInventory.EquippedTool
			player.inventory.EquippedAttackTool.Value = playerInventory.EquippedAttackTools

			for toolName, toolValue in pairs(playerInventory.OwnedTools) do
				player.inventory.OwnedTools:FindFirstChild(toolName).Value = toolValue
			end
			
			for toolName, toolValue in pairs(playerInventory.OwnedAttackTools) do
				player.inventory.OwnedAttackTools:FindFirstChild(toolName).Value = toolValue
			end
		else
			-- Initialize owned tools.
			local initialSuccess, initialError = pcall(function()
				for _, tool in pairs(player.inventory.OwnedTools:GetChildren()) do
					if tool.Name == "BasicCheese" then
						tool.Value = true
					else
						tool.Value = false
					end
				end
				-- Attack tools.
				for _, tool in pairs(player.inventory.OwnedAttackTools:GetChildren()) do
					if tool.Name == "Nothing" then
						tool.Value = true
					else
						tool.Value = false
					end
				end

				player.inventory.EquippedTool.Value = "BasicCheese"
				player.inventory.EquippedAttackTool.Value = "Nothing"
			end)
			
			if not initialSuccess then
				warn("⚠️ Error initializing data: "..initialError)
				player:Kick("⚠️ An error occurred while initializing your data. Please rejoin the game later. ⚠️")
			end
		end
		
		if playerSettings then
			print("✅ Settings Data for player "..player.Name.." has successfully loaded.")
			print(playerSettings)
			player.PlayerGui.MenuUI.Settings.Settings.Shadows.Value = playerSettings.Shadows
			player.PlayerGui.MenuUI.Settings.Settings.Music.Value = playerSettings.Music
			player.PlayerGui.MenuUI.Settings.Settings.Sound.Value = playerSettings.Sound
		else
			print("⚠️ No settings data found for player "..player.Name..". Initializing data.")
			
			local initialSuccess, initialError = pcall(function()
				player.PlayerGui.MenuUI.Settings:WaitForChild("Settings").Shadows.Value = true
				player.PlayerGui.MenuUI.Settings:WaitForChild("Settings").Music.Value = true
				player.PlayerGui.MenuUI.Settings:WaitForChild("Settings").Sound.Value = true
			end)
			
			if not initialSuccess then
				warn("⚠️ Error initializing data: "..initialError)
				player:Kick("⚠️ An error occurred while initializing your data. Please rejoin the game later. ⚠️")
			end
		end
		
		if playerMapping then
			print("✅ Mapping Data for player "..player.Name.." has successfully loaded.")
			print(playerMapping["Teleports"])
			
			-- Load teleports
			for teleportName, teleportValue in pairs(playerMapping.Teleports) do
				player.values:FindFirstChild(teleportName).Value = teleportValue
				--if table.find(devTeam, player.Name) then
				--print(player.DisplayName.." is apart of the devteam! ⚠️")
				--player.values:FindFirstChild(teleportName).Value = true
				--end
			end
		else
			print("⚠️ No data found for player "..player.Name..". Initializing data.")

			-- Initialize teleports
			for _, teleport in ipairs(player.values:GetChildren()) do
				teleport.Value = false
			end
		end
	else
		warn("⚠️ Error loading data: "..errorMessage.." ⚠️")
		player:Kick("⚠️ "..player.Name.."'s data has failed to load. Please rejoin the game. ⚠️")
	end
end

return DataModule
3 Likes

I don’t need anyone to re-write this entire piece of code, I just need some pointers or some smaller code snippets on where the issue is. Thanks! :smile:

seems like no one has a solution so far :sob:

Not gonna lie, this is a bit hard to start looking into cause it’s not too clear what’s happening. Maybe if you could break it down for us into sections and use this feature:

hide details feature

to organize your problem better?

Well um. I’ve never seen anyone attempt to do a pcall() for calling all DataStore:SetAsync at once. I’m wondering which print statement you are referring to that prints nil?

Yeah, I thought I should’ve made it shorter.
Ok, so let’s start from the top:

Saving Data Function:

Summary

Basically, the function creates a table for each data to save and inserts all the values for everything that needs to go into each table.
(for ex:

local inventory = {
        EquippedTool = player.inventory.EquippedTool.Value,
        OwnedTools = ownedToolsTable,
        EquippedAttackTool = player.inventory.EquippedAttackTool.Value,
	OwnedAttackTools = ownedAttackToolsTable,
}
	
playerToolsToSave["Inventory"] = inventory).`

Toward the end of the function, the data is saved using :SetAsync(). I tried to put everything into a single pcall() so the code wouldn’t become messy.

Loading Data Function:

Summary

The longer function, but pretty much what it does is create an initial value (let’s call them dataTables) before using :GetAsync to get all of the data to set each dataTable accordingly.
(ex. playerSettings gets the PlayerSettingsData of the player.UserId)
Then, the function goes onto proceeds with the loading by changing the values of everything to the stored values of the dataTable it calls for.
(ex. player.leaderstats:WaitForChild("Cheese").Value = playerData.Cheese)
Before printing the data has successfully loaded and moving on to the next data to be loaded in, like playerMapping. If the data completely fails to load, the player gets kicked.

The Issue:

Summary

I can’t believe I forgot to add where the error was occurring! The error occurs on the line where the print(playerInventory["Inventory"].EquippedTool) is supposed to do its thing. This was to try and debug the main problem which occurs two lines later, but it prints: ReplicatedStorage.Modules.DataModule:124: attempt to index nil with 'EquippedTool'. The same thing happened before debugging, just with the actual line saying to change the value of EquippedTool. When I try to print player[“Inventory”] it prints the contents of ownedToolsTable, not sure if that’s a coincidence or not… I’ve tried a lot to try and solve the problem but to no avail.

Whenever trying to print playerInventory["Inventory"] in the saving function, it prints how I would expect it to be, all the tables with values and names within each accordingly. But the same story doesn’t happen for the loading function, as you heard earlier…

So, that’s why I’ve come here to ask everyone if they can help me figure out what’s going on. Data storage is pretty complicated, but I still hope that we can figure something out.

The issue is you’re trying to use DataStore to save a table, while data store doesn’t allow that. If you want to save the data with a table, you can do this:

game:GetService('DataStoreService'):SetASync(key,game:GetService('HttpService'):JSONEncode(PutTheTable))

To get data, you can make a variable referring to the JSON decoded version of the table:

local Data = game:GetService('HttpService'):JSONDecode(game:GetService('DataStoreService'):GetASync(key))

Then the variable would refer to a normal table like usual as if you were referring to the table you originally made.

You can store dictionaries and tables in a dataStore.

Yeah, and that’s the reason why I’m confused about it.

Are you sure? Saving tables with data store never worked for me until I started using JSON Encoding and Decoding

100% sure, I guess you must’ve messed up somewhere when trying it.

It would let me use :SetAsync(key,{})?

yeah​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

image
Here’s a visualization using the dataStore editor plugin, not sure what the best way to prove it is.

⁽ᵀʰᶦˢ ᶦˢ ᵐʸ ᵒʷⁿ ᵈᵃᵗᵃ, ᴵ’ᵐ ⁿᵒᵗ ˡᵉᵃᵏᶦⁿᵍ ᵃⁿʸᵗʰᶦⁿᵍ ʷᶦᵗʰᵒᵘᵗ ᵖᵉʳᵐᶦˢˢᶦᵒⁿ⁾

Thanks for sending that lol, I had no idea we didnt have to use JSON encoding and decoding

yeah, for a moment i thought i had to completely re-write my code

for example this would work:

local function SaveData(player)
	local playerData = {
		Kills = player.leaderstats.Kills.Value,
		Warnings = player.Warnings.Value
	}

	PlayerData:SetAsync(player.UserId, playerData)
end

I’d recommend just spamming prints all over your script to start pinpointing the problem,
also I’d recommend storing all the playerData in 1 datastore using a dictionary (or an array if you prefer so).

Using a dataStore module like SDM can help you organize the script as well since you don’t have to mess around with pcalls and data validation.

:grin: Yeah, I eventually figured it out though by just combining that piece of code into my current save and managed to make a similar code idea for the loading system, too.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.