How to store data in seeds?

So i was working on my datastore, but i have to save tons of data, and i was wondering if it is possible/optimized to store data in seeds.

Lets just say i have a table:

local tool = {
	NameID = 23, -- a name in id
	Damage = 10,
	Range = 9,
}

The tool will be randomy generated, so the Damage, range, name will become different every time. (Note: i am saving way more data than this, this is just an example)

So lets say we have an inventory:

Inventory = {
	tool, -- the other varable i just made
	{
		NameID = 12,
		Damage = 56,
		Range = 1,
	},
}

So instead of doing this, i would like to save it in numbers like this:

Inventory 		= {
	17529365,1237128375187 -- the seeds
}

Is this optimized/good? And if it is, how can i achieve this?
If not, please give me other ideas of how to save tons of data!

1 Like

Sounds like compression, this module should work:

3 Likes

Wait, isn’t this text compression? I am not sure how i can use this in this case. Would you explain to me how? (but thanks for the link, it should be usefull to me later)

(nm looking into the first link you sended)

Also i go to sleep in a few minutes so you know i why i won’t reply.

If I understand you correctly, you’re are saying that SeedA generates an item with some properties, since that seed always generates the same item, you can save the seed instead of a list of the items attributes. This is clever but you are going to have an incredibly rough time if you ever need to change the random function the generates the items from the seeds.

1 Like

What do you mean exacly with that? I dont understand what you mean. Can you explain a bit more?

If you want to change the random generation later, its going to be almost impossible to make sure that everyone’s existing items stay the same. The only way I can think of doing it is keeping every old generator version’s code and including which version was used with every item. I don’t think that’s worth the memory saving.

What the other commenters are talking about is encoding your data as text and then (optionally) running it through a text compression algorithm to save storage.

Your idea of deterministically generating the data from a seed is totally sound and could save a lot of storage, assuming you have lots of generated data. Someone pointed out one potential pitfall with this: if you change the code that generates things, players’ saved items will change too!

You can create an RNG with a specific seed using the new-ish Random class: Random | Roblox Creator Documentation

So if you have a sword generator, you can use it like this:

function generateSword(seed)
    local r = Random.new(seed)
    local sword = {
        seed = seed,
        power = r:NextNumber(5, 20)
        ... many more properties
    }
    return sword, seed
end

local r = Random.new()
function givePlayerSomeSwords(player)
    for _ = 1, 3 do
        local sword, seed = generateSword(r:NextInteger())
        --store in the players' runtime inventory
    end
end

function loadPlayerInventory(player)
    local compressedInventory = getPlayerCompressedInventory(player)
    local inventory = {swords={}}--reconstruct runtime inventory from compressed data
    for _, seed in ipairs(compressedInventory.swords) 
        table.insert(inventory.swords, generateSword(seed))
    end
    return inventory
end

function savePlayerInventory(player)
    local inventory = getPlayerInventory(player)
    local compressedInventory = {swords={}}
    for _, item in ipairs(inventory.swords)
        table.insert(compressedInventory.swords, item.seed)
    end
    return compressedInventory
end
2 Likes

Thanks, i’ll check when i open my PC
It might take a while. But how can i convert my data into text? Using tostring()? Or how should i do that?

You should serialize the table, a common method is to use JSONEncode

1 Like

I am Working on that TextCompressen modulescript but for some reason it doen’t work. I have no idea why, can you send the modulescript and how to enable it.

Apperantly this does not work
Module:
image
script:

error:
image

What the heck is happening

Modulescript code entirely
local Module = {}

function Module.GetCode(text)

local dictionary, length = {}, 0
for i = 32, 127 do
	if i ~= 34 and i ~= 92 then
		local c = string.char(i)
		dictionary[c], dictionary[length] = length, c
		length = length + 1
	end
end

local escapemap = {}
for i = 1, 34 do
	i = ({34, 92, 127})[i-31] or i
	local c, e = string.char(i), string.char(i + 31)
	escapemap[c], escapemap[e] = e, c
end
local function escape(s)
	return (s:gsub("[%c\"\\]", function(c)
		return "\127"..escapemap[c]
	end))
end

local function unescape(s)
	return (s:gsub("\127(.)", function(c)
		return escapemap[c]
	end))
end

local function copy(t)
	local new = {}
	for k, v in pairs(t) do
		new[k] = v
	end
	return new
end

local function tobase93(n)
	local value = ""
	repeat
		local remainder = n%93
		value = dictionary[remainder]..value
		n = (n - remainder)/93
	until n == 0
	return value
end

local function tobase10(value)
	local n = 0
	for i = 1, #value do
		n = n + 93^(i-1)*dictionary[value:sub(-i, -i)]
	end
	return n
end

local function compress(text)
	local dictionary = copy(dictionary)
	local key, sequence, size = "", {}, #dictionary
	local width, spans, span = 1, {}, 0
	local function listkey(key)
		local value = tobase93(dictionary[key])
		if #value > width then
			width, span, spans[width] = #value, 0, span
		end
		sequence[#sequence+1] = (" "):rep(width - #value)..value
		span = span + 1
	end
	text = escape(text)
	for i = 1, #text do
		local c = text:sub(i, i)
		local new = key..c
		if dictionary[new] then
			key = new
		else
			listkey(key)
			key, size = c, size+1
			dictionary[new], dictionary[size] = size, new
		end
	end
	listkey(key)
	spans[width] = span
	return table.concat(spans, ",").."|"..table.concat(sequence)
end

local function decompress(text)
	local dictionary = copy(dictionary)
	local sequence, spans, content = {}, text:match("(.-)|(.*)")
	local groups, start = {}, 1
	for span in spans:gmatch("%d+") do
		local width = #groups+1
		groups[width] = content:sub(start, start + span*width - 1)
		start = start + span*width
	end
	local previous;
	for width = 1, #groups do
		for value in groups[width]:gmatch(('.'):rep(width)) do
			local entry = dictionary[tobase10(value)]
			if previous then
				if entry then
					sequence[#sequence+1] = entry
					dictionary[#dictionary+1] = previous..entry:sub(1, 1)
				else
					entry = previous..previous:sub(1, 1)
					sequence[#sequence+1] = entry
					dictionary[#dictionary+1] = entry
				end
			else
				sequence[1] = entry
			end
			previous = entry
		end
	end
	return unescape(table.concat(sequence))
	end
return {compress = compress, decompress = decompress}
end
return Module

What am i doing wrong?

You’re lacking require().
Why would you optimize the data if it makes such little difference?
Data limit for a single data store value is 4MB meaning you can store 4,000,000 standard characters per value.
Let’s say a JSON encoded table for a single tool is so huge that it takes 1,000 bytes.
Then, you’ll still be able to store almost 4,000 tools.
Unless you necessarily need such extreme quantities, I wouldn’t recommend overcomplicating the task.

1 Like

Yeah, but i am going to use this method to save more data, like maps, etc…

And i like optimized stuff (;

I basicly like spending way too much time on things to just improve it slightly.

What you are doing in generate sword, is sending the seed again back, so it can be used again. But is there a way, you can get a seed, depending on the stats in the table? So you can turn every single table into a seed, and then return it into a table (or string)?

Also you forgot typing do after the for loop.

for _, item in ipairs(inventory.swords) do -- you forgot to type the "do"

So you’re basically making it harder for yourself to operate the data because you like having it optimized when it’s not significant?

yes, and to learn new things in lua, such as the httpsservice and storing data in seeds.
And how seeds work and how i can use them.

What you’re looking for is modular arithmetic. If all of the max values are the same (I.e. NameID can be max 100, Damage can be max 100, Range can be max 100) You can do this:

Ideally you would encode and decode the output with base93.

But if the maximum values are different you can’t do this.

1 Like

Please note that you probably shouldn’t/can’t use this for everything. For example vector3’s don’t have a max. And if you set the max value too high (E.G. 10000000) it’d be boatloads of times worse than just plain ol’ text encoding.

This is probably what you’re looking for, but not what you should use.

2 Likes

If thats not what i should use, do you have some different ways if storing data?

Yes, please look at my first reply.

But didn’t you just say “but not what you should use”? I don’t exacly know what you mean?