Skin Data Saving

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    A data saver to save every skin a player owns.
  2. What is the issue? Include screenshots / videos if possible!
    I just cannot fully comprehend how I will pull it off since the code is all tangled.
  3. What solutions have you tried so far? Did you look for solutions on the Creator Hub?
    Roblox Documentation
    After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!
local ds=game:GetService("DataStoreService")
for i,v in pairs(script.Parent.BartSkins:GetChildren()) do
	local ab=script.Parent.HomerSkins:GetChildren()
	table.insert(ab,v)
	for i,v in pairs(ab) do
		if v.Name=="TextButton" then
			if v.Text~="Buy" then
				local skins={}
				for i,v in pairs(v.ViewportFrame:GetChildren()) do
					if v:IsA("MeshPart") then
						local skin=string.lower(v.Name)
						if skin=="grampa" then
							skin="abe"
						end
						table.insert(skins,skin)
						local nds=ds:GetDataStore("PlayerSkins")
						for i,val in pairs(skins) do
							for i,v in pairs(game.Players:GetPlayers()) do
								local success, errorMessage = pcall(function()
									nds:SetAsync(v.UserId,val)
								end)
								if not success then
									print(errorMessage)
								end
							end
						end
					end
				end
			end
		end
	end
end

Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

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.

This is a ServerScript by the way. But I’ll try. However, I need another player. Could you join my group as a Developer to see if it works? I already know every skin id to put in.

You shouldn’t be reading from UI on the server, or modifying it from the server.

UI Logic should remain on the client, the server just needs to validate things, for example, how much cash a player has when requesting to buy something, and also checking if the player already owns something if that something is meant to be an one-time-purchase.

^^

You don’t need 2 players for datastores, and if you really want to verify that skin ownership data doesn’t overlap across different clients, studio has an in-built mode where you can test on multiple clients with different UserIds.
In the top-left, click the “Test” dropdown, then select “Server and Clients”, and then up the client count to 2.


image

Oh thank you! I never knew of that feature. I’ll try it out riiight now. Problem is, there is a much bigger problem. Skin purchasing doesn’t use Events it uses 2 StringValues stored inside of a Folder stored inside of the Character model. And I really don’t want to reform all of the shop system since then ill have to manually edit like.. 25 textbuttons for it to work. And i would like to say that it is a You vs Homer game, so theres Bart and Homer skins aka 2 separate StringValues.

Idk, just how I make games, I prefer using Values to transmit data over Events primarily.

The client has to communicate to the server in one way or another. Again, UI input should be handled by the clients, and the clients should then request to do XYZ from the server.

If you’ve managed to somehow get the server to read client input, I’ll be damned.

Either way, you can just re-use the function and change its implementation. What I provided is just a cookie-cutter example. You don’t have to use it exactly how I wrote it, and really I wouldn’t. I’d add to it if I could be bothered.

I don’t think that’s the best idea considering the character gets removed before the player (I think?), and that the character is completely reset when they die or when the character is replaced using LoadCharacter, meaning that anything added to them, such as data, is deleted.

I’m stumped. No idea how you’ve managed to do this, or why even. You can transmit more usable data across events, and have client > server as well as server > client instead of just the latter.

I don’t want to tell you you’re doing things wrong, but you are 100% doing things wrong.

I really do want to keep trying to help you, but at this point I’m so confused by your systems that I don’t even know what to do at all anymore.

I’m just going to say that the way you’ve built things really is not good at all, personally I’d write a local script that creates frames for each skin at runtime, and then use remote functions to process purchase and equip requests.

My game is so unbelievably disorganized beyond belief the only alternative I can think of is giving you a .rbxl, I’m not lying, my code could be possessed by how structurally bad it is. Im sorry man. I dont wanna flood the thread, its my fault for not knowing things well.