TIL The Length Operator (#) can cause a time-bomb desync.

Level 9
Joined
Jun 28, 2023
Messages
51
Basically the title.

I've been helping the CoE map by modernizing its implementation and translating it to LUA, and this gnarly desync started happening after 30min or more of game time.

It turns out that, after a lot of bug hunting, the # operator in LUA does not behave consistently across clients when the table its being used on has holes in it (keys with nil as a value).

Excerpt from the official LUA manual to prove it:

A table with exactly one border is called a sequence.For instance, the table {10, 20, 30, 40, 50} is a sequence,as it has only one border (5).The table {10, 20, 30, nil, 50} has two borders (3 and 5),and therefore it is not a sequence.(The nil at index 4 is called a hole.)The table {nil, 20, 30, nil, nil, 60, nil}has three borders (0, 3, and 6),so it is not a sequence, too.The table {} is a sequence with border 0.

When t is a sequence,#t returns its only border,which corresponds to the intuitive notion of the length of the sequence.When t is not a sequence,#t can return any of its borders.(The exact one depends on details ofthe internal representation of the table,which in turn can depend on how the table was populated andthe memory addresses of its non-numeric keys.)

So, since each client has probably a different table memory layout (same reason why pairs() can desync), the # operator can desync if there are nils in your tables...

Hope this helps someone thats slaving over hunting desyncs, like I was a few days ago!

If I'm wrong, please debunk!
 
Top