How do you "Compress" tables?

I am using messaging service to make an “infinite player server”, and I ran into an issue where the message is too big. The message size limit for messaging service is 1kb, and I have now reached this limit. I am sending a table through messaging service, so i’m wondering if there is any way to “compress tables”. Could someone show me how to do that?

(Roblox, pls increase the limit ;-; ! Its outrageously small !)

Have you tried using the JSON string format?

1 Like

No, I haven’t, would that help?

Yeah! I converts tables into strings. From there you can check this out, to then compress that string even further. Your flow would look something like this

image

3 Likes

Could you show a typical message’s content? - There are many methods to “compress” data, but without knowing your specific usage, you would only get generic suggestions.

For instance, a few days ago there were this topic also questioning on how to “compress string”. To that topic I added my suggestion for a very domain-specific way to “compress” data.

A very easy method would be to “keep field-identifiers very short”. So instead of a field named Player_UserId, you could shorten it to just PI for the message - as long as you, in your own code, know why you shortened it.

2 Likes

Here is an example of the data:

local playerRecord = {"ReplicatePosition", {{"Player's Name", {CFrame}}, {"Player's Name", {CFrame}}}} -- We will send this through messaging service

The reason why we have two of these {"Player's Name", {CFrame}} is because it records the player’s position 20 times per 2 seconds, then adds it to a table, then sends it.

CFrame is just the PlayersCFrame:GetComponents().

1 Like

My method would work perfectly.

2 Likes

Is there any specific requirement of having an entire CFrame object 20 times per player?

Couldn’t it “just” be 20 Positions and 1 Orientation?

Or how about sending; 1 absolute-position, 19 position-offsets, 1 orientation? - Basically reducing the size of “objects” per player, that is attempted being published every 2nd second?

I haven’t seen any technical explanation of “how” MessagingService’s publishing/subscribing methods does their thing, when transmitting those variant-typed data across server-borders. But I highly suspect that some serialization/deserialization is involved, which most likely is a binary version of a kind of JSON structure.

Another possibility you could look into, would be to “just drop generating data” when getting close to some threshold of “the maximum message size”. So if for example, one server has 10 players (10 players * 20 CFrames / 2 seconds), if that generates “too much data”, then attempt to throttle down the occurrence of data (10 players * 10 CFrames / 2 seconds). Or ‘divide and conquer’, so do half of the players for first message, then the next half of players for second message, alter between them.

1 Like

You could just send in the key for a DataStore, containing a larger amount of data. If you need to get this data fast as well, you’ll have to look into compression algorithms and try compress a JSON string.

For things like CFrame, positions, numbers, you could also use string.byte and string.char to compress numbers into a base 256 byte, rather than a base 10 digit, and then reduce the resolution based on your needs for precision. For CFrames you only need the position, an axis (like LookVector), and an angle to determine an orientation, so you can simplify that into 7 numbers rather than 12, then convert this number into bytes, then into a character via string.char

2 Likes

Can you expand on this compression with string.byte and string.char? I am working with large amounts of data, where I store lots and lots of indexes to other data points (kinda like pointers), where all of these numbers are integers. I’m not too savvy about how Lua numbers work in terms of data use, but I can’t help but feel like my integers are stored in way more bytes than necessary, given my highest stored values go up to 20k, and I do not care for the sign of the integer.

Do scientific notations help you?

I have no idea what that has to do with what I am asking. Each integer is a specific value which corresponds to the index of another set of data. I am not looking to round or truncate any values. I’m just seeing if there is a more memory efficient way to store this data instead of just plain numbers, which Lua could be seeing as any kind of thing for all I know.

I’m just trying to think of simple maths.
How about square rooting the number, then when you need it you can unsquare root it?

If the integer is under 4 billion, you can use the bit32 library to split your number into bytes

local myNumber = 4294967295 -- Max number representable in this way; if you have a greater number, or a non-integer type, you'll have to build your "bytes" in a different way.

-- Convert number to table of bytes
local bytes = table.create(4); do
    local remainder = myNumber
    local i = 1
    repeat
        bytes[i] = bit32.extract(remainder, 0, 8)
        remainder = bit32.lshift(remainder, -8)
        i = i + 1
    until remainder == 0
end

-- Convert bytes to characters
local myChars = table.create(#bytes)
for i = 1, #bytes do
    myChars[i] = string.char(bytes[i])
end

-- Concatenate characters
local myString = table.concat(myChars)

print(myString) -- This generally won't be readable, but will encode your number as compressed as possible.

For non-integer numbers, higher numbers, etc. you’ll have to use a different method to turn your number into bytes. Dividing a number by 256 is equivalent to bit32.lshift(remainder, -8), as a hint for encoding >4B numbers.

2 Likes

I don’t understand what’s happening in that snippet just yet, but this will serve as a great starting point for my research into bit operations in Lua. Thanks a ton!

One question, is there a reason why you specifically used lshift with a negative displacement, rather than a positive displacement with rshift?