i want to add a feature to my game where changes to server are saved and loaded when someone joins that server
the current problem that i have is the data size
roblox’s data store has a limit for saved data
is there a way i can fix that and is there a compression formula i can use?
convert your data to a string and store that, almost like a manual encryption of sorts. for example like coins = c and its value is 100 so as a string its c100. you just gotta make sure when you read that info from the datastore you have to convert it out of that form.
You can split the data into multiple Data Stores
.
You can use Squash to serialize your data (including converting numbers to strings in base 256, but be careful, datastores only accept 93 of the 256 characters). 1 “normal” string character takes up 1 byte. So converting base 10 numbers to base 93 strings saves up space. Other characters like emojis, etc, take up more than 1 byte
You can also use buffers to write to bytes directly, and datastore can now save buffers. I didn’t have the chance to use them yet, but I assume they will give you much tighter control on memory, and also probably will allow us to use the whole 256 possible values of a byte
There are also these luau implementations of compression algorithms you can find on the forum, such as this one LZW text-compression
If you convert the coins to higher base you can turn write that 100 with fewer bytes. And instead of using c
as the header you can use another less common character, which allows your alphabet to be more continuous.
This post is too vague … I need to know what the data you’re saving is…
Is it like this:
local items = {“Item1”, “Item2”, “Item3”}
or like this:
local items = {Item1 = 2, Item2 = 5, Item3 = 3}
Compressing data don’t work out well in Roblox due to the fact it’s hard to have a large enough amount to compress. Small data amounts actually turn out larger “Compressed”.
Let’s say I have 12 items or options a player could have.
This is a completely different way to save data…
- 01 - Sniper
- 02 - Gum
- 03 - Boots
- 04 - Shield
- 05 - Sword
- 06 - Armor
- 07 - Backpack
- 08 - Grappling Hook
- 09 - Magic Wand
- 10 - Flashlight
- 11 - Map
- 12 - Key
Right now I have: Gum, a Sword and a Map.
I could save that to the datastore as “020511”
Then read back 2 digits (strings) at a time to get: 02 05 11. Those would be the numbers (strings) to the items. This way we can have a many items or options by all kinds of names, each with their own number. So when we save to the datastore the data is just a short string, making it super small. When we read it back for use, it would need a conversion function to decode the save back to the items or options.
Could even use 4 digits for item and number of them. Either way you will cut down data needed to save by a mile. 4 digits would be: Up to 99 things and up to 99 of them.
Well without a reply I have no choice but to just guess.
This is a bit hardcore … ya that is a compression Module.
This will need some love to get to work for you the way you wish.
-- ModuleScript in ReplicatedStorage named Compress
local HttpService = game:GetService("HttpService")
local Compress = {}
local dict, len = {}, 0
for i = 32, 127 do
if i ~= 34 and i ~= 92 then
local c = string.char(i)
dict[c], dict[len] = len, c
len = len + 1
end
end
local escMap = {}
for i = 1, 34 do
i = ({34, 92, 127})[i - 31] or i
local c, e = string.char(i), string.char(i + 31)
escMap[c], escMap[e] = e, c
end
local function escape(s)
return (s:gsub("[%c\"\\]", function(c)
return "\127" .. escMap[c]
end))
end
local function unescape(s)
return (s:gsub("\127(.)", function(c)
return escMap[c]
end))
end
local function toBase93(n)
local val = ""
repeat
local rem = n % 93
val = dict[rem] .. val
n = (n - rem) / 93
until n == 0
return val
end
local function toBase10(val)
local n = 0
for i = 1, #val do
n = n + 93^(i - 1) * dict[val:sub(-i, -i)]
end
return n
end
function Compress.compressTable(tbl)
local serializedData = HttpService:JSONEncode(tbl)
local org = #serializedData
local data = Compress.compress(serializedData)
return data, org
end
function Compress.compress(txt)
local d = {}
for k, v in pairs(dict) do
d[k] = v
end
local key, seq, size = "", {}, #d
local width, spans, span = 1, {}, 0
local function listKey(k)
local v = toBase93(d[k])
if #v > width then
width, span, spans[width] = #v, 0, span
end
seq[#seq + 1] = (" "):rep(width - #v) .. v
span = span + 1
end
txt = escape(txt)
for i = 1, #txt do
local c = txt:sub(i, i)
local new = key .. c
if d[new] then
key = new
else
listKey(key)
key, size = c, size + 1
d[new], d[size] = size, new
end
end
listKey(key)
spans[width] = span
return table.concat(spans, ",") .. "|" .. table.concat(seq)
end
function Compress.decompress(txt)
local d = {}
for k, v in pairs(dict) do
d[k] = v
end
local seq, spans, content = {}, txt:match("(.-)|(.*)")
local groups, start = {}, 1
for span in spans:gmatch("%d+") do
local w = #groups + 1
groups[w] = content:sub(start, start + span * w - 1)
start = start + span * w
end
local prev
for w = 1, #groups do
for val in groups[w]:gmatch(('.'):rep(w)) do
local entry = d[toBase10(val)]
if prev then
if entry then
seq[#seq + 1] = entry
d[#d + 1] = prev .. entry:sub(1, 1)
else
entry = prev .. prev:sub(1, 1)
seq[#seq + 1] = entry
d[#d + 1] = entry
end
else
seq[1] = entry
end
prev = entry
end
end
return unescape(table.concat(seq))
end
function Compress.deserializeTable(str)
return HttpService:JSONDecode(str)
end
function Compress.printTable(tbl, indent)
indent = indent or ""
for key, value in pairs(tbl) do
if type(value) == "table" then
print(indent .. key .. ":")
Compress.printTable(value, indent .. " ")
else
print(indent .. key .. ": " .. tostring(value))
end
end
end
return Compress
-- ServerScript in ServerScriptService
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local Compress = require(game:GetService("ReplicatedStorage"):WaitForChild("Compress"))
local playerDataStore = DataStoreService:GetDataStore("PlayerDataStore")
local function onPlayerAdded(player)
local playerId = player.UserId
local loadedData
local loadSuccess, loadData = pcall(function()
return playerDataStore:GetAsync(tostring(playerId))
end)
if loadSuccess and loadData then
local decompressedData = Compress.decompress(loadData)
loadedData = Compress.deserializeTable(decompressedData)
print("Loaded Data:")
Compress.printTable(loadedData)
else
print("No data found for player or failed to load data.")
loadedData = {}
end
player.AncestryChanged:Connect(function(_, parent)
if not parent then
loadedData = { score = 100, level = 5, inventory = {"sword", "shield"} }
local compressedData, org = Compress.compressTable(loadedData)
local com = #compressedData
print("Original Size: " .. org .. " bytes")
print("Compressed Size: " .. com .. " bytes")
local success, err = pcall(function()
playerDataStore:SetAsync(tostring(playerId), compressedData)
end)
if success then
print("Data saved successfully.")
else
warn("Failed to save data: " .. err)
end
end
end)
end
Players.PlayerAdded:Connect(onPlayerAdded)
ion even script you know whats up
i fw memory alocation heavy…
Roblox’s buffer library will be the fastest and easiest way to compress data.
I don’t recommend any other compression library (personal opinion and some facts you can research on your own)
Some of the reasons you’d use the buffer library and why you should, is simply just because it’s built for roblox’s engine and is performance optimized. It’s a roblox library so it’s constantly being updated and worked on behind the scenes, no external dependencies, and it’s memory efficient.
Of course, the option is yours, this is just my opinion. buffer | Documentation - Roblox Creator Hub
I also recommend watching this video if you’re new to using the buffer library. (or just read the documentation on it)