I am creating a datastore module. However, one of the functions error every test. I’m assuming it is DataGuard:GetData()
but I am not sure.
Module Code:
local metaServerData = {
ServerData = {};
}
local DataGuard = setmetatable({}, {__index = metaServerData})
local hs = game:GetService("HttpService")
local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("Data")
local sessionLocks = dss:GetDataStore("SessionLocked")
type Data = {}
type PlayerName = {}
if not script:WaitForChild("Save Structure", 2) then local ss = Instance.new("Folder") ss.Name = "Save Structure" ss.Parent = script end
local function SessionLock(player: Player)
local locked
sessionLocks:UpdateAsync(tostring(player.UserId), function(isLocked)
locked = isLocked
return true
end)
return locked
end
local function UnsessionLock(player: Player)
sessionLocks:UpdateAsync(tostring(player.UserId), function(isLocked)
return false
end)
end
local function createEvent(eventName)
local event = Instance.new("BindableEvent")
event.Name = eventName
return event.Event, event
end
function DataGuard:GetData(player: Player | PlayerName)
if not player then Warn("Missing player.") return end
if type(player) ~= "string" then if not player:IsA("Player") then return end end
local plr = type(player) == "string" and game.Players:FindFirstChild(player) or player
if not DataGuard.ServerData[plr] then return end
return DataGuard.ServerData[plr]
end
local function playerAdded(player: Player)
if DataGuard.ServerData[player] then return end
local data = {}
if not script["Save Structure"]:GetChildren()[1] then Warn("No save structure provided!") return end
for _, v in script["Save Structure"]:GetChildren() do
if not v:IsA("ValueBase") then continue end
data[v.Name] = v.Value
end
local metadata = {__metatable = "You can just see what's inside the metatable via the code..."; StatsStorage = {}}
local changedEvent, bindable = createEvent("Changed")
metadata.Changed = changedEvent
function metadata:Set(Key: string, NewValue: any)
if SessionLock(player) then return end
if Key == nil or NewValue == nil then Warn("Value missing!") return end
if not metadata.StatsStorage[Key] then Warn("Could not find", Key, "in", player.Name.."'s", "DataStore!") return end
metadata.StatsStorage[Key] = NewValue
bindable:Fire(Key, NewValue)
end
function metadata:Save()
if SessionLock(player) then return end
local success = false
for attempts = 1, 5 do
success = pcall(function()
ds:SetAsync(tostring(player.UserId), hs:JSONEncode(data))
end)
if success then break end
end
if not success then Warn("Could not save", player.Name.."'s data!") end
end
function metadata:GetLastSave(): Data
local returned
pcall(function()
local saved = ds:GetAsync(tostring(player.UserId))
if saved then
returned = hs:JSONDecode(saved)
else
returned = false
end
end)
return
end
function metadata:GetValue(Key: string): any
if Key == nil then Warn("Value missing!") return end
return metadata.StatsStorage[Key]
end
metadata.__index = metadata
metadata.__tostring = function(t)
if t ~= data then return t end
local s = "{"
for i, v in t do
s = s..v
if i ~= #t then
s = s..", "
end
end
s = s.."}"
return s
end
data = metadata:GetLastSave() or data
for k, v in data do
metadata.StatsStorage[k] = v
end
data = {}
setmetatable(data, metadata)
DataGuard.ServerData[player] = data
end
for _, v in game.Players:GetPlayers() do
playerAdded(v)
end
game.Players.PlayerAdded:Connect(playerAdded)
game.Players.PlayerRemoving:Connect(function(player)
DataGuard.ServerData[player]:Save()
UnsessionLock(player)
DataGuard.ServerData[player] = nil
end)
game:BindToClose(function()
for _, v in game.Players:GetPlayers() do
DataGuard.ServerData[v]:Save()
end
task.wait(5)
end)
function Warn(...)
warn("DataGuard:", ...)
end
local elapsed = 0
game:GetService("RunService").Heartbeat:Connect(function(delta)
elapsed = math.clamp(elapsed + delta, 0, 60)
if elapsed ~= 60 then return end
elapsed = 0
if not DataGuard.AutoSaveEnabled then return end
for _, v in game.Players:GetPlayers() do
DataGuard.ServerData[v]:Save()
end
end)
return DataGuard
Test Script Code:
local DataGuard = require(script.Parent:WaitForChild("DataGuard"))
game.Players.PlayerAdded:Connect(function(player)
local data
repeat data = DataGuard:GetData(player) task.wait() until DataGuard:GetData(player)
print(data)
print(getmetatable(data))
task.wait(0.09)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local kills = Instance.new("IntValue")
kills.Name = "Kills"
kills.Value = data:GetValue("Kills")
kills.Parent = leaderstats
local wins = Instance.new("IntValue")
wins.Name = "Wins"
wins.Value = data:GetValue("Wins")
wins.Parent = leaderstats
leaderstats.Parent = player
data.Changed:Connect(function(value, newvalue)
print(value, newvalue)
leaderstats[value].Value = newvalue
end)
player.Chatted:Connect(function(message)
if message:lower() == "kill" then
data:Set("Kills", data:GetValue("Kills") + 1)
elseif message:lower() == "win" then
data:Set("Wins", data:GetValue("Wins") + 1)
end
end)
end)