iam working on a new game
you can check the game devlogs here :-
Devlog#1 Here
Devlog#2 Here
Devlog#3 Here
and i wanted to update my datastore script to be more professional
after searching on how to make it better , i added session locking and i replaced SetAsync and GetAsync with UpdateAsync ,
the problem is with session locking , the script needs around 2 sec to save the data , but because iam the only player in the playtest the server shut down super quickly in a way that makes the script could save all the data , this is not a big issue since i also added auto saving every 1 min , the problem is at first it lock the session and after saving all the data it open it again and since the server shut down very quick it has no time to open the session again ! ,
so when the player joins the script kick the player because it thinks that the player session is locked , after it kicks the player it does not save his data
(because if the player already had data the data will be cleared because the script did not load it)
and the player stuck as session locked for half a hour , and for sure i am not gonna wait 30 min to playtest again !, i tried fixing it for 3 days now but i can not figure it out , so please if you can help me then help i will be so thankful
local PlayersService = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local DataBase = DataStoreService:GetDataStore("PlayersData")
local Assets = ReplicatedStorage:WaitForChild("Assets")
local PlayersGuns = Assets:WaitForChild("PlayersGuns")
local KickedPlayers = {}
local function WaitForBudget(RequestType , BudgetAmount)
local Budget = DataStoreService:GetRequestBudgetForRequestType(RequestType)
while Budget < BudgetAmount do
Budget = DataStoreService:GetRequestBudgetForRequestType(RequestType)
task.wait(5)
end
end
local function SaveData(Player , SessionLock , BindToClose)
if table.find(KickedPlayers , Player.UserId) then
print("player was kicked")
table.remove(KickedPlayers , table.find(KickedPlayers , Player.UserId))
return
end
local UserId = Player.UserId
if SessionLock then
local IsSucsuss , Data = nil
repeat
IsSucsuss , Data = pcall(function()
if not BindToClose then
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
end
DataBase:UpdateAsync(tostring(UserId).."Session" , function(OldVersion)
return {
["SessionLock"] = true,
["LastSessionTime"] = os.time(),
}
end)
end)
if not IsSucsuss then
warn(Data)
end
until IsSucsuss
end
local DoneRequests = 0
local Screws = 0
local MaxPositions = 6
local PlayerDataFolder = Player:FindFirstChild("Data")
if PlayerDataFolder then
local PlayerScrews = PlayerDataFolder:FindFirstChild("Screws")
local PositionStorage = PlayerDataFolder:FindFirstChild("MaxPotions")
if PlayerScrews and PositionStorage then
Screws = PlayerScrews.Value
MaxPositions = PositionStorage.Value
end
end
local GunParts = {}
if Player:FindFirstChild("OwnedGunParts") and #Player.OwnedGunParts:GetChildren() > 0 then
for i , GunPart in pairs(Player.OwnedGunParts:GetChildren()) do
if not table.find(GunParts , GunPart.Name) then
table.insert(GunParts , GunPart.Name)
end
end
end
local UsedGunParts = {}
if Player:FindFirstChild("UsedGunPieces") then
for i , Value in pairs(Player.UsedGunPieces:GetChildren()) do
if Value.Name == "Front" then
UsedGunParts["UsedFrontPiece"] = Value.Value
elseif Value.Name == "Middle" then
UsedGunParts["UsedMiddlePiece"] = Value.Value
elseif Value.Name == "Back" then
UsedGunParts["UsedBackPiece"] = Value.Value
end
end
end
local Gears = {}
if Player:FindFirstChild("OwnedGears") and #Player.OwnedGears:GetChildren() > 0 then
for i , Gear in pairs(Player.OwnedGears:GetChildren()) do
if not table.find(Gears , Gear.Name) then
table.insert(Gears , Gear.Name)
end
end
end
local UsedGear = nil
if Player:FindFirstChild("UsedGear") then
UsedGear = Player.UsedGear.Value
end
local Potions = {}
if Player:FindFirstChild("OwnedPotions") and #Player.OwnedPotions:GetChildren() > 0 then
for i , Potion in pairs(Player.OwnedPotions:GetChildren()) do
for i = 1 , Potion.Value do
table.insert(Potions , Potion.Name)
end
end
end
task.spawn(function()
local Sucsess , ErrorMessage
repeat
if not BindToClose then
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
end
Sucsess , ErrorMessage = pcall(function()
DataBase:UpdateAsync(tostring(UserId) , function(OldVersion)
return {
["Screws"] = Screws,
["MaxPotions"] = MaxPositions,
}
end)
end)
until Sucsess
DoneRequests += 1
end)
task.spawn(function()
local Sucsess , ErrorMessage
repeat
if not BindToClose then
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
end
Sucsess , ErrorMessage = pcall(function()
DataBase:UpdateAsync(tostring(UserId).."GunParts" , function(OldVersion)
return GunParts
end)
end)
until Sucsess
DoneRequests += 1
end)
task.spawn(function()
local Sucsess , ErrorMessage
repeat
if not BindToClose then
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
end
Sucsess , ErrorMessage = pcall(function()
DataBase:UpdateAsync(tostring(UserId).."UsedGunParts" , function(OldVersion)
return UsedGunParts
end)
end)
until Sucsess
DoneRequests += 1
end)
task.spawn(function()
local Sucsess , ErrorMessage
repeat
if not BindToClose then
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
end
Sucsess , ErrorMessage = pcall(function()
DataBase:UpdateAsync(tostring(UserId).."Gears" , function(OldVersion)
return Gears
end)
end)
until Sucsess
DoneRequests += 1
end)
task.spawn(function()
local Sucsess , ErrorMessage
repeat
if not BindToClose then
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
end
Sucsess , ErrorMessage = pcall(function()
DataBase:UpdateAsync(tostring(UserId).."UsedGear" , function(OldVersion)
return UsedGear
end)
end)
until Sucsess
DoneRequests += 1
end)
task.spawn(function()
local Sucsess , ErrorMessage
repeat
if not BindToClose then
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
end
Sucsess , ErrorMessage = pcall(function()
DataBase:UpdateAsync(tostring(UserId).."Potions" , function(OldVersion)
return Potions
end)
end)
until Sucsess
DoneRequests += 1
end)
repeat
task.wait(0.01)
until DoneRequests == 6
if SessionLock then
local IsSucsess2 , Data2 = nil
repeat
if not BindToClose then
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
end
IsSucsess2 , Data2 = pcall(function()
DataBase:UpdateAsync(tostring(UserId).."Session" , function(OldVersion)
return {
["SessionLock"] = false,
["LastSessionTime"] = os.time(),
}
end)
end)
until IsSucsess2
print("yes")
end
end
local function LoadCertainData(Key:string , Player:Player , DefualtData)
local Data = nil
repeat
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
local Sucsess = pcall(function()
DataBase:UpdateAsync(Key , function(OldVersion)
if OldVersion then
Data = OldVersion
return Data
else
return DefualtData
end
end)
end)
until (Sucsess and Data) or not PlayersService:FindFirstChild(Player.Name)
return Data
end
local function LoadData(Player:Player)
local Character = Player.CharacterAppearanceLoaded:Wait()
for i , Object in pairs(Player.PlayerGui:GetDescendants()) do
if Object:IsA("LocalScript") then
Object.Enabled = false
end
end
local SessionLock , SessionLockTime , IsSucsess , Error = nil
repeat
WaitForBudget(Enum.DataStoreRequestType.UpdateAsync , 1)
IsSucsess , Error = pcall(function()
DataBase:UpdateAsync(tostring(Player.UserId).."Session" , function(OldVersion)
if OldVersion then
SessionLock = OldVersion.SessionLock or false
SessionLockTime = OldVersion.LastSessionTime or os.time()
if SessionLock == true and (os.time() - SessionLockTime) < 1800 then
table.insert(KickedPlayers , Player.UserId)
Player:Kick("Failed to load your data , please try again later")
return nil
else
SessionLock = false
SessionLockTime = os.time()
return {
["SessionLock"] = true,
["LastSessionTime"] = SessionLockTime,
}
end
else
SessionLock = false
SessionLockTime = os.time()
return {
["SessionLock"] = true,
["LastSessionTime"] = SessionLockTime,
}
end
end)
end)
until (IsSucsess and SessionLock ~= nil and SessionLockTime) or not PlayersService:FindFirstChild(Player.Name)
if SessionLock or not PlayersService:FindFirstChild(Player.Name) then
return
end
local Folder = Instance.new("Folder")
Folder.Name = "Data"
Folder.Parent = Player
local Screws = Instance.new("IntValue")
Screws.Name = "Screws"
Screws.Parent = Folder
local PotionsStorage = Instance.new("IntValue")
PotionsStorage.Name = "MaxPotions"
PotionsStorage.Parent = Folder
local DefaultPlayerData = {
["Screws"] = 0,
["MaxPotions"] = 6,
}
local PlayerData = LoadCertainData(tostring(Player.UserId) , Player , DefaultPlayerData)
if not PlayersService:FindFirstChild(Player.Name) then
return
end
Screws.Value = PlayerData.Screws
PotionsStorage.Value = PlayerData.MaxPotions
local GunPartsFolder = Instance.new("Folder")
GunPartsFolder.Name = "OwnedGunParts"
GunPartsFolder.Parent = Player
local PlayerGunParts = LoadCertainData(tostring(Player.UserId).."GunParts" , Player , {"Default Chamber" , "Default Loading Port" , "Default Comb"})
if not PlayersService:FindFirstChild(Player.Name) then
return
end
if PlayerGunParts then
for i , GunPart in PlayerGunParts do
local Value = Instance.new("StringValue")
Value.Name = GunPart
Value.Parent = GunPartsFolder
end
end
local PlayerUsedGunPieces = Instance.new("Folder")
PlayerUsedGunPieces.Name = "UsedGunPieces"
PlayerUsedGunPieces.Parent = Player
local UsedPlayerGunParts = LoadCertainData(tostring(Player.UserId).."UsedGunParts" , Player , {
["UsedFrontPiece"] = "Default Chamber",
["UsedMiddlePiece"] = "Default Loading Port",
["UsedBackPiece"] = "Default Comb",
})
if not PlayersService:FindFirstChild(Player.Name) then
return
end
if UsedPlayerGunParts then
local Value1 = Instance.new("StringValue")
Value1.Name = "Front"
Value1.Value = UsedPlayerGunParts.UsedFrontPiece
Value1.Parent = PlayerUsedGunPieces
local Value2 = Instance.new("StringValue")
Value2.Name = "Middle"
Value2.Value = UsedPlayerGunParts.UsedMiddlePiece
Value2.Parent = PlayerUsedGunPieces
local Value3 = Instance.new("StringValue")
Value3.Name = "Back"
Value3.Value = UsedPlayerGunParts.UsedBackPiece
Value3.Parent = PlayerUsedGunPieces
end
local GearsFolder = Instance.new("Folder")
GearsFolder.Name = "OwnedGears"
GearsFolder.Parent = Player
local PlayerGears = LoadCertainData(tostring(Player.UserId).."Gears" , Player , {})
if not PlayersService:FindFirstChild(Player.Name) then
return
end
if PlayerGears then
for i , Gear in PlayerGears do
local Value = Instance.new("StringValue")
Value.Name = Gear
Value.Parent = GearsFolder
end
end
local PlayerUsedGear = LoadCertainData(tostring(Player.UserId).."UsedGear" , Player , "")
if not PlayersService:FindFirstChild(Player.Name) then
return
end
local Value = Instance.new("StringValue")
Value.Name = "UsedGear"
Value.Value = if GearsFolder:FindFirstChild(PlayerUsedGear) then PlayerUsedGear else ""
Value.Parent = Player
local PotionsFolder = Instance.new("Folder")
PotionsFolder.Name = "OwnedPotions"
PotionsFolder.Parent = Player
local PlayerPotions = LoadCertainData(tostring(Player.UserId).."Potions" , Player , {})
if not PlayersService:FindFirstChild(Player.Name) then
return
end
if PlayerPotions then
for i , Potion in PlayerPotions do
if PotionsFolder:FindFirstChild(Potion) then
local ExistedValue = PotionsFolder[Potion]
ExistedValue.Value += 1
else
local Value = Instance.new("IntValue")
Value.Name = Potion
Value.Value = 1
Value.Parent = PotionsFolder
end
end
end
for i , Object in pairs(Player.PlayerGui:GetDescendants()) do
if Object:IsA("LocalScript") then
Object.Enabled = true
end
end
end
game.Players.PlayerAdded:Connect(function(Player)
task.spawn(LoadData , Player)
end)
game.Players.PlayerRemoving:Connect(function(Player)
task.spawn(SaveData , Player , true , false)
end)
game:BindToClose(function()
for i, Player in pairs(game.Players:GetChildren()) do
task.spawn(SaveData , Player , true , true)
end
end)
while true do
task.wait(60)
for i , Player in ipairs(PlayersService:GetChildren()) do
task.spawn(SaveData , Player , false , false)
end
end