Issue with DataStore for a Shop / Loadout

Hello, (Sorry for the Duplicate post),

I am trying to make a Shop Gui with DataStoreService, But i have some Issues which are Listed below:
My Issues:

  • When I buy the Items, It doesnt Save

  • Buying an Item buys all the Items


Server Script (My Main Issue):
local DataStore = game:GetService("DataStoreService"):GetDataStore("Items")
local Players = game:GetService("Players")
local ReplicatedStorage = game.ReplicatedStorage

Players.PlayerAdded:Connect(function(Player)
	for _,Item in pairs(Player.PlayerGui:WaitForChild("LoadoutGui").Loadout.ScrollingFrame:GetChildren()) do
		
		if Item:IsA("TextButton") then
			DataStore:GetAsync(Player.UserId..": Store", Item["Purchased"].Value)
			
		Item["Purchased"]:GetPropertyChangedSignal("Value"):Connect(function()
				DataStore:SetAsync(Player.UserId..": Store", Item["Purchased"].Value)
				print("Changed")
			end)
			end
	end
	ReplicatedStorage.ShopEvent.OnServerEvent:Connect(function()
	for i, Button in pairs(Player.PlayerGui.LoadoutGui.Loadout.ScrollingFrame:GetChildren()) do
				if Button:IsA("TextButton") then
		if Player:WaitForChild("SavedStats").Credits.Value >= Button["Price"].Value then
					Player:WaitForChild("SavedStats").Credits.Value -= Button["Price"].Value
					Button["Purchased"].Value = true
					Button.TextLabel.Text = "Purchased"
else
warn("Insufficent Funds")
		end
		end
	end
end)
	
end)

Purchased is a BoolValue, used to determine if the item has been bought of not

Edit:

I have noticed my error with the Gui, i was using for i in pairs loop on all the Buttons

I appreciate any assistance,
Thanks.

There’s not much information to look at to help you, but I assume Datastores are not working as expected. Everytime I see a Datastore related problem, I suggest switching to Datastore2 or ProfileService. That’s all I can say at the moment.

1 Like

@bluebxrrybot Thanks,
But I rather not use ModuleScripts, i perfer DataStoreService as i dont experience Data Loss with them, but i am trying to save a BoolValue to determine if the Item is purchased or not. Along with a RemoteEvent.

Im Just asking on how to fix the issues I have with storing Data. Do I use a table, Do i use a StringValue?

Of course, using a table would be better because it allows you to store multiple values. Strings also allow you to store tables, but they need to be converted into strings using HttpService when saving, though you would still need to translate them to a table when getting the data. Table does the same thing, but you don’t need to encode and decode the data.

Seeing your code all messed up, not many people would read and understand the code; since the title is related to DataStores, others would just recommend using modules, which clearly you are not ready for since you don’t even know how to fix your own issue. Why they only recommend using modules is because they are reliable, which helps developers avoid player data loss.

Also, why are you creating an event listener for value inside the player’s GUI? The event would only get fired when the server was responsible for doing so. This probably the reason why the players data is not getting saved. You need a remote event.

And why are you saving the player’s data every time the event gets fired, which is unlikely to get fired unless the server changes the value? You need to only save the player’s data when leaving the game, or every five minutes, with the “UpdateAsync” method. Example:

Responsible for getting and saving player data
local KEY_IDENTIFIER = "Player_"
local MAX_ATTEMPT = 10

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

local Datastore = DataStoreService:GetDataStore("PlayersData")
local PLAYERSDATA_PATH = ReplicatedStorage.PlayersData

-- This module does not contain anything
local PlayersData = require(PLAYERSDATA_PATH)

-- This is why open source services are reliable
-- We don't need to do this unnecessary function
local function GetPlayerData(player: Player, attempt: number?): (boolean, PlayersData.Data?)
	attempt = attempt or 0

	local success, data = pcall(function()
		return Datastore:GetAsync(KEY_IDENTIFIER .. player.UserId)
	end)

	if not success and attempt < MAX_ATTEMPT then
		task.wait(5)
		return GetPlayerData(player, attempt + 1)
	end

	return success, data
end

Players.PlayerAdded:Connect(function(player)
	local success, data = GetPlayerData(player)

	if not success then
		player:Kick("Something wen't wrong trying to retrieving data")
	end

	-- We are initilizing the players data for new players
	if data == nil then
		data = {
			Items = {},
			Money = 0,
		}
	end

	PlayersData[player] = data
end)

Players.PlayerRemoving:Connect(function(player)
	local data: PlayersData.Data = PlayersData[player]

	if data then
		Datastore:UpdateAsync(KEY_IDENTIFIER .. player.UserId, function(pastData)
			-- this function just allows you to check if the data should be updated
			-- you can do whatever you want from here
			-- if your not going to do anything from here just use SetAsync instead.
			return data
		end)
	else
		-- This is unlikely to happen
		warn(player.Name .. "_" .. player.UserId, "was not able to be added to PlayersData")
	end
end)
PlayersData module

Like what I said from above this module does not contain anything other than a few stuffs, this just allows for other script to access the player data. Unless you want to do some advance scripting such as metatables which allows you to do a lot like adding event listeners for other script to listen when the data was changed.

export type Data = {
	Items: { string },
	Money: number,
}

return {}
When the player bought something from the shop
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local PLAYERSDATA_PATH = ReplicatedStorage.PlayersData

local RemoteFunctionBuy: RemoteFunction = ReplicatedStorage.RemoteFunctionBuy
local Items: Folder = ReplicatedStorage.Items

local PlayersData = require(PLAYERSDATA_PATH)

RemoteFunctionBuy.OnClientInvoke = function(player, itemName: string): (boolean, string?)
	local data: PlayersData.Data = PlayersData[player]
	local item = Items:FindFirstChild(itemName)

	if not item then
		return false, "Invalid item"
	end

	local price: number = item:GetAttribute("Price")

	if data.Money < price then
		return false, "You need " .. price - data.Money .. " more!"
	end

	item:Clone().Parent = player.Backpack
	data.Money -= price

	return true
end

Thanks, but this is a pretty complex example, i kind of just woke up to this in front of me, plus some parts of it are not needed for it to work the same.


To Answer These:

  • Thats kind of all I needed to know

  • Looking at my script, yes, it is crap, (This however was my first attempt at actually trying to save items), but downright saying im “not ready”? Kinda sad.
    I made a better version right after, but needed help saving the Items, i perfer just regular DataStoreService as i do not experience Data loss.

  • The only RemoteEvent im using is to check Players cash and buy the item if not purchased

  • Forgot to do that, that was my mistake, I thought it would just save when an item was bought and then the Data would just save

I Aint the Smartest scripter, nor the dumbest, i make a lot of bad decisions, code, etc, just becauae i have trouble fixing my own issue doesnt mean im stupid, i can always ask for help.

1 Like

I see the main problems here. For one, you never save/get the data (I assume you haven’t started that part yet), and two, you run the OnServerEvent inside the PlayerAdded function, which would overlap the effects. Lastly, you aren’t using the player that fired the event, so the event would buy the item for every player TIMES the amount of players each time.

local DataStore = game:GetService("DataStoreService"):GetDataStore("Items")
local Players = game:GetService("Players")
local ReplicatedStorage = game.ReplicatedStorage
local playerdata = {}

Players.PlayerAdded:Connect(function(plr)
	playerdata[plr.UserId] = DataStore:GetAsync(plr.UserId) or {}
end)

game.Players.PlayerRemoving:Connect(function(plr)
	DataStore:SetAsync(plr.UserId, playerdata[plr.UserId])
	playerdata[plr.UserId] = nil
end)

game:BindToClose(function()
	for i, plr in pairs(game.Players:GetPlayers()) do
		DataStore:SetAsync(plr.UserId, playerdata[plr.UserId])
		playerdata[plr.UserId] = nil
	end
end)

local itemdata = {} --[[all item prices ex:
local itemdata = {
	itemname = 100
}
--]]

ReplicatedStorage.ShopEvent.OnServerEvent:Connect(function(plr, item)
	--have them send the item to buy
	if not table.find(playerdata[plr.UserId], item) then
		local price = itemdata[item]
		
		if price and plr.SavedData.Credits.Value >= price then
			plr.SavedData.Credits.Value -= price
			table.insert(playerdata[plr.UserId], item)
		else
			warn("Price not found or insufficient funds.")
		end
	else
		warn("already owned")
	end
end)

I Have. I remade the script after learning how to save Tables. But the Issue is that it wont retrieve the data.
(Same Issue i have with this script, but this script is literal trash)
This Script, not yours.


I Have noticed, i made changes to it.


Yes, I may or may not have used the PlayerAdded Variable and not the OnServerEvent Variable, my bad.


Thank you for the feedback, Truly appreciated.

My script stores the data inside of a table within the script. To know whether the data has saved/been retrieved, you will probably need to add some prints.

local DataStore = game:GetService("DataStoreService"):GetDataStore("Items")
local Players = game:GetService("Players")
local ReplicatedStorage = game.ReplicatedStorage
local playerdata = {}

Players.PlayerAdded:Connect(function(plr)
	playerdata[plr.UserId] = DataStore:GetAsync(plr.UserId) or {}
	print("retrieved "..plr.Name, playerdata[plr.UserId])
end)

game.Players.PlayerRemoving:Connect(function(plr)
	DataStore:SetAsync(plr.UserId, playerdata[plr.UserId])
	print("saved "..plr.Name, playerdata[plr.UserId])
	playerdata[plr.UserId] = nil
end)

game:BindToClose(function()
	for i, plr in pairs(game.Players:GetPlayers()) do
		DataStore:SetAsync(plr.UserId, playerdata[plr.UserId])
		print("saved "..plr.Name, playerdata[plr.UserId])
		playerdata[plr.UserId] = nil
	end
end)

local itemdata = {} --[[all item prices ex:
local itemdata = {
	itemname = 100
}
--]]

ReplicatedStorage.ShopEvent.OnServerEvent:Connect(function(plr, item)
	--have them send the item to buy
	if not table.find(playerdata[plr.UserId], item) then
		local price = itemdata[item]
		
		if price and plr.SavedData.Credits.Value >= price then
			plr.SavedData.Credits.Value -= price
			table.insert(playerdata[plr.UserId], item)
		else
			warn("Price not found or insufficient funds.")
		end
	else
		warn("already owned")
	end
end)

also calling it complete trash is just a bit rude ngl.

@Kaid3n22 Im not calling your script trash, im talking about mine. sorry if i wasnt clear.

@Kaid3n22
I have already tried this, but it doesnt save anything even after trying.

Edit: I fixed your script by:

Players.PlayerAdded:Connect(function(plr)
	playerdata[plr.UserId] = DataStore:GetAsync(plr.UserId, playerdata[plr.UserId]) or {}
	print("retrieved "..plr.Name, playerdata[plr.UserId])
end)

Thanks but i have one question: When i rejoin it doesnt prevent me from buying it again, how do i fix this?

Edit: Nvm, i fixed it by simply adding tosting(item)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.