How can I fix this bug where the entire server gets an item in my game from a case?

I would like the game to only give the item to the player that opens said case.

Case remote:

game.ReplicatedStorage.Spin.OnServerEvent:Connect(function(player, case, amount, key)
	local playerData = Data.Get(player);
	amount = tonumber(amount) or 1	
	if case then
		local caseItems = require(script.CaseData)[case].Items;
		local casePrice = require(script.CaseData)[case].Price;
		local winner = caseItems[math.random(1,#caseItems)];
		local bux = playerData.Values.Bux
		local casesOpen = playerData.Values.CasesOpen;
		if bux >= casePrice and casesOpen >= amount then
			playerData.Values.Bux-=casePrice*amount;
			local secretChance = math.random(1, 1000000000)
			if secretChance == 1 then 
				winner = 10760425;
				game.ReplicatedStorage.Chat:FireAllClients(player.Name .. ' has unboxed a secret item! The Staff of Sparks! (0.000000001' .. '%)','SourceSansBold')
			end
			openCase(player, winner, amount)
                        giveItem(player, winner, amount)
			playerData.Values.Progress += 1
		else
			Data.post(player, 'You are missing bux or cases required.','Case')
		end
	end
end)

giveItem function:

function giveItem(player, item2, Amount)
	local playerData
	while wait() do
		playerData = Data.Get(player)
		if playerData then break end;
	end
		local item
		local inventory = playerData.Arrays.Inventory;
		for i = 1, #inventory do
			local v = inventory[i]
			if v.ItemId == item2 then
				v.Amount += Amount
				return
			end
		end
		local items = require(script.Items)
		for i, v in pairs(items) do
			if v.ItemId == item2 then
				item = v
			end
		end
		item.Amount = Amount;
	inventory[#inventory + 1] = item;
	return
end

If you need anymore code, please let me know. It is a huge bug in my game and ruins the entire game.

What about your openCase function?

Here you go!

function openCase(player, itemid, amount)
	for i = 1, #require(script.Items) do
		local item = require(script.Items)[i];
		if item.ItemId == itemid then
			game.ReplicatedStorage.Spin:FireClient(player, item, amount)
			break
		end
	end
end

I have confirmed the problem is GiveItem, I’m not sure where to go with it.

This might not be the problem, but I think its related to your datastore.

If this is working like I think it is, you’re giving a table of the players data to the function. And that table probably has a template table you keep in a module so you can easily give out templates to new players.

But I think whats going on is instead of giving the player a copy of the template table, you’re giving them a reference to it, which means when you give 1 person data you’re giving everyone data because they’re sharing the template table reference.

To fix this, instead of giving the player a template table, you’d give them a copy of the template table with table.copy() or if its a dictionary you’d use this function to clone it

local function shallowCopy(original)
	local copy = {}
	for key, value in pairs(original) do
		copy[key] = value
	end
	return copy
end

I could be entirely wrong also, but I don’t know how your datastore script works so I don’t know for sure.

Hmm. I used this data store in other games and it has never been the problem. Here is my datastore script (as it could be somewhere in this, but unsure!) if it would help me get a better understanding on this.

local aloe = {};
--// SERVICES \\
local DataStoreService = game:GetService("DataStoreService")

--// DATA VARS \\
local sessionData = {};
local dataSet = DataStoreService:GetDataStore("AloeV.11")
local RapSet = DataStoreService:GetOrderedDataStore('AloeV.3_Rap')
local startData = {
	Values = {
		Bux = 250000,
		Tix = 0,
		XP = 0,
		Level = 0,
		Gems = 0,
		CasesOpen = 1,
		Progress = 0,
		Prestige = 1,
	},
	Arrays = {
		Inventory = {},
		Flags = {},
		Equipped = {};
	}
}
--// FUNCTIONS \\
aloe.post = function(player, body, title)
	game.ReplicatedStorage.Notification:FireClient(player, body, title)
end
aloe.Get = function(value1)
	return sessionData[value1];
end
local rewards = {};
aloe.AddReward = function(player, amount)
	rewards[player] += amount
end
aloe.GetReward = function(player)
	return rewards[player]
end
aloe.RAPCalculate = function(value1)
	local inventory = sessionData[value1].Arrays.Inventory;
	if not inventory then return 0 end
	local rap = 0
	for i, v in pairs(inventory) do
		rap += v.Amount * v.Rap;
	end
	return rap;
end
aloe.StartServer = function(value1)
	if dataSet:GetAsync(value1.UserId) then
		sessionData[value1] = dataSet:GetAsync(value1.UserId)
	else
		sessionData[value1] = startData
	end
	rewards[value1] = 0
end
aloe.Save = function(value1)
	if not sessionData[value1].Arrays.Flags then
		sessionData[value1].Arrays.Flags = {};
	end
	if not sessionData[value1].Values.Progress then
		sessionData[value1].Values.Progress = 0
	end
	dataSet:SetAsync(value1.UserId, sessionData[value1])
	RapSet:SetAsync(value1.Name .. ";" .. value1.UserId, aloe.RAPCalculate(value1))
end
aloe.GrabRap = function()
	local globalData = {};
	local page = RapSet:GetSortedAsync(false, 100);
	page = page:GetCurrentPage();

	for key, value in next, page do
		globalData[#globalData + 1] = {value.key, value.value};
	end

	return globalData;
end

aloe.GiveItem = function(player, item2, Amount)
	local item
	local inventory = sessionData[player].Arrays.Inventory;
	for i = 1, #inventory do
		local v = inventory[i]
		if v.ItemId == item2 then
			v.Amount += Amount
			return
		end
	end
	local items = require(script.Parent.Items)
	for i, v in pairs(items) do
		if v.ItemId == item2 then
			item = v
		end
	end
	item.Amount = Amount;
	inventory[#inventory + 1] = item;
end
aloe.CaseItem = function(player, item2, Amount)
	local item
	local inventory = sessionData[player].Arrays.Inventory;
	for i = 1, #inventory do
		local v = inventory[i]
		if v.ItemId == item2.ItemId then
			v.Amount += Amount
			return
		end
	end
	item2.Amount = Amount;
	inventory[#inventory + 1] = item2;
end
aloe.RemoveItem = function(player, itemId, amount)
	local inventory = sessionData[player].Arrays.Inventory;
	if not itemId then return end
	local buxToReturn = 0 -- This is only if selling and not jackpotting
	for i, v in pairs(inventory) do
		if v.ItemId == itemId then
			if v.Locked == true then
				buxToReturn = 0;
				break
			end
			if v.Amount - amount == 0 then
				buxToReturn = amount * v.Rap
				table.remove(inventory, i);
				break
			end
			if tonumber(v.Amount) > amount then
				v.Amount -= amount;
				buxToReturn = amount * v.Rap
				break
			end
		end
	end
	local x = sessionData[player].Values.Prestige
	if x == 1 then x = 1 else x = x * 5.75 end
	return buxToReturn * x
end
aloe.Prestige = function(player)
	local inventory = sessionData[player].Arrays.Inventory; -- BYPASSES LOCK
	local prestigeItemCount = 0
	for i, v in pairs(inventory) do
		if inventory[i].ItemId == 140469106 then
			prestigeItemCount += inventory[i].Amount
			break
		end
	end
	sessionData[player].Arrays.Inventory = {};
	aloe.GiveItem(player, 140469106, prestigeItemCount)
end
aloe.SellAll = function(player)
	local inventory = sessionData[player].Arrays.Inventory;
	for i = 1, #inventory + 10 do
		wait()
		if inventory[i] then
		sessionData[player].Values.Bux += aloe.RemoveItem(player, inventory[i].ItemId, inventory[i].Amount);
		end
		wait()
	end
end
aloe.JackpotItem = function(player, itemId, amount)
	local inventory = sessionData[player].Arrays.Inventory;
	local doesHaveItem = false
	if amount < 1 then 
		return false
	end
	for i, v in pairs(inventory) do
		if v.ItemId == itemId then
			if v.Locked == true then
				doesHaveItem = false;
				break
			end
			if v.Amount - amount == 0 then
				doesHaveItem = true;
				table.remove(inventory, i);
				break
			end
			if tonumber(v.Amount) > amount then
				v.Amount -= amount;
				doesHaveItem = true;
				break
			end
		end
	end
	return doesHaveItem;
end
return aloe;

Here, you set it to the start table, which is just a reference to that table and not an actual new copy of the table

You should change that line to

sessionData[value1] = shallowCopy(startData)

And add that shallowCopy function from my first line, that might help. I had this same issue before where adding an item to a player gave it to everyone, and my fix was doing this, so it might help you too.

I will test this now, and report back to you on the result! Thanks for your time.

1 Like

I wiped the datastores and tried this, and it still is giving the item to everyone in the server.

Looking at your datastore code, you are using nested tables. Shallow copying is not enough. You need to recursively copy all tables of your starting template as new. Try this:

-- Recursively copies a table.
local function recursiveCopy(dataTable)
	local tableCopy = {}
	for index, value in pairs(dataTable) do
		if type(value) == "table" then
			value = recursiveCopy(value)
		end
		tableCopy[index] = value
	end
	return tableCopy
end

I think KdudeDev nailed it as to what the problem is. Tables are references. If you just give the players a reference to your starting table, anytime one player get’s their data updated, they all get the same update. Just copying the top table isn’t enough. The nested tables are also tables and have references to them as well. So you have to recursively copy everything. The root cause seems to be that there is no data separation between players. The above function will ensure that there is data separation between the players.

This fixed it, thanks so much!