Tilemap Town takes a short, but noticeable amount of time to load on the 3DS, which is a combination of me not implementing websocket compression yet (because wslay doesn't do it for you and it's not trivial to add, and I couldn't find examples of people doing it), and the 3DS's download speeds just being slow.
So there's this nagging feeling that I should try and have a look at formatting the data differently to see if I can improve the bandwidth usage here, using the current state of the main map as an example of some complicated and realistic test data.
Here's what I got:
Original
uncompressed: 150155 - 100.00%
zlib level 6: 18561 - 12.36%
zlib level 9: 18745 - 12.48%
RLE [[x, y, type, count], ...]
uncompressed: 84794 - 56.47%
zlib level 6: 13667 - 9.10%
zlib level 9: 13247 - 8.82%
RLE [[x, y, type, count], ...] Packed
uncompressed: 80339 - 53.50%
zlib level 6: 14520 - 9.67%
zlib level 9: 14005 - 9.33%
RLE [type, x, y, count, ...]
uncompressed: 109284 - 72.78%
zlib level 6: 18860 - 12.56%
zlib level 9: 18555 - 12.36%
Coordinate list
uncompressed: 55088 - 36.69%
zlib level 6: 15250 - 10.16%
zlib level 9: 15223 - 10.14%
Coordinate list + Counts
uncompressed: 54681 - 36.42%
zlib level 6: 13101 - 8.72%
zlib level 9: 13061 - 8.70%
Coordinate list + Counts + Packing
uncompressed: 40417 - 26.92%
zlib level 6: 13199 - 8.79%
zlib level 9: 13088 - 8.72%
Turns out that some very simple run-length encoding stuff cuts the map size a lot as expected, but Deflate was already handling that redundancy pretty well (if the client supports websocket compression).
By packing I meant I'm packing the bits together into one number, so it's something like 0xRRYYXX where R is the repeat count minus one, instead of a separate X, Y and repeat count. This made the compressed size actually worse.
Switching it over to just having the name/data for each tile once and then listing every place the tile is used helps a lot, but I think it makes the map much more expensive to encode (because it would have to keep a directory of what types are already used, which is difficult when types can be arbitrary JSON objects) while not shrinking the compressed size enough to be worth it.
I think the takeaway is to either leave it how it is or go for the first kind of RLE I tried, which has a small but nonzero impact to how complicated map decoding in clients has to be. It's nice to avoid a breaking protocol change where I don't need to do one, and also nice that the simple format I established years ago actually works fine.