Why is My Equipped Tabled Deleting?

So I’m making a shop and inventory system where I store the players Inventory inside a table. It looks like this:
image
But whenever I add an item into a table for example Effects, the equipped table will get deleted out of it when something is inserted in. This is the code that handles a purchase:

RemoteFunctions.RequestPurchase.OnServerInvoke = function(player, TypeOfItem, ItemToBuy)
	if not table.find(PlayerInventorys[player.UserId][TypeOfItem], ItemToBuy) then 
		local Item = ShopItems:FindFirstChild(TypeOfItem):FindFirstChild(ItemToBuy)
		if Item then
			if player.PlayerStats.Coins.Value >= Item.Price.Value then 
				print(PlayerInventorys[player.UserId])
			  table.insert(PlayerInventorys[player.UserId][TypeOfItem], Item.Name)
			  player.PlayerStats.Coins.Value -= Item.Price.Value
			  print(PlayerInventorys[player.UserId])
			  return true
			end
		end
	end
	return false
end

This is what the table looks like after a purchase:


Why has it deleted the Equipped Table?

Sending mixed tables through remotes or bindables is not supported on Roblox.

To fix this, you’ll need to change the format of your data to avoid mixed tables.

Let’s dig into what that means.

What is a mixed table?

In Lua, think of there being two kinds of tables.

  1. Dictionary: Keys may be anything. Imagine finding a word (key) in a physical dictionary and looking up its definition (value).
  2. Array: Keys must be numbers starting from 1 and counting up, with no holes. The first null value indicates the END of the array. Imagine an ordered list (array) of things.

For example, when you define a table like this, e.g. as an array:

Array

{
	"hi",
	"world",
}

that’s syntactic sugar for this equivalent dictionary-style definition:

Also an array, but shown like a dictionary

{
	[1] = "hi",
	[2] = "world",
}

A dictionary that is not an array looks like this:

Dictionary

{
	hi = "world",
}

which, by the way, is the same thing as

Dictionary (alternate)

{
	["hi"] = "world"
}

So both arrays and dictionaries are technically the same type under the hood, but certain Lua functions give special treatment to arrays.

For example, in an array with keys counting from 1 to n, ipairs will stop iterating at n even if n+2 exists, or even if a non-numeric key exists. Similarly, the # operator will only count items up to n. This is also why you can’t use # on a dictionary!

A mixed table is a table that contains both styles of keys.

Usually, you want to write your code to avoid this kind of table since it creates exactly the kind of confusion you’re having now.

Why mixed tables are causing you problems

Now that we understand tables formatted as arrays vs dictionaries, let’s look at why it’s causing you problems.

Setting up your table

In your code, you first define your Effects table with a dictionary-style index Equipped, creating essentially this in a server script:

local playerdata = {
	Effects = {
		Equipped = {}, -- Effects contains Equipped, a dictionary key
	},
}

Verifying the setup

If you print playerdata.Effects now from the same server script with log mode of the output off, this calls a Roblox function under the hood to “pretty print” your table, giving you this output:

Command (server script)

print(playerdata.Effects)

Output (server)

{
	["Equipped"] = {}
}

Nice! It includes Equipped just as expected.

Create the mixed table

Later, you use table.insert to add the item into the Effects table.

table.insert(playerdata.Effects, "Gamer")

Let’s re-run the print commands from earlier.

Command (server script)

print(playerdata.Effects)

Output (server)

{
	[1] = "Gamer",
	["Equipped"] = {}
}

Woah - we see the same “Equipped” key as before, but now there’s also a 1 key! That’s because table.insert treats it like an array, and it sees the lowest non-nil numeric index is [1], so it inserts the value there.

We now have a mixed table, where part of it is defined as an array and another part is defined as a dictionary.

But so far, this is expected! This looks good! What about your problem, where the Equipped table is going away?

Replicating the problem

You have the player’s inventory stored in a server script, but you need your client script to have access to it. So, naturally, you send it to your client script using a Remote Event.

Something similar to:

Code (server script)

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local replicateInventory = ReplicatedStorage.ReplicateInventoryRemoteEvent
...
print(playerdata) -- this is the print from earlier. Everything was fine.
replicateInventory:FireClient(somePlayer, playerdata)

And on the client, you have something like:

Code (client script)

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local replicateInventory = ReplicatedStorage.ReplicateInventoryRemoteEvent

replicateInventory.OnClientEvent:Connect(function(playerdata)
	print(playerdata.Effects) -- Let's print what the client receives
end)

On the client, the print outputs this:

Output (client)

{
	[1] = "Gamer"
}

AHA!

It happened! Equipped is gone from the Effects table! But it was just there on the server! Here, you encounter a limitation of Roblox. As stated at the top of this post, Roblox does not support sending mixed tables through Remote Events/Functions, or even through Bindable events/functions for that matter.

The reason is because bindables and remotes pass your data through a C++ backend which doesn’t keep mixed tables. I assume this hasn’t been “fixed” in order to avoid breaking legacy games that depend on this behavior.

Anyway, as stated at the top of this post, you need to change the way you define your data to avoid mixed tables to fix your problem. :white_check_mark:

For example, a schema like this would work because on a per-table basis, keys are always either array-style OR dictionary-style, but never both in the same table:

local playerdata = {
	Effects = {
		Owned = {
			"item1",
			"item6",
			"item5",
			"item7",
			"item2",
		},
		Equipped = {
			"item5",
			"item1",
		}
	},
	Trails = {
		...
	},
	...
}
1 Like

Thanks alot for explaining this to me. I had found the solution because I noticed a dictionary and array in the same table to I separated Equipped similar to your solution but I didn’t know why that was a problem. Thanks alot for explaining how this actually works and expanding my knowledge.

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