OK, the docs are just wrong here. You piqued my interest. It’s much more complicated.
Summary
Docs:
Avoid passing a mixed table (some values indexed by number and others by key), as only the data indexed by number will be passed .
Reality:
Only numeric keys >= 1 will be passed. They also have to be contiguous. Except when they don’t have to be. And also sometimes it matters if you implicitly or explicitly declare the index.
Basically, lua is doing things behind the scenes, and I think “mixed” is more complicated than ROBLOX makes it out to be.
Setup
Server script
local tests = {
{1, 2, 3},
{[0]=0, 1, 2, 3},
{1, 2, [4]=4},
{1, 2, 3, 4}, -- [3] = nil
{[2]=2, [3]=3, [4]=4},
{1, 2, 3, [4]=4},
{1, 2, 3, [4]=4}, -- [3] = nil
{1, 2, 3, [4]=4, [6]=6},
{1, 2, 3, [6]=6},
{1, 2, 3, 4, [6]=6},
{[-1]=-1, [0]=0},
{[-1]=-1, [0]=0, [1]=1},
{[-1]=-1, [0]=0, [2]=2},
{[-1]=-1, [0]=0, [2]=2, 1},
}
tests[4][3] = nil
tests[7][3] = nil
wait(4)
for idx, tbl in ipairs(tests) do
wait(.1)
-- print all keys sending
local keys = {}
for k, v in pairs(tbl) do
table.insert(keys, k)
end
print(idx, "sending keys", unpack(keys))
-- print #tbl and ipairs keys
local ikeys = {}
for i, v in ipairs(tbl) do
table.insert(ikeys, i)
end
print("...", #tbl, "ikeys", unpack(ikeys))
-- send
game:GetService("ReplicatedStorage").RemoteEvent:FireAllClients(tbl)
end
Client script
-- client
game:GetService("ReplicatedStorage").RemoteEvent.OnClientEvent:Connect(function(tbl)
local keys = {}
for i,v in pairs(tbl) do
table.insert(keys, i)
end
print("received", unpack(keys))
print()
end)
Testing Results
Show results
tbl |
#tbl |
ipairs keys (server) |
keys received (client) |
notes |
{1, 2, 3} |
3 |
1, 2, 3 |
1, 2, 3 |
duh |
{[0]=0, 1, 2, 3} |
3 |
1, 2, 3 |
1, 2, 3 |
0 is not sent. the array part of tables starts at 1. |
{1, 2, [4]=4} |
2 |
1, 2 |
1, 2 |
4 is not sent. the array part should be contiguous. 4 must be in the hash part of the table |
{1, 2, 3, 4}; tbl[3]=nil |
4 |
1, 2, 3, 4 |
1, 2, 4 |
4 is sent, because it was initially in the array part of the table? #tbl == 4 but ipairs only sees the first two elements. Roblox must just care about what’s in the array part. |
{[2]=2, [3]=3, [4]=4} |
0 |
|
4, 3, 2 |
all sent in random order. all keys are probably put in the hash part of the table (so, not mixed), and are sent in a random order. |
{1, 2, 3, [4]=4} |
4 |
1, 2, 3, 4 |
1, 2, 3, 4 |
all sent. numeric keys are contiguous. doesn’t really make sense though, since explicitly doing [4] means it should go in the hash part? |
{1, 2, 3, [4]=4}; tbl[3]=nil |
2 |
1, 2 |
1, 2 |
sort of makes sense. 4 is in the hash part because it was explicit? |
{1, 2, 3, [4]=4, [6]=6} |
6 |
1, 2, 3, 4 |
1, 2, 3, 4, 6 |
??? bizarre. non-contiguous hash keys still sent |
{1, 2, 3, [6]=6} |
3 |
1, 2, 3 |
1, 2, 3 |
??? … except when they aren’t. Possibly something to do with how lua is dynamically resizing its array part. So maybe… |
{1, 2, 3, 4, [6]=6} |
4 |
1, 2, 3, 4 |
1, 2, 3, 4 |
now I’m just totally lost
|
{[-1]=-1, [0]=0} |
0 |
|
-1, 0 |
makes sense, all hash |
{[-1]=-1, [0]=0, [1]=1} |
1 |
1 |
1 |
sort of makes sense, but [1] is put in the array part. must be some optimization or something |
{[-1]=-1, [0]=0, [2]=2} |
0 |
|
0, -1, 2 |
makes sense, all hash. |
{[-1]=-1, [0]=0, [2]=2, 1} |
2 |
1, 2 |
1, 2 |
okay… |
Wonder if @Osyris or someone who knows more about this could weigh in.