Points System Issue

Points System Issue

  1. What do you want to achieve? I have a point system for my juice bar that rewards a staff member with a point every time they mop a spill. Pretty self explanatory but I figured I should explain it anyway.

  2. What is the issue? There isn’t really an issue, but more of an inconvenience/a desire. I would like to configure it so that if you are below a specific rank (e.g. if you are below Junior Barista), your points will be shown as “N/A”.

  3. What solutions have you tried so far? I’ve tried replacing specific parts of the code with other parts that I thought could have worked, but it didn’t.

I do have the whole code but I won’t be posting everything here.

My original code:

Points.Value = DS:GetAsync(Player.UserId) or 0
    DS:SetAsync(Player.UserId, Points.Value)

    Points.Changed:Connect(function()
        DS:SetAsync(Player.UserId, Points.Value)
    end)
end)

My attempted code:

if Player:GetRankInGroup(7664258) >= 4 then
		Points.Value = DS:GetAsync(Player.UserId)
		DS:SetAsync(Player.UserId, Points.Value)
	else if Player:GetRankInGroup(7664258) < 4 then
		Points.Value = DS:GetAsync(Player.UserId)
		DS:SetAsync(Player.UserId, "N/A")
	end
end

I would like you to know that I am not experienced with programming whatsoever, so you’re gonna have to bear with me here. I thank you in advance.

I’m not familiar with DS2, but if I had to guess these are the problems.

if Player:GetRankInGroup(7664258) >= 4 then
        -- I assume Points is a StringValue
        -- here you are getting a value from a DS and then immediately setting the same value to the DS, so the second like is redundant
		Points.Value = DS:GetAsync(Player.UserId)
		DS:SetAsync(Player.UserId, Points.Value) -- (redundant line)
	else if Player:GetRankInGroup(7664258) < 4 then
		Points.Value = DS:GetAsync(Player.UserId) -- setting Points.Value to DS
		DS:SetAsync(Player.UserId, "N/A") -- setting DS to N/A (Points.Value won't be N/A)

        -- I assume at this point you expect Points.Value to be N/A, but it won't since 
        -- you set it to whatever value was in the DS, and then after you set Points you 
        -- then set the DS to N/A

        -- if you switch those two lines around you will be setting DS to N/A and then setting Points.Value to DS (which will be N/A)
	end
end

again, I don’t have the full context this is me assuming you want Points.Value to = N/A

1 Like

In terms of your group, instead of saving as “N/A”, it may be better to simply showcase there point value as N/A when they join.

One possible use case on why this is better is because if someone were to join your group, then leave it, then return at a later date, none of there points from when they used to work there would save. Also, it looks like you’re saving data whenever they gain a point which isn’t recommended. Generally speaking it’s a much better option to save the players data when they leave the game. Instead of saving data whenever they mop a spill, simply edit the value of there points leaderstat with:

player.leaderstats.Points.Value += 1

Anyways, here is a quick example of loading in IntValues for specfic group roles, and then loading in StringValues for users not authorized to gain points.

local Players = game:GetService("Players")
local DSS = game:GetService("DataStoreService")
local PointsDS = DSS:GetDataStore("Points")


Players.PlayerAdded:Connect(function(player) -- generate leaderstats and get points 
	
	local succ, result = pcall(function()
		return PointsDS:GetAsync(player.UserId)
	end)
	
	local PointsValue 
	
	if succ then
		if result ~= nil then
			PointsValue = result
		else
			PointsValue = 0
		end
	end
	
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player

	if player:GetRankInGroup(7664258) >= 4 then
		local points = Instance.new("IntValue")
		points.Name = "Points"
		points.Parent = leaderstats
		points.Value = PointsValue
	else -- if they are lower than RoleSet 4 this will fire instead
		local points = Instance.new("StringValue")
		points.Name = "Points"
		points.Parent = leaderstats
		points.Value = "N/A"
		
	end
end)

game.Players.PlayerRemoving:Connect(function(player) -- instead of saving to the datastore every time they gain a point, simply just save them whenever a player leaves.
-- if you save it every single time a player gains a point, depending on how many players you have some requests may be dropped and not actually save.
	if player:GetRankInGroup(7664258) >= 4 then
		PointsDS:SetAsync(player.userId, player.leaderstats.Points.Value)
	end
end)
1 Like

I never took those reasons into account, but you make a great point. Thank you for your contributions; they’re greatly appreciated.

1 Like

Correct, I do want Points.Value being set to N/A for those under a specific rank. Thank you so much for attempting to help, but I will go forward with sorify’s idea. :smile:

1 Like

ohhh, I just reread the thread. For some reason I thought you were using DataStore2 :joy: ignore my first post.


Anyway glad you got the answer, and expanding on what sorify said

You should always read and write data to a proxy (a table or ValueBase) because of two huge bugs that can occur.

  • Surpassing the Datastore call budget, if you call to a Datastore too many times the requests will get queued and then dropped resulting in data loss. (big problem)
  • Race condition bugs, where some code reads from a Datastore after the Datastore should be changed but since it’s an Async call it hasn’t been changed yet. This can be very problematic since players can exploit this (or it can happen by mistake) to duplicate money/items. (big problem)

Loleris has a good module for streamlining data saving called ProfileService

You are still allowed to push to the datastore after an action happens (you may need to do this in server wide messages or global leaderboards) But you should never read and write values directly to the Datastore.

Anyway good luck!

1 Like