BIG Problems with DataStore

I’m start learning DataStore. And for now I redone 2 scripts. First saves Player inventory, second saves Player buildings. But when I start combining they together I receive a problem - data don’t saves at all. But before combining they both work perfectly. For combining I use 2 keys, (player + ID + inventory) and (player + ID + building).

local RS = game:GetService("ReplicatedStorage")
local Remotes = game:GetService("ReplicatedStorage"):WaitForChild("Remotes")
local InitPlacement = Remotes:WaitForChild("InitPlacement")
local InvokePlacement = Remotes:WaitForChild("InvokePlacement")
local DSPlacement = Remotes:WaitForChild("DSPlacement")
local Delete = Remotes:WaitForChild("Delete")
local SaveSlot = "1"

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UIFuction = Remotes:WaitForChild("UIFunction")
local SlotsLoading = Remotes:WaitForChild("SlotsLoading")

local DataStoreService1 = DataStoreService:GetDataStore("Players", "Buildings")
local BuildMod = require(RS.ModulScripts:WaitForChild("BuildMod"))
local placementObjects = {}

local dataStore = DataStoreService:GetDataStore("Players", "Inventory")
local default = {
	SessionLock = false,
	Gold = 100,
	Crystals = 0,
	Stone = 0,
	-- and much others int values.
}

local updateAsync = Enum.DataStoreRequestType.UpdateAsync

function InitPlacement.OnServerInvoke(player, canvasPart)
	placementObjects[player] = BuildMod.new(canvasPart)
	return placementObjects[player].CanvasObjects
end

InvokePlacement.OnServerEvent:Connect(function(player, func, ...)
	if (placementObjects[player]) then
		BuildMod[func](placementObjects[player], ...)
	end
end)

function DSPlacement.OnServerInvoke(player, saving, useData)
	--First key for buildings
	local key = "Player_".. player.UserId .. "_Building" .. SaveSlot

	local success, result = pcall(function()
		if (saving and placementObjects[player]) then
			if (useData) then
				print("3.1")
				print(placementObjects[player])
				for k, v in pairs(placementObjects[player]) do
					print(k)
					print(v)
				end
				DataStoreService1:SetAsync(key, placementObjects[player]:Serialize())
			else
				print("3.2")
				DataStoreService1:SetAsync(key, {})
			end
		elseif (not saving) then
			return DataStoreService1:GetAsync(key)
		end
	end)

	if (success) then
		print("3.3")
		return saving or result
	else
		warn(result)
	end
end

local function Clear(player, Object)
	Object:Destroy()
end

local function waitForRequestBudget()
	local currentBudget = DataStoreService:GetRequestBudgetForRequestType(updateAsync)

	while currentBudget < 1 do
		currentBudget = DataStoreService:GetRequestBudgetForRequestType(updateAsync)
		wait(5)
	end
end

local function setUp(player)
	local name = player.Name
	local userId = player.UserId
	--Second key for inventory
	local key = "Player_" .. userId .. "_Inventory_" .. SaveSlot

	local StatsFolder = Instance.new("Folder")
	StatsFolder.Name = "StatsFolder"

	local GoldValue = Instance.new("IntValue")
	local CrystalsValue = Instance.new("IntValue")
	local StoneValue = Instance.new("IntValue")
	--and others intValues

	GoldValue.Name = "Gold"
	CrystalsValue.Name = "Crystals"
	StoneValue.Name = "Stone"
	--again tons of values

	local success, ret, data
	repeat
		waitForRequestBudget()
		success, ret = pcall(dataStore.UpdateAsync, dataStore, key, function(oldData)
			oldData = oldData or default

			if oldData.SessionLock then
				--He is still sessionlocked, so just wait
				if os.time() - oldData.SessionLock < 1800 then
					--session is alive
					--if something going wrong.
					print("Currently DataStore have issues. Try play game after 30 mins or more.")
					ret = "Wait"
				else
					--session is dead, take over
					oldData.SessionLock = os.time()
					data = oldData
					print("Your Data is DEAD. Recovering Data.")
					return data
				end
			else
				oldData.SessionLock = os.time()
				data = oldData
				print("Data loaded! Let's PLAY!!!")
				return data
			end
		end)

		if ret == "Wait" then
			wait(5)
		end
	until success or not Players:FindFirstChild(name)

	if success then
		print(Players:FindFirstChild(name))
		print(success)
		GoldValue.Value = data.Gold
		CrystalsValue.Value = data.Crystals
		StoneValue.Value = data.Stone
		--tons of values...
		GoldValue.Parent = StatsFolder
		CrystalsValue.Parent = StatsFolder
		StoneValue.Parent = StatsFolder
		--again
		-------------------------------------
		StatsFolder.Parent = player
	end
end

local function save(player, dontLeave, dontWait)
	local userId = player.UserId
	local key = "Player_" .. userId

	local StatsFolder = player:FindFirstChild("StatsFolder")

	if StatsFolder then
		local GoldValue = StatsFolder.Gold.Value
		local CrystalsValue = StatsFolder.Crystals.Value
		local StoneValue = StatsFolder.Stone.Value
		--and again...
		local success

		repeat
			if not dontWait then
				waitForRequestBudget()
			end
			success = pcall(dataStore.UpdateAsync, dataStore, key, function()
				return {
					SessionLock = dontLeave and os.time() or nil,
					Gold = GoldValue,
					Crystals = CrystalsValue,
					Stone = StoneValue,
					--and again...

				}
			end)
		until success
	end
end

local function onShutdown()
	if RunService:IsStudio() then
		wait(1)
	else
		for _,player in ipairs(Players:GetPlayers()) do
			coroutine.wrap(save)(player, nil, true)
		end
	end
end

local function AddValue(player, Value, newValue)
	local StatsFolder = player:FindFirstChild("StatsFolder")
	if StatsFolder then
		local Table2 = StatsFolder:GetDescendants()
		for _, item in ipairs(Table2) do
			if item.Name == Value then
				item.Value = item.Value + newValue
				return item.Value
			end
		end
	end
end

local function OnPlayerAdded(player)
	SlotsLoading:FireClient(player)
end

for _,player in ipairs(Players:GetPlayers()) do
	coroutine.wrap(setUp)(player)
end

UIFuction.OnServerInvoke = AddValue
Players.PlayerAdded:Connect(setUp)
Players.PlayerAdded:Connect(OnPlayerAdded)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutdown)
Delete.OnServerEvent:Connect(Clear)

To create this script I use:

Output don’t have any warning and errors. After re-entering, Output contain message:
"Currently DataStore have issues. Try play game after 30 mins or more."

If someone know, can he tell me where I make mistake? (And can we use this 2 keys together or not? If not, how I can save buildings and inventory, maybe from 2 diffirent scripts ?)

2 Likes

Maybe I’m not seeing something, but I think you are using different keys for saving and setting up:

local key = "Player_" .. userId

and

local key = "Player_" .. userId .. "_Inventory_" .. SaveSlot

I think 30 minutes ago, you actually saved using your key that you use to set up, and that’s why it prints
“Currently DataStore have issues. Try play game after 30 mins or more.”

This’s really help. BIG thx to you. I’m so inconsiderate… :anguished:

1 Like

Btw, regarding your onShutdown function, I noticed there were some problems with that so I updated my tutorial to use

local function onShutdown()
	if RunService:IsStudio() then
		task.wait(2)
	else
		local finished = Instance.new("BindableEvent")
		local allPlayers = Players:GetPlayers()
		local leftPlayers = #allPlayers

		for _,player in ipairs(allPlayers) do
			coroutine.wrap(function()
				saveData(player, true)
				leftPlayers -= 1
				if leftPlayers == 0 then
					finished:Fire()
				end
			end)()
		end

		finished.Event:Wait()
	end
end

I didnt understand entirely what is purpose of this code but your problem will resolve after removing SessionLock thing. It is locking load data for 30 min after save but i dont know why actually. scriptinghelpers page not loading also so i couldnt check.

SessionLocking is to prevent duping mainly.

It saves a value that you use when loading data to know if the player is still in another session or not.

Hmm i wasnt aware of that problem but this is not the solution also. In this case, it is blocking players from playing the game so this is worse i think but it is a choice. Also i am saying that this is not the solution because players can still get away from that in the scenario that “I started playing the game on the server at 4:30 local time, after that I spent one of my valuables and left the game and re-entered the game through my friend who lives in a place with 5:30 local time.” so system thinks I am playing after 1 hour which is not. So i did what i want if race conditions occurred

I think (because he marked it as solution) what caused this is, as I mentioned in my first reply, that he was using different keys.

os.time() always measures it in UTC time.

yeah it is in UTC but it returns different values for servers in different areas

I dont know entire code but if problem resolved no problem.