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)
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
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)
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