Saved data replicating to every player in server

I have some items that can be unlocked and they are stored in CrateData folder that’s on a player.

The unboxing script, which unboxes items and grants true false depending what was unlocked, is here. It works normally and saves normally when I’m alone in a server, but when I tried with multiple people, chaos…

My replicated data (i have everything unlocked) it cloned to every player and everyone unlocked everything

--Variables
local rs = game:GetService("ReplicatedStorage")
local remotes = rs:WaitForChild("RemoteEvents")
local crates = rs:WaitForChild("Crates")
--local gamepasscrates = rs:WaitForChild("GamepassCrates")
--local normalcrates = rs:WaitForChild("Crates")
local items = rs:WaitForChild("Items")
local ms = game:GetService("MarketplaceService")
local gamepassId = 735558039
local doubleLuckRemote = rs:WaitForChild("DoubleLuckRemote")

--function boostChances(plr)
	--local ownsGamepassServer = false
	--if ms:UserOwnsGamePassAsync(plr.UserId, gamepassId) then
		--ownsGamepassServer = true
		--plr:SetAttribute("DoubleLuck", true)
		--crates = gamepasscrates
		--print("User owns game pass! Their chances increase.")
	--else
		--ownsGamepassServer = false
		--plr:SetAttribute("DoubleLuck", false)
		--crates = normalcrates
		--print("User does not own the game pass, their chances are default.")
	--end
--end


local unboxDisplayer = rs:WaitForChild("UnboxDisplayer") 
local unboxUnlocker = rs:WaitForChild("UnboxUnlocker")

local crateDataFolder

local rnd = Random.new()

local playersUnboxing = {}

--Data handling
local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("UNBOXING_SYSTEM_DATA")
local unboxDs = dss:GetDataStore("UnboxInventoryData")

function saveData(plr)
	if not plr:FindFirstChild("DATA_FAILED_TO_LOAD") then
		local coins = plr.leaderstats.Coins.Value -- Load Coins instead of Cash

		local compiledData = {
			Coins = coins -- Save Coins instead of Cash
		}

		local success, err = nil, nil
		while not success do
			success, err = pcall(function()
				ds:SetAsync(plr.UserId, compiledData)
			end)
			if err then
				warn(err)
			end
			task.wait(0.02)
		end
	end
end

game.Players.PlayerRemoving:Connect(saveData)

game:BindToClose(function()
	for _, plr in pairs(game.Players:GetPlayers()) do
		saveData(plr)
	end
end)

game.Players.PlayerAdded:Connect(function(plr)
	local dataFailedWarning = Instance.new("StringValue")
	dataFailedWarning.Name = "DATA_FAILED_TO_LOAD"
	dataFailedWarning.Parent = plr

	local plrData = nil
	local success = pcall(function()
		plrData = ds:GetAsync(plr.UserId)
	end)
	if success and plrData then
		dataFailedWarning:Destroy()
		crateDataFolder = plr:FindFirstChild("CrateData")

		-- If 'Coins' doesn't exist in leaderstats, create it
		local leaderstats = plr:FindFirstChild("leaderstats")
		local coinsValue = leaderstats and leaderstats:FindFirstChild("Coins")
		if not coinsValue then
			coinsValue = Instance.new("IntValue")
			coinsValue.Name = "Coins"
			coinsValue.Value = plrData.Coins
			coinsValue.Parent = leaderstats or plr
		else
			coinsValue.Value = plrData.Coins -- Update existing Coins value
		end
	else
		warn("Failed to load player data for " .. plr.Name)
	end
	
	plr.CharacterAdded:Connect(function()
	end)
end)

--Unboxing a crate
remotes:WaitForChild("BuyCrate").OnServerEvent:Connect(function(plr, crateName)
	local plrCoins = plr.leaderstats.Coins -- Use Coins instead of Cash

	if crates:FindFirstChild(crateName) and not playersUnboxing[plr] then
		local crateProperties = require(crates[crateName])

		if plrCoins.Value >= crateProperties["Price"] then
			plrCoins.Value -= crateProperties["Price"]

			local chances = crateProperties["Chances"]
			local plrChance = rnd:NextNumber() * 100

			local n = 0
			local rarityChosen = nil

			for rarity, chance in pairs(chances) do
				n += chance
				if plrChance <= n then
					rarityChosen = rarity
					break
				end
			end

			local unboxableItems = crateProperties["Items"]

			for i = #unboxableItems, 2, -1 do
				local j = rnd:NextInteger(1, i)
				unboxableItems[i], unboxableItems[j] = unboxableItems[j], unboxableItems[i]
			end

			local itemChosen = nil

			for _, itemName in pairs(unboxableItems) do
				if items:FindFirstChild(itemName, true).Parent.Name == rarityChosen then
					itemChosen = items:FindFirstChild(itemName, true)
					break
				end
			end
			
			local function CaseHandler(plr)
				local itemName = itemChosen.Name
				local item = crateDataFolder:FindFirstChild(itemName)
				if item then
					if item.Value ~= true then
						item.Value = true
						print(itemName .. " is now unlocked!")
					else
						plrCoins.Value += crateProperties["Price"] / 2
						print("Item already owned! Granting 50% of the case price.")
					end

					if plr then
						local playerData = {}
						for _, child in ipairs(crateDataFolder:GetChildren()) do
							playerData[child.Name] = child.Value
						end
						unboxDs:SetAsync(tostring(plr.UserId), playerData)
					end
				else
					print("Item not found!")
				end
			end

			playersUnboxing[plr] = itemChosen.Name
			print("Chosen item is " .. itemChosen.Name)

			local unboxTime = rnd:NextNumber(3, 7)
			remotes:WaitForChild("CrateOpened"):FireClient(plr, crateName, itemChosen, unboxTime)

			local timeStarted = tick()
			while true do
				if (tick() - timeStarted >= unboxTime) or (not plr.Character) then
					break
				end
				game:GetService("RunService").Heartbeat:Wait()
			end
			
			unboxDisplayer:FireClient(plr, itemChosen.Name)
			CaseHandler(plr)
			playersUnboxing[plr] = nil
		end
	end
end)
1 Like

when setting your crate data folder make sure it is separated for the player so you can do and the crateDataFolder variable at the start is defined as a table crateDataFolder[plr] = plr:FindFirstChild("CrateData")

Then to get the crate Data folder you can just do local data = crateDataFolder[player]. To remove it when they leave you can also do crateDataFolder[player] = nil

1 Like

Can you explain in more detail where should I put it?

so when you define crateDataFolder at the start it needs to be a table.

When a player joins instead of doing crateDataFolder = plr:FindFirstChild("CrateData") you do crateDataFolder[plr] = plr:FindFirstChild("CrateData") to store it for that player. So now when you want to use that data again you can do crateDataFolder[plr] to access it.

When you want to delete it so when the player leaves you set it to be nil

--Variables
local rs = game:GetService("ReplicatedStorage")
local remotes = rs:WaitForChild("RemoteEvents")
local crates = rs:WaitForChild("Crates")
--local gamepasscrates = rs:WaitForChild("GamepassCrates")
--local normalcrates = rs:WaitForChild("Crates")
local items = rs:WaitForChild("Items")
local ms = game:GetService("MarketplaceService")
local gamepassId = 735558039
local doubleLuckRemote = rs:WaitForChild("DoubleLuckRemote")

--function boostChances(plr)
	--local ownsGamepassServer = false
	--if ms:UserOwnsGamePassAsync(plr.UserId, gamepassId) then
		--ownsGamepassServer = true
		--plr:SetAttribute("DoubleLuck", true)
		--crates = gamepasscrates
		--print("User owns game pass! Their chances increase.")
	--else
		--ownsGamepassServer = false
		--plr:SetAttribute("DoubleLuck", false)
		--crates = normalcrates
		--print("User does not own the game pass, their chances are default.")
	--end
--end


local unboxDisplayer = rs:WaitForChild("UnboxDisplayer") 
local unboxUnlocker = rs:WaitForChild("UnboxUnlocker")

local crateDataFolder = {}

local rnd = Random.new()

local playersUnboxing = {}

--Data handling
local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("UNBOXING_SYSTEM_DATA")
local unboxDs = dss:GetDataStore("UnboxInventoryData")

function saveData(plr)
	if not plr:FindFirstChild("DATA_FAILED_TO_LOAD") then
		local coins = plr.leaderstats.Coins.Value -- Load Coins instead of Cash

		local compiledData = {
			Coins = coins -- Save Coins instead of Cash
		}

		local success, err = nil, nil
		while not success do
			success, err = pcall(function()
				ds:SetAsync(plr.UserId, compiledData)
			end)
			if err then
				warn(err)
			end
			task.wait(0.02)
		end
	end
end

game.Players.PlayerRemoving:Connect(saveData)

game:BindToClose(function()
	for _, plr in pairs(game.Players:GetPlayers()) do
		saveData(plr)
	end
end)

game.Players.PlayerAdded:Connect(function(plr)
	local dataFailedWarning = Instance.new("StringValue")
	dataFailedWarning.Name = "DATA_FAILED_TO_LOAD"
	dataFailedWarning.Parent = plr

	local plrData = nil
	local success = pcall(function()
		plrData = ds:GetAsync(plr.UserId)
	end)
	if success and plrData then
		dataFailedWarning:Destroy()
		crateDataFolder[plr] = plr:FindFirstChild("CrateData")

		-- If 'Coins' doesn't exist in leaderstats, create it
		local leaderstats = plr:FindFirstChild("leaderstats")
		local coinsValue = leaderstats and leaderstats:FindFirstChild("Coins")
		if not coinsValue then
			coinsValue = Instance.new("IntValue")
			coinsValue.Name = "Coins"
			coinsValue.Value = plrData.Coins
			coinsValue.Parent = leaderstats or plr
		else
			coinsValue.Value = plrData.Coins -- Update existing Coins value
		end
	else
		warn("Failed to load player data for " .. plr.Name)
	end
	
	plr.CharacterAdded:Connect(function()
		
	end)
end)

--Unboxing a crate
remotes:WaitForChild("BuyCrate").OnServerEvent:Connect(function(plr, crateName)
	local plrCoins = plr.leaderstats.Coins -- Use Coins instead of Cash

	if crates:FindFirstChild(crateName) and not playersUnboxing[plr] then
		local crateProperties = require(crates[crateName])

		if plrCoins.Value >= crateProperties["Price"] then
			plrCoins.Value -= crateProperties["Price"]

			local chances = crateProperties["Chances"]
			local plrChance = rnd:NextNumber() * 100

			local n = 0
			local rarityChosen = nil

			for rarity, chance in pairs(chances) do
				n += chance
				if plrChance <= n then
					rarityChosen = rarity
					break
				end
			end

			local unboxableItems = crateProperties["Items"]

			for i = #unboxableItems, 2, -1 do
				local j = rnd:NextInteger(1, i)
				unboxableItems[i], unboxableItems[j] = unboxableItems[j], unboxableItems[i]
			end

			local itemChosen = nil

			for _, itemName in pairs(unboxableItems) do
				if items:FindFirstChild(itemName, true).Parent.Name == rarityChosen then
					itemChosen = items:FindFirstChild(itemName, true)
					break
				end
			end
			
			local function CaseHandler(plr)
				local itemName = itemChosen.Name
				local item = crateDataFolder[plr]:FindFirstChild(itemName)
				if item then
					if item.Value ~= true then
						item.Value = true
						print(itemName .. " is now unlocked!")
					else
						plrCoins.Value += crateProperties["Price"] / 2
						print("Item already owned! Granting 50% of the case price.")
					end

					if plr then
						local playerData = {}
						for _, child in ipairs(crateDataFolder[plr]:GetChildren()) do
							playerData[child.Name] = child.Value
						end
						unboxDs:SetAsync(tostring(plr.UserId), playerData)
					end
				else
					print("Item not found!")
				end
			end

			playersUnboxing[plr] = itemChosen.Name
			print("Chosen item is " .. itemChosen.Name)

			local unboxTime = rnd:NextNumber(3, 7)
			remotes:WaitForChild("CrateOpened"):FireClient(plr, crateName, itemChosen, unboxTime)

			local timeStarted = tick()
			while true do
				if (tick() - timeStarted >= unboxTime) or (not plr.Character) then
					break
				end
				game:GetService("RunService").Heartbeat:Wait()
			end
			
			unboxDisplayer:FireClient(plr, itemChosen.Name)
			CaseHandler(plr)
			playersUnboxing[plr] = nil
		end
	end
end)

Like this?

I’ve fixed the issue thank you. Would you happen to know a solution to something else?

I want to announce globally when a player gets something.

local rs = game:GetService("ReplicatedStorage")
local unboxEvent = rs:WaitForChild("UnboxDisplayer")

local legendaries = {
	"Angel of Angels", "Endless Void", "Witch Companion"
}

local epics = {
	"Burning Sensation", "Clover Companion"
}

local rares = {
	"Glitter Companion", "Pink Cyborg"
}

local uncommons = {
	"Triangular Companion", "Orange Dash"
}

local commons = {
	"Blu Companion", "Blue Ripples"
}

unboxEvent.OnClientEvent:Connect(function(itemChosen)
	local color
	if table.find(legendaries, itemChosen) then
		color = Color3.fromRGB(255, 152, 25) -- Orange color for legendaries
	elseif table.find(epics, itemChosen) then
		color = Color3.fromRGB(240, 74, 146) -- Purple color for epics
	elseif table.find(rares, itemChosen) then
		color = Color3.fromRGB(102, 54, 247) -- Blue color for rares
	elseif table.find(uncommons, itemChosen) then
		color = Color3.fromRGB(33, 233, 255) -- Yellow color for uncommons
	else
		color = Color3.fromRGB(124, 227, 55) -- Cyan color for commons
	end

	game.StarterGui:SetCore("ChatMakeSystemMessage", {
		Text = itemChosen .. " has just been unboxed by " .. tostring(game.Players.LocalPlayer) .. "!",
		Color = color,
		Font = Enum.Font.SourceSansBold,
		TextSize = 18,
	}) 
end)

This is a local script that is located in starterplayerscripts

you should look into messaging service for that

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