Try using this function before sending that remote event over:
local func = type --or use typeof
local function typeDump(t, l, checked)
l = (l and l .. "\t") or ""
checked = checked or {t}
for k, v in next, t do
if type(v) == "table" and not checked[v] then
checked[v] = true;
print(l..func(k),k,func(v),v)
typeDump(v, l, checked)
else
print(l..func(k),k,func(v),v)
end
end
end
Yes, I have checked the initial keys of the table being passed through. However, if you mean whether or not every table is a key or value then I cannot tell since I haven’t ran a deep check function like you’ve mentioned. Here is the output when I look through the keys before sending:
EDIT: I did type(i) that is why it’s printing “string”
I use the player’s userId to hold track of players currently attacking the boss in the system. I can change it into a string via tostring function if that’ll change anything.
One reason, just taking a guess, that this could be erroring is if there is an array that doesn’t start with the index of 1 it will throw the Invalid table key type used error. For example this array would error:
Numeric keys do not go into the array portion of the table just because they are numbers. Generally, ignoring holes, if your table doesn’t have values at keys from 1 to the maximum number, the higher values will go into the dictionary part. and not the array. Hole behavior is more complicated.
It doesn’t have to start at an index of 1 actually because it will replace those fields with null afaik. It has to be either classified as an array or a string key dictionary, and based on the array size and the space between indices, it chooses which part a numerical index would land in (array or dictionary part).
because I was really sure that wasn’t the case but I trust your judgment, I’ve turned it to a string and the error seems to be gone. However, can you elaborate on what took place here? I was under the impression that it was only the tables passed through directly that were affected by the Roblox checks (first generation tables is what I mean). This is a nested table within the passed in the table which had a numerical index. I’m still a bit confused.
I will leave this thread here. This thread is a good example of how bad Roblox’s design decision was. Lua programmers should mostly not need to know the difference between array and hashtable portions of tables.
Roblox has to serialize all the information it uses to communicate to the server and to the client, it does not just magically send objects and text over. It might be using JSON or some other type of storage format to do so but their serializer / type cannot handle those occasions.
If you mean Sparse Tables that wasn’t really the case with mine as I was sure I was passing in stringed keys. However, I see what you mean now, thanks for elaborating.
They are not really arrays, just dictionaries with numerical keys (because they are not placed into the array part). But yes, you have to make all similar occurrences of huge numbers as keys into strings.
Realistically you need an abstraction layer for sending data through Remotes, period. You don’t want to have to care about this for every table, you’ll also run into this with userdata keys you’ll frequently want.
Serialize the table yourself, I use {Key = ActualKey, Value = ActualValue} recursively and unpack like that, very simple.
local NetworkUtility = {}
local function GetTableSerialization(Source)
local Serialization = {}
for Key, Value in pairs(Source) do
local KeySerialization = nil
if type(Key) == "table" then
KeySerialization = GetTableSerialization(Key)
else
KeySerialization = Key
end
local ValueSerialization = nil
if type(Value) == "table" then
ValueSerialization = GetTableSerialization(Value)
else
ValueSerialization = Value
end
local KeyValuePair = {
Key = KeySerialization,
Value = ValueSerialization,
}
table.insert(Serialization, KeyValuePair)
end
return Serialization
end
function NetworkUtility.GetSerialization(...)
local Serialization = {}
local Arguments = {
...
}
for _, Argument in ipairs(Arguments) do
local ArgumentSerialization = nil
if type(Argument) == "table" then
ArgumentSerialization = GetTableSerialization(Argument)
else
ArgumentSerialization = Argument
end
table.insert(Serialization, ArgumentSerialization)
end
return Serialization
end
local function GetTableUnserialization(Source)
local Unserialization = {}
for _, KeyValuePair in ipairs(Source) do
local Key = KeyValuePair.Key
local KeyUnserialization = nil
if type(Key) == "table" then
KeyUnserialization = GetTableUnserialization(Key)
else
KeyUnserialization = Key
end
local Value = KeyValuePair.Value
local ValueUnserialization = nil
if type(Value) == "table" then
ValueUnserialization = GetTableUnserialization(Value)
else
ValueUnserialization = Value
end
Unserialization[KeyUnserialization] = ValueUnserialization
end
return Unserialization
end
function NetworkUtility.GetUnserialization(Serialization)
local Unserialization = {}
for _, Argument in ipairs(Serialization) do
local ArgumentUnserialization = nil
if type(Argument) == "table" then
ArgumentUnserialization = GetTableUnserialization(Argument)
else
ArgumentUnserialization = Argument
end
table.insert(Unserialization, ArgumentUnserialization)
end
return Unserialization
end
return NetworkUtility