DataStores: leaderstat replication issue across servers (first post)

DataStores: leaderstat replication issue

Game: Minigames by @MejjyBoi

Goal: @MejjyBoi and I want to create a datastore system that displays how much Robux a player as spent on Developer Products
(ex: A player purchases 10 Robux DevProduct, the server recongizes it and adds 10 to their Donated leaderstat value and saves their data.)

Issue: If someone donates 250 Robux, and they are inside a Server housing about 50 clients, only the player who donated gets their data updated and saved. This has been confirmed through server console log. However, if the same players join a new Server, all players who were inside the previous server with the donator will get 250 added to their datastore, and it will be saved. This means that it will appear as if everyone in said server donated 250 Robux, when in reality only one donated.

Solutions attempted: We have searched for the developer forum for answers but have not come across a viable solution yet. Multiple attempts to patch the before-mentioned issue have already been made, with little progress achieved.

Code (SaveClientDataHandler) (contains 2 BindableEvents: SavePlayerData and ModifyClientData):

– BEGIN CODE

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local DataStoreService = game:GetService("DataStoreService") 

local ServerDatabase = DataStoreService:GetDataStore("PlayerData23")
local CurrentSessionData = {}
local Enabled = true

local folder = script.Parent
local SavePlayerDataEvent = folder:WaitForChild("SavePlayerData")

local ModifyClientDataEvent = folder:WaitForChild("ModifyClientData")

local DefaultData = {

	["Donated"] = 0,

}

local function DatabaseConnect(player)
	
	-- SETTING VALUES
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local Donated = Instance.new("NumberValue")
	Donated.Name = "Donated"
	Donated.Parent = leaderstats
	
	-- SAVING DATA TO SERVERDATABASE
	
	local success = nil
	local PlayerData = nil
	local attempt = 0

	repeat
		success,PlayerData = pcall(function()
			return ServerDatabase:GetAsync(player.UserId)
		end)

		if not success then
			warn(PlayerData)
			task.wait(1)
		end

		attempt += 1
	until success or attempt == 5

	if success then
		print("[DATASTORES]: Roblox Server successfully connected to ServerDatabase")

		if not PlayerData then
			warn("[DATASTORES]: User data not found. Assigining the default values.")
			PlayerData = DefaultData
		end
		CurrentSessionData[player.UserId] = PlayerData

	else
		warn("[DATASTORES]: Unable to retrieve data for " ..player.UserId .. " (" .. player.Name .. ")")
		player:Kick("Connection terminated: DataStore warning")
	end
	
	-- SYNCING DATASTORES

	Donated.Value = CurrentSessionData[player.UserId].Donated
	Donated.Changed:Connect(function()
		CurrentSessionData[player.UserId].Donated = Donated.Value
	end)
	leaderstats.Parent = player


end

local function SavePlayerData(player)
	
	if player then
		
		local targetPlayerId = game.Players[player].UserId
		local targetPlayerName = game.Players[player].Name

		if CurrentSessionData[targetPlayerId] then
			local success = nil
			local errorMsg = nil
			local attempt = 0

			repeat
				success, errorMsg = pcall(function()
					ServerDatabase:SetAsync(targetPlayerId, CurrentSessionData[targetPlayerId])
				end)

				if not success then
					warn("[DATASTORES]: Fault in data saving for " .. targetPlayerId .. " (" .. targetPlayerName .. ")")
					warn(errorMsg)
					task.wait(3)
				end

				attempt += 1
			until success or attempt == 5

			if success then

				print("[DATASTORES]: Successfully saved data for " .. targetPlayerId .. " (" .. targetPlayerName .. ")")

			else
				
				warn("[DATASTORES]: Unable to save data for " .. targetPlayerId .. " (" .. targetPlayerName .. ")")

			end

		end
		
	end
end

SavePlayerDataEvent.Event:Connect(function(savingFor)
	
	if savingFor then
		--print("Found")
		if savingFor == true then
			print("[DATASTORES]: Attempting to save data for all connected clients")
			for i, plr in ipairs(Players:GetPlayers()) do
				task.spawn(function()
					SavePlayerData(plr.Name)
				end)
			end
		else
			--print(savingFor)
			SavePlayerData(savingFor)
		end
	else
		warn("[DATASTORES]: Couldn't find client to save data for!")
	end
	
end)

Players.PlayerAdded:Connect(DatabaseConnect)

Players.PlayerRemoving:Connect(function(plr)
	if Enabled == true then
		SavePlayerDataEvent:Fire(plr.Name, plr)
	else
		print("[DATASTORES]: Data Saving is not enabled in this session")
	end
end)

ModifyClientDataEvent.Event:Connect(function(targetedPlayer, key, value, changeType)

	local playerLeaderstatsFolder = game.Players[targetedPlayer.Name]:WaitForChild("leaderstats")

	local targetedKey = playerLeaderstatsFolder:FindFirstChild(key)

	if targetedKey and Enabled == true then

		if changeType == true then

			targetedKey.Value += value

			print("[DATASTORES]: Successfully modified client data for player: " .. targetedPlayer.Name .. " | Changed: " .. key .. " += " .. value)
			
			SavePlayerDataEvent:Fire(targetedPlayer.Name)

		elseif changeType == false then

			targetedKey.Value -= value

			print("[DATASTORES]: Successfully modified client data for player: " .. targetedPlayer.Name .. " | Changed: " .. key .. " -= " .. value)
			
			SavePlayerDataEvent:Fire(targetedPlayer.Name)

		else

			targetedKey.Value = value

			print("[DATASTORES]: Successfully modified client data for player: " .. targetedPlayer.Name .. " | Changed: " .. key .. " = " .. value)
			
			SavePlayerDataEvent:Fire(targetedPlayer.Name)

		end

	else

		warn("[DATASTORES]: Cannot modify client data, invalid key recieved")

	end

end)

while true do
	wait(30)
	SavePlayerDataEvent:Fire(true)
end

-- MODIFY DATASTORE FOR A SPECIFIC CLIENT
-- game.ServerScriptService.DataStoreService.ModifyClientData:Fire("Trecinus", "Wins", 100, true)

-- SAVE DATASTORE FOR A SPECIFIC CLIENT
-- game.ServerScriptService.DataStoreService.SavePlayerData:Fire("Trecinus")

-- SAVE DATASTORES FOR ALL CLIENTS
-- game.ServerScriptService.DataStoreService.SavePlayerData:Fire(true)

– END OF CODE

Please format the script correctly using “```”

format should be fixed now sorry guys

Why would you do this:

--Example
local Bool = false

if Bool then
if Bool == true then

print("hello") 

end
end

What does that do and what would it acomplish?

Are you asking what the code does?

Yes, I’m asking what your example would do.

Im just demonstrating that how you wrote your if’s was not good but works

Did you write that code? Because i feel like it you understand data stores you would understand this

I adapted it from the documentation and a short tutorial. I know how datastores work mostly, just havent been able to find the before-mentioned issue.

Hey!

I think the problem you’re having is that you’re not saving the player’s data to the datastore immediately when they change it. Instead, you’re waiting until the player leaves the game or until a certain amount of time has passed. This means that if a player joins a server, donates 250 Robux, and then leaves the server before their data is saved, then their donation will be lost.

To fix this, you need to save the player’s data to the datastore immediately when they change it. You can do this by adding a SavePlayerData() function to the ValueChanged event of the player’s Donated leaderstat.

Here’s an example:

local Donated = player.leaderstats:FindFirstChild("Donated")
Donated.ValueChanged:Connect(function()
    SavePlayerData(player)
end)

This will make sure that the player’s donation is saved to the datastore right away once they make it.

Another thing you can do is to save the player’s data when they leave the game. You can do this by adding a SavePlayerData() function to the PlayerRemoving event.

Here’s an example:

Players.PlayerRemoving:Connect(function(player)
    SavePlayerData(player)
end)

This will ensure that the player’s data is saved to the datastore even if they leave the game before it’s automatically saved.

I hope this helps!

1 Like

I’m unsure if this will fix the main issue, but I will give it a shot. Thanks!

We’re sorry, but this does not appear to fix the issue.

Issue: If someone donates 250 Robux, and they are inside a Server housing about 50 clients, only the player who donated gets their data updated and saved. This has been confirmed through server console log. However, if the same players join a new Server, all players who were inside the previous server with the donator will get 250 added to their datastore, and it will be saved. This means that it will appear as if everyone in said server donated 250 Robux, when in reality only one donated.

We believe the issue lies in how the DefaultData is managed, and will be testing out solutions soon. If it works, this discussion will be closed.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.