Issue with saving data

So, since yesterday I was tinkering on a data handling (module) script for my game. The data in question are character slots, each of which is a separate entry in a datastore.

When I was seemingly done with basics such as data loading on player join and data save on player leave / server shutdown, an issue arised: even though no warnings / errors were thrown, and everything looked to work as intended when observed in debug mode (I used both breakpoints and “debug prints” to see whether or not data was saved and loaded with correct keys and if there was data to be saved at all) - the data did not save!

PlayerDataHandler, at the time of writing this post:

-- Script responsible for loading and saving player data


local PlayerDataHandler = {}

---- Services and modules ----

local DataStoreService = game:GetService("DataStoreService")

---- Global variables ----

PlayerDataHandler.SessionData = {}

local dataStore: DataStore = DataStoreService:GetDataStore("PlayerData")
local dataTemplate = {
	Lives = 3
}

---- Functions ----

-- General functions
local function loadPlayerData(playerKey: string)
	local playerData = {}
	local slotsKeys = dataStore:ListKeysAsync(playerKey .. "/CharacterSlots/Slot_", nil, nil, true):GetCurrentPage()
	for _, slotKey in slotsKeys do
		local slotNumber = tonumber(string.match(string.match(slotKey.KeyName, "Slot_%d+"), '%d+'))
		local success, value = pcall(dataStore.GetAsync, dataStore, slotKey)
		if success then playerData[slotNumber] = value else warn("GetAsync failed. Error: " .. value) end
	end
	PlayerDataHandler.SessionData[playerKey] = if next(playerData) then playerData else {[1] = dataTemplate}
end

local function savePlayerData(playerKey: string)
	for slotNumber, slotData in PlayerDataHandler.SessionData[playerKey] do
		local slotKey = playerKey .. "/CharacterSlots/Slot_" .. slotNumber
		local success, value = pcall(dataStore.SetAsync, dataStore, slotKey, slotData)
		if not success then warn("SetAsync failed. Error: " .. value) end
	end
end

-- Event-related functions
local function onPlayerAdded(player: Player)
	local playerKey = "Player_" .. player.UserId
	loadPlayerData(playerKey)
end

local function onPlayerRemoving(player: Player)
	local playerKey = "Player_" .. player.UserId
	savePlayerData(playerKey)
	PlayerDataHandler.SessionData[playerKey] = nil
end

local function onServerShutDown()
	for playerKey, _ in PlayerDataHandler.SessionData do
		coroutine.wrap(savePlayerData)(playerKey)
	end
end

---- Events ----

game.Players.PlayerAdded:Connect(onPlayerAdded)
game.Players.PlayerRemoving:Connect(onPlayerRemoving)

game:BindToClose(onServerShutDown)

----

return PlayerDataHandler

To solve the issue, I have tried the following:

  • Commented out “PlayerDataHandler.SessionData[playerKey] = nil” line from onPlayerRemoving function, based on assumption that since I’m the only player testing onPlayerRemoving and onServerShutDown might be called too fast in succession, resulting in onServerShutdown overwriting data for the given key and setting it’s value to nil. The issue persisted, unchanged.

  • Changed the line “local success, value = pcall(dataStore.GetAsync, dataStore, slotKey.KeyName)” in loadPlayerData function. This solved the issue, as turns out I forgot I was dealing with DataStoreKey object and haven’t extracted it into a string.

If it helps, which I hope it will, here’s the script I use to test my module:

PDHTest

local PlayerDataHandler = require(game.ServerScriptService.PlayerDataHandler)

game.Players.PlayerAdded:Connect(function(player: Player)
	-- Wait until player's data is loaded
	local playerData
	repeat 
		playerData = PlayerDataHandler.SessionData["Player_" .. player.UserId]
		wait()
	until playerData
	-- Add an extra data slot to player data
	playerData[#playerData + 1] = {Lives = 3}
end)

Please help me track down the cause of the topic’s issue, and thank you for your time spent reading my post, as it turned out quite lenghty despite my attempts to make it shorter and simpler to comperhend.

1 Like
blank

@DarkblazeFlare389

shouldn’t

SetAsync(key,value) 

have only 2 arguments

why are you passing datastore as one of the arguments?
this also applies to

GetAsync(key)

which only requires the key

edit:
thank you didn’t know that it worked like this

Thats because how metatables work. You have to give a “self” argument if you are directly using the Index method in place for a member which would normally be used with namecall.

2 Likes

It should, and it does. I’m wrapping both GetAsync and SetAsync in pcall to catch errors which may arise on respective function runs.

The reason I’m passing datastore as second argument to pcall is because in luau, unlike other scripting languages such as python or javascript, calling class methods from it’s objects does not result in objects being silently passed as method’s “self” (object to call method upon) parameter - instead you have to manually pass the object to the method,

So, consider example:

local s: string = "HELLO WORLD"
print(s.lower()) -- Error: missing argument #1 to 'lower' (string expected) 
print(s.lower(s)) -- "hello world"

And now, going back to my code:

local success, response = pcall(
    dataStore.SetAsync, -- The method(function) to call
    dataStore, -- Object to call the method upon
    slotKey, slotData -- Arguments passed to method's parameters.
)

As much as I wish this was the issue, it isn’t. Still, thanks for your response,

1 Like

@DarkblazeFlare389

Hi there, i think i found the answer
in here you forgot to include .KeyName, i think

local success, value = pcall(dataStore.GetAsync, dataStore, slotKey.KeyName)

Indeed, the solution always lies in my own stupidity. This solved the issue, huge thanks! xD

1 Like

Glad it worked,
Thank you too,
I learnt something new

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