I’m not sure if this is a bug. I previously made a post claiming the BindToClose()
method was unnecessary and that only .PlayerRemoving
was required. This held true when saving data of any size but I’ve noticed an inconsistency after revising the codebase.
This is the hierarchy (the rest is a normal baseplate)
This is the code in the Server script
-- Services
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
-- Containers
local ServerModules = ServerStorage["Server Modules"]
-- Modules
local DATASTORE = require(ServerModules.DataStore)
-- Functions
local function PlayerAdded(player)
print(tostring(player) .. " joined the game!")
DATASTORE.LoadPlayerData(player)
end
local function PlayerRemoving(player)
print(tostring(player) .. " is leaving!")
task.wait(2) -- To yield and stop SavePlayerData() from firing, simulating a failed save
DATASTORE.SavePlayerData(player)
end
local function Exit()
for Index, player in pairs(Players:GetChildren()) do
if not DATASTORE.Saved[player] then
print("Save")
DATASTORE.SavePlayerData(player)
end
end
print("Server closed")
end
-- Connections
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)
-- BindToClose
game:BindToClose(Exit)
This is the code in the DataStore module
-- Services
local DataStoreService = game:GetService("DataStoreService")
-- DataStore
local DataStore = DataStoreService:GetDataStore("DataStore Index")
-- Module
local MODULE = {}
MODULE.Saved = {} -- To store if their data has already been saved
function MODULE.SavePlayerData(player)
local function SetData()
DataStore:IncrementAsync(player.UserId, 1) -- Increase their data by 1
end
local Success = pcall(SetData)
if Success then
print(tostring(player) .. "'s data saved!")
MODULE.Saved[player] = true
end
end
function MODULE.LoadPlayerData(player)
local Data
local function GetData()
Data = DataStore:GetAsync(player.UserId) -- player.UserId is only called once so I didn't create a variable for it
end
local Success = pcall(GetData)
if Success then
if not Data then -- Initialize the data if it doesn't exist
Data = 0
end
print(tostring(player) .. "'s data: " .. tostring(Data))
end
end
return MODULE
Sometimes (most of the time) I get this output
Rarely I get this output
Nothing consistently changes the outcome.
I’ve replaced the Exit()
function with the code below and that seemed to slightly increase the rate at which it successfully saves
local function Exit()
for Index, player in pairs(Players:GetChildren()) do
print(tostring(player)) -- Added this
if not DATASTORE.Saved[player] then
print("Save")
DATASTORE.SavePlayerData(player)
end
end
print("Server closed")
end
The server closes before the data can save and I want to avoid yielding with methods like task.wait()
it feels so unreliable and hacky
I also tried replacing the Saved
index with player.UserId
instead of the player object, that didn’t work
I’ve considered making a handler but if the code isn’t reliably firing I’m not sure how I can expect to integrate one (adding prints for some reason increases the success rate)
I’m thinking that as long as you don’t yield the .PlayerRemoving
event, it should be quick enough (not matter the size) to save the data. My issue is also wanting to perform other actions, like refunding something a player spawned in the server (checking for it, removing it, then modifying the data)
Note: I cannot test in an actual game because the output isn’t replicated when servers shutdown (most of the time, that also seems to be inconsistent)