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.