Yeah nah this is a full rewrite scenario.
Problem 1: This appears to be a local script. You cannot use datastores in local scripts. If not, then you’re attempting to access information the server does not have (the text content of a button that the client is (hopefully) changing itself).
Problem 2: You’re using a single datastore to try and save all skins, however you constantly overwrite it to only save one skin.
Problem 3: Your skins are saved by name and not ID. Not a massive problem, but it’s a lot easier to work with IDs when you may be renaming skins.
Problem 4: This script only runs once and tries to save data that doesn’t exist for players that also do not exist.
Here’s a little example on how I’d structure the saving, at least.
--// Just a heads up that this isn't a complete system and I'd recommend just using it for reference.
--// This is also a server script, not a local script.
--// The client should handle inputs and UI, the server should handle logic.
--// Also, due to me writing this in the forums and not in studio, there may be typos, and I couldn't be bothered writing more complex logic.
--// With this, you can just change the names and not have to write additional logic to fix players data. It also uses less bytes to save.
--// The rest of the system is also written in such a way that you can simply continue using names elsewhere in code, the IDs exist purely for saving.
--// Again, better system if you're going to be renaming things, otherwise you probably could just get away with using the names directly.
local skinIds = {
["SkinName"] = 1,
["SkinName"] = 2,
["SkinName"] = 3,
}
--// Probably a better way to do this, but from memory, table.find does not work with string indexing.
local function getNameFromId(id: number): string?
for skinName, skinId in skinIds do
if id == skinId then
return skinName
end
end
end
local function getOwnedSkins(player: Player)
if not player:FindFirstChild("skinInventory") then return end
local skins = {}
for skinName, skinId in skinIds do
if not player.SkinInventory:FindFirstChild(skinName) then continue end
skins[tostring(skinId)] = true
end
return skins
end
local function saveSkins(player: Player)
skinDatastore:UpdateAsync(player.UserId, function()
return getOwnedSkins(player)
end)
end
local function addSkin(player: Player, skinName: string)
if not player:FindFirstChild("skinInventory") then return end
local skin = Instance.new("BoolValue")
skin.Name = skinName
skin.Parent = player.skinInventory
end
local function loadSkins(player: Player)
local skins = skinDatastore:GetAsync(player.UserId)
local folder = Instance.new("Folder")
folder.Name = "skinInventory"
folder.Parent = player
for id, _ in skins do
addSkin(player, getNameFromId(tonumber(id)))
end
end
game.Players.PlayerAdded:Connect(loadSkins)
game.Players.PlayerRemoving:Connect(saveSkins)
--// Example skin purchasing
local function onSkinPurchaseRequest(player: Player, skinName: string)
--// 500 is a stand-in for skin cost
if player.Cash.Value < 500 then return end
player.Cash.Value -= 500
addSkin(player, skinName)
end
game.ReplicatedStorage.PurchaseSkin.OnServerEvent:Connect(onSkinPurchaseRequest)
Things like UI and inputs should be handled by the clients, and you should use RemoteEvents/RemoteFunctions to prompt skin purchasing.
I’d personally use RemoteFunctions, as it makes the client responding to the server a little easier, for example, showing an error message is purchasing fails, or playing an animation and/or updating text when it succeeds.