Why is this not saving?

Hello. i have this simple shop system and it works, except when you leave the game and rejoin, only your currency saves. this means the button in the shop makes u buy it again, and the button in ur inventory is now gone. (bc it didnt save)

does anyone know how to make it save or how to fix this? I have a leaderboard / datastore script in server script service, but the buttons still will not save. if anyone could help me fix this that would be great.

here are some of the scripts -

the datastore / leaderstat

local datastore = game:GetService("DataStoreService")
local ds1 = datastore:GetDataStore("MoneySaveSystem")

local storage = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")
local re = storage.RemoteEvent


re.OnServerEvent:Connect(function(player, value)
	if player.leaderstats.FairyDust.Value >= value then
		player.leaderstats.FairyDust.Value -= value
	else
		player:Kick("Do not exploit.")
	end
end)





game.Players.PlayerAdded:connect(function(plr)
	local folder = Instance.new("Folder", plr)
	folder.Name = "leaderstats"
	local FairyDust = Instance.new("IntValue", folder)
	FairyDust.Name = "FairyDust"

	FairyDust.Value = ds1:GetAsync(plr.UserId) or 0
	ds1:SetAsync(plr.UserId, FairyDust.Value)

	FairyDust.Changed:connect(function()
		ds1:SetAsync(plr.UserId, FairyDust.Value)
	end)

end)


game:BindToClose(function()
	for _, plr in pairs(players:GetPlayers()) do
		local money = plr.leaderstats.FairyDust
		ds1:SetAsync(plr.UserId, money.Value)
	end
end)



--Quest money -- ignore this

game.ReplicatedStorage.Quests.Hundred.OnServerEvent:Connect(function(plr)
	plr.leaderstats.FairyDust.Value = plr.leaderstats.FairyDust.Value +100
end)```

---------------------------------------------------------------------------------------------------------------------------------
and this is the script in the buy button which is in the shop

local storage = game:GetService("ReplicatedStorage")
local re = storage.RemoteEvent
local players = game:GetService("Players")
local player = players.LocalPlayer or players.PlayerAdded:Wait()
local item = script.Parent
local shoes = script.Parent.Parent.Parent.Frame1.Shoes  
local value = 40000

item.MouseButton1Click:Connect(function()
	if player.leaderstats.FairyDust.Value >= value then
		re:FireServer(value)
		item.Text = "Purchased"
		shoes.Visible = true
		value = 0
	else
		item.Text = "Insuffiecetn Funds"
		task.wait(2)
		item.Text = "Purchase"
	end
end)
8 Likes

i forgot to mention i do have a remote in replicated storage.

3 Likes

i dont see anything other than saving the “FairyDust”.

anyway why dont you save the data using the PlayerRemoving event along with BindToClose? instead of spamming SetAsync everytime the FairyDust of a player changes its value.

4 Likes

Hi thanks so much for getting back to me. how would i make the other things save?

also, i can try to do what you suggested with the fairydust value i just am not very good at scripting

2 Likes

what does the shop do after taking an amount from your “FairyDust”? (sorry for late response)

2 Likes

it basically just makes another button in a different frame visible, and make itself not buyable again, and it changes from Purchase to Purchased

2 Likes

You should save on player removing instead of doing it every time the value changes, could cause issues with datastore limits, causing data loss

2 Likes

i rlly would if i could, i just dont know how to script that well. only basic things.

2 Likes

that’s really simple, just connect data saving function to player removing event smtg like this

game.Players.PlayerRemoving:Connect(function(player)
ds1:SetAsync(player.UserId, player.leaderstats.FairyDust.Value)
end)
3 Likes

This might be happening because SetAsync() is being called immediately after setting the initial value of FairyDust

try this

game.Players.PlayerAdded:Connect(function(plr)
    local folder = Instance.new("Folder", plr)
    folder.Name = "leaderstats"
    local FairyDust = Instance.new("IntValue", folder)
    FairyDust.Name = "FairyDust"

    FairyDust.Value = ds1:GetAsync(plr.UserId) or 0

    FairyDust.Changed:Connect(function()
        ds1:SetAsync(plr.UserId, FairyDust.Value)
    end)

    ds1:SetAsync(plr.UserId, FairyDust.Value)
end)

1 Like

do it like this:

local dss = game:GetService("DataStoreService")
local players = game:GetService("Players")
local shop = dss:GetDataStore("themonkeshop")
local item = workspace.flyingball -- (assume this is the item u want to give)

players.PlayerAdded:Connect(function(player)
	-- in this case the game only has 1 item to purchase (if u have multiple just use for loops lol)
	local key = "FlyingBallsOfFate_UID" .. player.UserId
	if (shop:GetAsync(key)) then -- hooray, they have purchased it before.
		-- give the item
		item:Clone().Parent = player:WaitForChild("Backpack")
	end
end)

-- assume `re` is the remote that's called when purchase
re.OnServerEvent:Connect(function(player, value) -- i think value is the price of the "item"?
	if (player.leaderstats.FairyDust.Value < value) then return end; -- they dont have sufficient "FairyDust", nobody wants negative currency innit
	local UserId = player.UserId
	local key = "FlyingBallsOfFate_UID" .. UserId -- we make a key for an item. so if we assume the buyer's UserId is 123 the key becomes FlyingBallsOfFate_UID123
	local status = shop:GetAsync(key)
	if (status) then return end -- the person already has it so dont buy it again
	player.leaderstats.FairyDust.Value -= value -- take away the "FairyDust" thingy
	--- <do something>
	-- give the item
	item:Clone().Parent = player:WaitForChild("Backpack")
	-- tell the datastore service that they already bought it.
	shop:SetAsync(key, true)
end)

sorry if my comments aren’t understandable, i hope you can be a better scripter in the future :+1:

2 Likes

Hello and thanks for getting back to me. I dont know if this makes sense but there is no item. There is no backpack needed to be involved. Technically, the person is just clicking a button (the buy button) and that just makes another button in a different frame become visible, then when you click said button other stuff happens. I just need help making that button stay visible even when the person leaves the game.(and in the shop so u cannot buy it again bc u already bought it) Idk how you would do that though.

2 Likes

it’s very easy, just write to the datastore that the player already bought it and make the button not interactive once. then with that datastore data use it to know whenether the player already have bought something or haven’t, and the second the UI loads i suppose you can modify the player’s UI from a serverscript. also dont forget to modify it on the PlayerGui folder, not StarterGui. sorry for the late response.

1 Like

is there anyway you could right out a little of it i literally like i understand that’s whant needs to be done i just literally do not understand anything about data saving and that stuff. if not that is totally ok.

if you dont understand something, ask it here. i’m always here to help you.

1 Like

Using .Changed can some times overflow the saving process you need to save on exit or use DataStore 2 thats just how roblox works thats Issue number 1 use PlayerRemoving to save Money instead of BindToClose and also save FairyDust on exit as well

1 Like

what would i wrtie in the data store to show that the player already bought it and stuff? and like so it stays in there inventory

so is that more effecient? should i do this instead

1 Like

Hi. Sorry for getting back late. I still do not understand what is wrong, as I have made a remote event that when fired does this

game.ReplicatedStorage.Quests.Hundred.OnServerEvent:Connect(function(plr)
	item.MouseButton1Click:Connect(function()
		if player.leaderstats.Money.Value >= value then
			re:FireServer(value)
			item.Text = "Successfully purchased!"
			shoes.Visible = true
			value = 0
		else
			item.Text = "Unsuccessful purchase!"
			task.wait(2)
			item.Text = "Purchase!"
		end
	end)
end)

But when i rejoin the game, the button still ask you to buy the product again.

Your problem here is that you save ONLY currency - that FairyDust, while you forgot about “Buttons” purchased. This results in resetting shop progress, while keeping currency one. To fix this, you need rewrite your system a bit:

local DataStore = game:GetService("DataStoreService")
local ProgressDataStore = datastore:GetDataStore("ProgressSaveSystem") <-- note that I changed name here to logically match a bit.

local RS = game:GetService("ReplicatedStorage")
local PLS = game:GetService("Players")
local Event = storage.RemoteEvent

local DefaultData = {
FairyDust = 0,
ShopItem1 = false,
ShopItem2 = false,
ShopItem3 = false,
-- and so on...
}

Event.OnServerEvent:Connect(function(player, value, ShopItem)
	if player.Leaderstats.FairyDust.Value >= value then
		player.Leaderstats.FairyDust.Value -= value
		player.Leaderstats[ShopItem].Value = true
	-- else do nothing not kick bc there's always possible for fair player to somehow glitch system
	end
end)

PLS.PlayerAdded:connect(function(plr)
	local Folder = Instance.new("Folder") -- never parent any object when it created, parent it only after all necessary properties and childrens are sat.
	Folder.Name = "Leaderstats"
	
	local Data = ProgressDataStore:GetAsync(plr.UserId) or default -- if there's no data, then use Default one
	
	-- there you need way of setting up saving system for Shop. IDK how you structured it, so I'll do most basic way: with respective Values instances.

	for PropName, PropValue in pairs(Data) do
		local ValueType = typeof(PropValue)
		ValueType = string.Upper(string.sub(ValueType, 1, 1)) .. string.sub(ValueType, 2 , -1)
		local Value = Instance.new(PropValue .. "Value"])
		Value.Value = PropValue
		Value.Name = PropName
		Value.Parent = Folder
	end
	Folder.Parent = plr
end)


game:BindToClose(function()
	local Players = (PLS:GetPlayers()
	for a = 1, #Players , 1 do
		local Data = Default
		local FolderData = Players[a].Leaderstats:GetChildren()
		for i = 1, #FolderData, 1 do
			Data[FolderData[i].Name] = FolderData[i].Value
		end
		ProgressDataStore:SetAsync(plr.UserId, Data)
	end
end)

PLS.PlayerRemoving:Connect(function(plr)
	local Data = Default
	local FolderData = plr.Leaderstats:GetChildren()
	for i = 1, #FolderData, 1 do
		Data[FolderData[i].Name] = FolderData[i].Value
	end
	ProgressDataStore:SetAsync(plr.UserId, Data)
end)

game.ReplicatedStorage.Quests.Hundred.OnServerEvent:Connect(function(plr)
	plr.leaderstats.FairyDust.Value = plr.leaderstats.FairyDust.Value +100
end)

--[==[
NOTE:
While this system saves what you buy, you need implement yourself
another system for your shop, which will check every item bought
and depending on that, it will turn button to already purchased
and that buttons, which not purchased yet.

! Also, this script may contain some small bugs and flaws !
--]==]

Also, you need remember that you should save data only when player leaves (or with 5 mins autosave to handle unexpected situations). Also, not use Instance.new(<Object>, Parent), and use Instance.new(<Object>), bc it’s slower than setting parent after everything other set.