Hi there is my datastore script good enough or do i need to change things for better optimization or make it more secure.
local Players = game:GetService("Players")
local DataService = game:GetService("DataStoreService")
local HttpsService = game:GetService("HttpService")
local MainDataStore = DataService:GetDataStore("MainData")
local SessionStore = DataService:GetDataStore("SessionStore")
local MaterialData = require(script.Parent.MaterialData)
local DataStoreTest = require(script.DataStoreTestSuite)
local ServerSessionId = game.JobId ~= "" and game.JobId or HttpsService:GenerateGUID(false)
local SESSION_TIMEOUT = 300
local DataHandler = {}
DataHandler.__index = DataHandler
local GlobalData = {}
local SaveLock = {}
local function ReleaseSessionLock(plr: Player)
local userId = tostring(plr.UserId)
local sessionKey = "Session_" .. tostring(userId)
pcall(function()
local current = SessionStore:GetAsync(sessionKey)
if current and current.SessionId == ServerSessionId then
SessionStore:RemoveAsync(sessionKey)
end
end)
end
function DataHandler.InitData(plr:Player)
local userId = tostring(plr.UserId)
local sessionKey = "Session_" .. tostring(userId)
local now = os.time()
print(now)
local got
local succes,error = pcall(function()
got = SessionStore:UpdateAsync(sessionKey, function(current)
if current == nil then
warn("nothing")
return {
SessionId = ServerSessionId,
TimeStamp = now
}
end
if current.SessionId == ServerSessionId then
warn("Same Server")
return current
end
if now - (current.TimeStamp) >SESSION_TIMEOUT then
warn("STAKE LOCK DETECTED FOR "..plr.Name.." Overiding")
return {
SessionId = ServerSessionId,
TimeStamp = now
}
end
warn("returing nil")
return nil
end)
end)
if not succes or not got then
plr:Kick("Your Data is locked by another session")
return
end
local data
local s,e = pcall(function()
data = MainDataStore:GetAsync(tostring(plr.UserId))
end)
if s then
if data then
print("Succesfully loaded")
data = DataHandler.ValidateData(data)
GlobalData[plr.UserId] = data
print(GlobalData[plr.UserId])
else
print("Doesnt have data")
GlobalData[plr.UserId] = DataHandler.ValidateData({})
end
else
warn("Couldnt Load data "..tostring(e))
plr:Kick("Couldnt Load Data")
end
end
function DataHandler.ValidateData(data)
if type(data) ~= "table" then return {Inventory = {}, DataVersion = 0} end
data.Inventory = typeof(data.Inventory) == "table" and data.Inventory or {}
data.DataVersion = typeof(data.DataVersion) == "number" and data.DataVersion or 0
return data
end
function DataHandler.SaveData(plr:Player)
if SaveLock[plr.UserId] == true then return end
SaveLock[plr.UserId] = true
local start = tick()
local s,e = pcall(function()
MainDataStore:UpdateAsync(tostring(plr.UserId),function(old)
if GlobalData[plr.UserId] == nil then return end
if type(GlobalData[plr.UserId].Inventory) ~= "table" or typeof(GlobalData[plr.UserId].DataVersion) ~= "number" then
warn("Invalid data format for ",plr)
GlobalData[plr.UserId] = DataHandler.ValidateData(GlobalData[plr.UserId])
print(GlobalData[plr.UserId])
return GlobalData[plr.UserId]
end
if not GlobalData[plr.UserId] then
warn("No data found for", plr)
GlobalData[plr.UserId] = DataHandler.ValidateData({})
return GlobalData[plr.UserId]
end
if (old) then
if old.DataVersion ~= GlobalData[plr.UserId].DataVersion then
return nil
else
GlobalData[plr.UserId].DataVersion += 1
print("Saved", plr.Name, "in", tick() - start, "seconds")
return GlobalData[plr.UserId]
end
else
GlobalData[plr.UserId].DataVersion += 1
print("Saved", plr.Name, "in", tick() - start, "seconds")
return GlobalData[plr.UserId]
end
end)
end)
if s then
print("Succesfully Saved Data")
SaveLock[plr.UserId] = nil
else
task.delay(1, function()
local retrySuccess = pcall(function()
DataHandler.SaveData(plr)
end)
if not retrySuccess then
plr:Kick("Could not autosave after retry: "..tostring(e))
end
end)
end
end
function DataHandler.GetPlayerData(plr)
local copy = table.clone(GlobalData[plr.UserId])
return copy
end
function DataHandler.SetPlayerInventoryData(plr, newData)
GlobalData[plr.UserId].Inventory = newData
end
Players.PlayerRemoving:Connect(function(plr)
warn("Using playeremoving")
ReleaseSessionLock(plr)
DataHandler.SaveData(plr)
GlobalData[plr.UserId] = nil
end)
game:BindToClose(function()
warn("Using BindToclose")
for _,plr in pairs(Players:GetPlayers()) do
coroutine.resume(coroutine.create(function()
ReleaseSessionLock(plr)
local s,e = pcall(function()
if not plr or not plr:IsDescendantOf(game) then return end
DataHandler.SaveData(plr)
end)
if not s then
warn(tostring(e))
end
end))()
end
end)
coroutine.resume(coroutine.create(function()
while true do
for _,plr in pairs(Players:GetPlayers()) do
if GlobalData[plr.UserId] then
coroutine.wrap(DataHandler.SaveData)(plr)
end
end
task.wait(60)
end
end))
return DataHandler