Saving items to an inventory

Hey there,
I’m making a Skin Shop where you can buy skins and they save to your inventory, I’m just having trouble finding a way to save if a player owns the skin and then cloning a template frame and updating the frame to match the skin. Any help or ideas would be very apricated.
Thanks :slight_smile:

1 Like

you can insert a string value inside the player have the id of player skins but from the server side so the players doesn’t cheat
then later you can use InsertService to load the skin and give it to the player
It’s a little complicated

1 Like

How will I check if the player has the string value?

do you want to save the skins in the datastore or only in the running game, in other word if the players leave the game and enter it again, they will find their skins or not?

1 Like

Yeah in a datastore. So they can buy skins and keep them.

1 Like

So first you need to get the data of player and put it inside string value:

local DSS = game:GetService("DataStoreService")
local GameData = DSS:GetDataStore("Mygamedata")

game.Players.PlayerAdded:Connect(function(Plr)
	local PlayerData = GameData:GetAsync(Plr.UserId)
	local Value = Instance.new("StringValue",Plr)
	Value.Name = "SkinsData"
	if PlayerData ~= nil and PlayerData ~= "" then
		Value.Value = PlayerData
	end
end)

now lets say the player buy a skin that will fire the server then the server will check the player money and if its more than the skin cost he will insert the id of the skin in the player skins value and take the money from the player:
Importent: if you dont know how to use DataStoreService read this.
So you need to do:

--Client
local Remote = game.ReplicatedStorage.BuySkins
Remote:FireServer(--[[Any Skin Id Exemple]]158471369)

--Server
local Remote = game.ReplicatedStorage.BuySkins
local Costs = {
	["158471369"] = 100,
	["158454069"] = 200,
	["154564510"] = 320,
--For exemple
}
local Http = game:GetService("HttpService")
local Insert = game:GetService("InsertService")
Remote.OnServerEvent:Connect(function(Plr,id)
	if Costs[tostring(id)] ~= nil then
		local Money = Plr.Money.Value
		if Costs[tostring(id)]<Money then
			Plr.Money.Value = Plr.Money.Value-Costs[tostring(id)]
			local SkinData = Plr.SkinData
			if SkinData == "" then
				SkinData = Http:JSONEncode({id})
			else
				local Data = Http:JSONDecode(SkinData.Value)
				table.insert(Data,id)
				Data = Http:JSONEncode(Data)
				SkinData.Value = Data
			end	
		end
	end
end)

Note: If you dont know how to use HttpService to convert Table to Srting read this and this.
Now lets go to the inventory so when the skins value changed the script will refresh the skins in the inventory and clone a new buttons of skins with their names:

local Skins = script.Parent.SkinsButtons --the skins buttons in the inventory
local Http = game:GetService("HttpService")
local Value = game.Players.LocalPlayer:WaitForChild("SkinData")
local MPS = game:GetService("MarketplaceService")



Value:GetPropertyChangedSignal("Value"):Connect(function()
	for _,Button in pairs(Skins:GetChildren()) do
		if Button:IsA("TextButton") and Button.Name ~= "Template" then
			Button:Destroy()
		end
	end
	local Data = Http:JSONDecode(game.Players.LocalPlayer.SkinData.Value)
	for _,skinId in pairs(Data) do
		local SkinName = MPS:GetProductInfo(skinId).AssetType
		local Clone = Skins.Template:Clone()
		Clone.Parent = Skins
		Clone.Name = SkinName
		Clone.Text = SkinName
		Clone:SetAttribute("SkinId",skinId) -- you will need it later
		Clone.MouseButton1Down:Connect(function()
			--Remote:FireServer(Clone:GetAttribute(SkinId))
		end)
	end
end)

you will need to use insert service in the server to insert the skin.
if you dont know what is InsertService read this.

I told you is It’s a little complicated hopefully you will understand this system. Good Luck

1 Like

Wow, ill make sure to read through all that but for the skin ids can they be any number?

What is the skin exactly? Shirt ,pants or what?

1 Like

Are your skins groups of Accessories, Shirts, and Pants?
Do they live inside your game, or have you uploaded them to the website?

You can use an ID system to keep track of the skins, or you can use the skin’s unique name. If you choose to use IDs you can have multiple skins with the same name, but I usually opt to use the skin’s name as a key in my shops most of the time, because I know my shop will never contain two skins of the same name.

Here is a suggestion for how to structure your skin system.
Well, it’s a bit more of a guide than a suggestion

-- how to store the skin data, should be kept in a module or some globally accessible directory
-- server script:
local skins = {
    ["Robot"] = {
        pants_id = "1234567" -- content id for pants
        shirt_id = "1234567" -- content id for the shirt
        accessories = { -- an array of all content ids for relevant accessories
            12345,
            12345,
            12345, 
        },
        cost = 500, -- cost for the skin, subtract from wherever you store their currency
    },

    ["Army Soldier"] = 
        pants_id = "1234567"
        shirt_id = "1234567"
        accessories = {
            12345,
            12345,
            12345,
        },
    cost = 1000,
    },

-- etc
}
-- code to generate the skins, it should be able to access the skin data table above
-- server script
local InsertService = game:GetService("InsertService")

local function ClearSkin(character)
  for _,object in next,character:GetChildren() do
        local typesToRemove = {"Accoutrement","Accessory","Hat","Shirt","T-Shirt","Pants"}
        if table.find(typesToRemove,object.Class) then
            object:Destroy()
        end
    end
end

local function GenerateSkin(character,skinName)
    local skinData = skins[skinName]
    ClearSkin(character)

    local function GenerateShirt()
        local shirt = Instance.new("Shirt")
        shirt.ShirtTemplate = skinData.shirt
        shirt.Parent = character
    end

    local function GeneratePants()
        pants.PantsTemplate = skinData.pants
        pants.Parent = character
    end

    local function GenerateAccessories()
        for _,assetId in next,skinData.accessories do
            local success, model = pcall(InsertService.LoadAsset, InsertService, assetId)
            if success and model then
              accessory = model:GetChildren()[1]
              accessory.Parent = character
            else     
                -- this accessory could not be loaded for some reason
            end
    end

    GenerateShirt();GeneratePants();GenerateAccessories()
end

-- this server code will give players a skin when they join
game.Players.PlayerAdded:connect(function(player)
    player.CharacterAdded:connect(function(character)
        GenerateSkin(character)
    end)
end)
-- this server code controls saving/loading/making comparisons to a player's table of owned skins, it should be able to call the functions in the server code above
local DS = game:GetService("DataStoreService")
local SkinsData = DS:GetDataStore("SkinsData")

local sessionSkinsData = {} -- this table holds

local function LoadPlayerSkinsData(player)
  local playerSkinsData = SkinsData:GetAsync(player.UserId) -- you should pcall this but I am pressed for time writing, so please make your own optimizations :)
sessionSkinsData[player] = playerSkinsData
end

game.Players.PlayerAdded:connect(LoadPlayerSkinsData)

local function SavePlayerSkinsData(player)
    local playerSkinsData = sessionSkinsData[player]
    if playerSkinsData then
        SkinsData:SetAsync(player.UserId,playerSkinsData)
    end
    if not player:IsDescendantOf(game.Players) then -- they have left the game
       sessionSkinsData[player] = nil -- clear the key
    end
end

game:BindToClose(function() -- save when the game closes
    for _,player in next,game.Players:GetPlayers() do
        SavePlayerSkinsData(player)
    end
end)

game.Players.PlayerRemoving:connect(SavePlayerSkinsData)

local SkinEquip = Instance.new("RemoteEvent")
skinEquip.Name = "SkinEquip"
skinEquip.Parent = game.ReplicatedStorage
skinEquip.OnServerEvent:connect(function(player,skinName)
    local character = player.Character
        GenerateSkin(character,skinName)
    end
end)

local SkinPurchase = Instance.new("RemoteFunction")
skinPurchase.Name = "SkinPurchase"
skinPurchase.Parent = game.ReplicatedStorage
skinPurchase.OnServerInvoke = function(player,skinName)
    local skinData = skins[skinName]
  
   local ownsSkin = playerSkinsData[skinName]
   local playerCurrencyPointer -- define this, set to the amount of currency they have
   local canAfford = skinData.cost <= playerCurrency

   if not (ownsSkin or canAfford) then
       playerCurrencyPointer -= skinData.cost -- subtract the player's currency here, make sure you define this correctly!
       
       SkinsData:UpdateAsync(player.UserId,function()
            local newData =  sessionSkinsData[player]
            newData[skinName] = true
            return newData
       end)
   else
    return nil -- maybe throw an error message in the return so you know what to tell the client (i.e. cannot afford, already owns, etc)
end

-- this local code in conjunction with the Remotes above will allow players to change their skins or buy new ones.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local skinPurchase = ReplicatedStorage:WaitForChild("SkinPurchase")
local skinEquip = ReplicatedStorage:WaitForChild("SkinEquip")

-- example purchase call
local result = skinPurchase:InvokeServer("exampleSkinName")
if result, errorMessage == true then
    -- we bought the skin successfully, update UI
    else
    -- we could not buy the skin
end

-- example equip call
skinEquip:FireServer("skinName")

As for visualizing skins in the shop, I recommend creating another event that returns information about whether or not a player owns a skin, and then displaying a different type of UI for equipping the skin if they own it, or purchasing it if they don’t, using the sessionSkinData table to avoid querying DataStores for that info.

This is just a general structure for how to equip skins, please pardon any errors, I only briefly reviewed the code. The biggest structural shortcoming of this system is ensuring that data saves and loads properly. I would highly recommend wrapping those calls inside of a pcall function where they are not already.

This guide is not completely comprehensive, as it relies on you to fetch the player’s currency information and update it correctly.

I would recommend using ProfileService by loleris to avoid all of these shortcomings, but again, I just wanted to show you what a general skin purchasing/equipping infrastructure should look like.

4 Likes

a whole new avatar like shirts, pants, hats, hairs

Bundles? or just the clothes
if the skins in your game you can clone it and give it to the players, if not you can insert them from their id’s to your game using InsertService