Making a gifting system (tutorial)

Before I start, I want to say that this is my first tutorial and I’m not really good at introducing myself and my topic

Skip to the end for full script.
Hello developers of Roblox. So I was browsing through the forum, and I saw someone wanted to make a gifting system. So that gave me the idea to create tutorial on how to make one.
First, you want to make a DataStore, to save the player’s stuff.

local DataStoreService = game:GetService('DataStoreService')
local CoinsDataStore = DataStoreService:GetDataStore('CoinsDataStore')

Now I have the DataStores set up. Next we have to make some type of currency to be given.

game.Players.PlayerAdded:Connect(function(player)
    local leaderstats = Instance.new('Folder', player)
    leaderstats.Name = 'leaderstats' -- make sure to always name it 'leaderstats'
    
    local Coins = Instance.new('IntValue', leaderstats)
    Coins.Name = 'Coins'
    player.CharacterAdded:Connect(function(char)
        -- this part isn't needed for this, i just like to add it
    end)
end)

Now we have the leaderstats made. Now we have to attempt to load it.

local CoinsData
local success, errormessage = pcall(function()
    CoinsData = CoinsDataStore:GetAsync('Coins_'..player.UserId)
end)

Make sure to always set the data to the player’s UserId, because the data will not save if the player changes their name.
Now we can set it’s coins to the data we get from the GetAsync function

if CoinsData and success then
    player.leaderstats.Coins.Value = CoinsData
else -- if it is returning nil
    warn(errormessage)
end

Alright, now we have the main part set up.
The script will look like this:

game.Players.PlayerAdded:Connect(function(player)
    local leaderstats = Instance.new('Folder', player)
    leaderstats.Name = 'leaderstats' -- make sure to always name it 'leaderstats'
    
    local Coins = Instance.new('IntValue', leaderstats)
    Coins.Name = 'Coins'
    player.CharacterAdded:Connect(function(char)
        -- this part isn't needed for this, i just like to add it
    end)
    local CoinsData
    local success, errormessage = pcall(function()
        CoinsData = CoinsDataStore:GetAsync('Coins_'..player.UserId)
    end)

    if CoinsData and success then
        player.leaderstats.Coins.Value = CoinsData
    else -- if it is returning nil
        warn(errormessage)
    end
end)

Now we will save the data when the player leaves

game.PlayersRemoving:Connect(function(player)
    local Coins = player.leaderstats.Coins
    local DataSaving
    local success, errormessage = pcall(function()
        CoinsDataStore:SetAsync('Coins_'player.UserId, Coins)
    end)
end)

And lastly, make sure it saves the data (shoutout to @Jackscarlett because I learned this from him).

game:BindToClose(function()
    for i, v in pairs(game.Players:GetChildren()) do
        CoinsDataStore:SetAsync('Coins_'v.UserId, v.leaderstats.Coins)
    end
end)

Ok well that was the first part of this script.
Will look like this:

game.Players.PlayerAdded:Connect(function(player)
    local leaderstats = Instance.new('Folder', player)
    leaderstats.Name = 'leaderstats' -- make sure to always name it 'leaderstats'
    
    local Coins = Instance.new('IntValue', leaderstats)
    Coins.Name = 'Coins'
    player.CharacterAdded:Connect(function(char)
        -- this part isn't needed for this, i just like to add it
    end)
    local CoinsData
    local success, errormessage = pcall(function()
        CoinsData = CoinsDataStore:GetAsync('Coins_'..player.UserId)
    end)

    if CoinsData and success then
        player.leaderstats.Coins.Value = CoinsData
    else -- if it is returning nil
        warn(errormessage)
    end
end)

game.PlayersRemoving:Connect(function(player)
    local Coins = player.leaderstats.Coins
    local DataSaving
    local success, errormessage = pcall(function()
        CoinsDataStore:SetAsync('Coins_'player.UserId, Coins)
    end)
end)

game:BindToClose(function()
    for i, v in pairs(game.Players:GetChildren()) do
        CoinsDataStore:SetAsync('Coins_'v.UserId, v.leaderstats.Coins)
    end
end)

Now for part 2: Gifting.
For this, I will use 2 text boxes, one for the amount, and one for the player

Make a remote event, call it Gift, place it in Replicated storage.

local amountTextBox =  -- define the text box that you want as the amount
local chosenPlayer =  -- define this too
local amount
local PlayerId
amountTextBox.FocusLost:Connect(function(entered)
    if entered then
        amount = tonumber(amountTextBox.Text)
            if amount and PlayerId then
                game.ReplicatedStorage.Gift:FireServer(amount, PlayerId)
            end
    end
end)
chosenPlayer.FocusLost:Connect(function(entered)
    if entered then
        if game.Players:GetUserIdFromNameAsync(chosenPlayer.Text) then
            PlayerId = game.Players:GetUserIdFromNameAsync(chosenPlayer.Text)
            if amount and PlayerId then
                game.ReplicatedStorage.Gift:FireServer(amount, PlayerId)
            end
        end
    end
end)

There we made it so that it will tell the client to update the chosen player’s Coins. One box will have the amount, and one will have the Player’s Id.

Now we can make a function for the server to tell when the event was fired.

game.ReplicatedStorage.Gift.OnServerEvent:Connect(function(player, amount, Id)
local coins
    local success, err = pcall(function()
        coins = CoinsDataStore:GetAsync('Coins_'..Id)
    end)
    if success then
    local success2, err2 = pcall(function()
        CoinsDataStore:SetAsync('Coins_'..Id, coins + amount)
    end)
    if success2 then; print('Saved'); end
    else
        warn('error')
end)

(If I made some errors, please correct me, I couldn’t test it.)
Well, that should be it. This is the full script:
Server script:

game.Players.PlayerAdded:Connect(function(player)
    local leaderstats = Instance.new('Folder', player)
    leaderstats.Name = 'leaderstats' -- make sure to always name it 'leaderstats'
    
    local Coins = Instance.new('IntValue', leaderstats)
    Coins.Name = 'Coins'
    player.CharacterAdded:Connect(function(char)
        -- this part isn't needed for this, i just like to add it
    end)
    local CoinsData
    local success, errormessage = pcall(function()
        CoinsData = CoinsDataStore:GetAsync('Coins_'..player.UserId)
    end)

    if CoinsData and success then
        player.leaderstats.Coins.Value = CoinsData
    else -- if it is returning nil
        warn(errormessage)
    end
end)

game.PlayersRemoving:Connect(function(player)
    local Coins = player.leaderstats.Coins
    local DataSaving
    local success, errormessage = pcall(function()
        CoinsDataStore:SetAsync('Coins_'player.UserId, Coins)
    end)
end)

game:BindToClose(function()
    for i, v in pairs(game.Players:GetChildren()) do
        CoinsDataStore:SetAsync('Coins_'v.UserId, v.leaderstats.Coins)
    end
end)

game.ReplicatedStorage.Gift.OnServerEvent:Connect(function(player, amount, Id)
local coins
    local success, err = pcall(function()
        coins = CoinsDataStore:GetAsync('Coins_'..Id)
    end)
    if success then
    local success2, err2 = pcall(function()
        CoinsDataStore:SetAsync('Coins_'..Id, coins + amount)
    end)
    if success2 then; print('Saved'); end
    else
        warn('error')
end)

Client (local script):

local amountTextBox =  -- define the text box that you want as the amount
local chosenPlayer =  -- define this too
local amount
local PlayerId
amountTextBox.FocusLost:Connect(function(entered)
    if entered then
        amount = tonumber(amountTextBox.Text)
            if amount and PlayerId then
                game.ReplicatedStorage.Gift:FireServer(amount, PlayerId)
            end
    end
end)
chosenPlayer.FocusLost:Connect(function(entered)
    if entered then
        if game.Players:GetUserIdFromNameAsync(chosenPlayer.Text) then
            PlayerId = game.Players:GetUserIdFromNameAsync(chosenPlayer.Text)
            if amount and PlayerId then
                game.ReplicatedStorage.Gift:FireServer(amount, PlayerId)
            end
        end
    end
end)
19 Likes

I forgot to reply back to this earlier but here I am now I suppose

Seems like a legit tutorial, although at your full code there are a couple of things that I do see that you could change better:

Reference - Server script

Do keep in mind that the first parameter of OnServerEvent is the player that fired it, so you could just reference the Player that way instead

  • Since Player is the first argument, the amount variable would be overtaking it & you’d be saving a Player Instance rather than the actual amount that’s passed by the client

You can encase your PlayerAdded/PlayerRemoving events in local functions, which you would then connect to fire the BindToClose function

  • Example:
local function LoadData()
 --Do stuff
end

local function SaveData()
    --Do stuff
end

game.Players.PlayerAdded:Connect(LoadData)
game.Players.PlayerRemoving:Connect(SaveData)

game:BindToClose(function()
     for _, Player in pairs(game.Players:GetPlayers()) do
        SaveData(Player) --We're looping through all players, and calling the function to save every player's data that way
    end
end)
  • Also side note, but you should make sure that the data does save using a pcall in your BindToClose function

Reference - Client (local script)

Although this would work, I’d personally have a TextButton that will confirm the action of the gifting since it’s more easier & efficient to do that way rather than detecting 2 Events to keep checking if there’s a valid amount & PlayerId variable

  • It’s a bit more organized to do this:
local amountTextBox --= Another something
local chosenPlayer --= Something
local amount 
local PlayerId

ConfirmButton.MouseButton1Down:Connect(function()
    amount = tonumber(amountTextBox.Text)
    PlayerId = game.Players:GetUserIdFromNameAsync(chosenPlayer.Text)

    if amount and PlayerId then
        game.ReplicatedStorage.Gift:FireServer(amount, PlayerId)
    end
end)

Then rather just listening for 2 events, which can clutter a lot of space in your script

Overall though, it’s not a bad tutorial!

3 Likes

Just wondering how can i change a value in a datastore that has multiple values?? Im unsure on how to select the coins value and not the others?

image

I’ll assume that the data returned from GetAsync() is a dictionary right?

Yes it is! Im not sure how to set only the value in the dictionary tho

CoinsData['Coins'] -- coins
CoinsData['groupRewardCollected'] -- groupRewardCollected

Wait so what do i do with those?? I dont really understnad? Do i put theem inside the set async or do i make a new variable?

CoinsDataStore:SetAsync(plr.UserId, {['Coins'] = int; ['groupRewardCollected'] = bool})

You just want to save the dictionary, so when you use GetAsync, it returns the dictionary.

1 Like

Tutorials are usually complicated, and take real-effort. You should try (practing?) some more and make a better, more elaborate tutorial, using the features that discourse gives (Being able to make sub-titles, categories, etc), which would make your topic, more “professional”.

I’m not going full-rage mode due to this being your “first tutorial”.

Hmmmm this still doesnt seem to work??

game.ReplicatedStorage.Gift.OnServerEvent:Connect(function(player,amount, Id)
		local coins
		local success, err = pcall(function()
			coins = DataStore:GetAsync("plr-"..Id)
		end)
		local coinsval = coins.Coins + amount
		if success then
			local success2, err2 = pcall(function()
				DataStore:SetAsync("plr-"..Id, {['Coins'] = coinsval})
			end)
			if success2 then 
				print('Saved') 
			end
		else
			warn('error')
		end
	end)

Can you print the Id and the amount? It makes it easier for me to fix since i can’t get on studio right now.

Right ok so its printing saved but its not actually saving it! I put the print to print as you said about and it doesnt print but ‘Saved’ does!

Can I see how you printed those things?

Ahh right its printing but its still not saving!

game.ReplicatedStorage.Gift.OnServerEvent:Connect(function(player,amount, Id)
		local coins
		local success, err = pcall(function()
			coins = DataStore:GetAsync("plr-"..Id)
		end)
		local coinsval = coins.Coins + amount
		if success then
			local success2, err2 = pcall(function()
				print(coinsval..", "..Id)
				DataStore:SetAsync("plr-"..Id, {['Coins'] = coinsval})
			end)
			if success2 then 
				print('Saved') 
			end
		else
			warn('error')
		end
	end)

Try this:

game.ReplicatedStorage.Gift.OnServerEvent:Connect(function(player,amount, Id)
print('Amount: '..amount)
print('Id: '..Id)
		local coins
		local success, err = pcall(function()
			coins = DataStore:GetAsync("plr-"..Id)
		end)
		local coinsval = coins.Coins + amount
		if success then
			local success2, err2 = pcall(function()
				print(coinsval..", "..Id)
				DataStore:SetAsync("plr-"..Id, {['Coins'] = coinsval})
			end)
			if success2 then 
				print('Saved') 
			end
		else
			warn('error')
		end
	end)
1 Like

Ok its printing fine but still not working??

image

1 Like
game.ReplicatedStorage.Gift.OnServerEvent:Connect(function(player,amount, Id)
print('Amount: '..amount)
print('Id: '..Id)
		local coins
		local success, err = pcall(function()
			coins = DataStore:GetAsync("plr-"..Id) or 0
		end)
		local coinsval = coins.Coins + tonumber(amount)
		if success then
			local success2, err2 = pcall(function()
				print(coinsval..", "..Id)
				DataStore:SetAsync("plr-"..Id, {['Coins'] = coinsval})
			end)
			if success2 then 
				print('Saved') 
			end
		else
			warn('error')
		end
	end)

Still no :grimacing: sorry for all this lol but thanks!

1 Like

Hi! a question, what does this local DataSaving?