How to store data within Datastores effectively

So I’m trying to make a game where every player has their own world where they can build and break blocks like Minecraft and it saves when they leave/rejoin using datastores. I’m just asking how would I efficiently save these blocks without surpassing the 1MB per player storage capacity? As there would be a lot of blocks and block data to save within these worlds.

Edit: Would I be able to manually create the worlds or make a procedurally generated system with a seed and then only store the seed and what the players edit in a table so that when they rejoin, it would load their seed and then go through the table with all the edits they made and perform such edits? Or is there a better way?

serialize data, CFrame, special flags or etc, dont save size since size can be fixed 5studs lets say

item name can just be an enum

local BlockEnums =  {
   Dirt = 1
}

local BlockToSave = {
   id = BlockEnums.Dirt
}

then when u load, u get the enum key from id and set visuals

Is it best to just save the blocks as the player places them? And can I somehow check if a player has reverted a block to its original state and then remove it from the table of blocks the player has changed eg. when they’d break something that’s originally dirt and then place it back I wouldn’t need to store that data a second time?

no, load on world enter, save on player leave.

So, let’s get started. Your algorithm should generate the world using a seed, as this is the only way to ensure that your world can be saved adequately. From here, we can immediately conclude that you need a function that can determine whether a block is located at a specific point in your world’s matrix without physically loading the world, using only the seed.
Additionally, the person above mentioned that you should serialize the data {id: number,position: {num,num,num}
I would like to point out that I would not make the position relative to world coordinates, but relative to your coordinates (for example, the second block on X has a position of X 2, not pos*size
Of course, you shouldn’t store the size of your blocks…

But now let’s get to the “fun” part. Ideally, when saving, you should go through each chunk in which the player was and save only the added/broken blocks, and of course the Seed itself, when loading, you load the Seed, and then go through the saved blocks → you load the supplied ones based on positions and ids, you remove the removed ones accordingly (first delete it, then save it)
Okay, to be honest, this is not “ideal,” but it is “exactly how it should be done,” otherwise your world will be measured in hundreds rather than tens of megabytes.
So, in the end, you get an incredibly huge table of objects when you save it. You should definitely use JsonEncode to convert it to a string. I think a player will place/break about 6,000 blocks in one session, and they will also perform many additional actions that will affect the weight, so I can multiply the final weight by three.

local a = { id=123,pos={100,100,100}} -- 30 bytes
local b = game.HttpService:JSONEncode(a)
local bytesOfUSave = string.len(b) * 6000 * 3 -- 30bytes * 6000times * 3multy
print(bytesOfUSave/1024/1024) -- 0.5 megabytes

So, in the end, you’ll be saving more than just blocks (oh, how much a chest filled with items weighs!), so it’s safe to assume that a single datastore (4mb) won’t be enough for even 3-4 sessions. That’s why people came up with text compression. You should look for the best combination

So when you’ve found the best combination of algorithms, I think you’ve compressed everything enough, but I’ll still write down what else can be done.

  1. Technically, the “id” key itself adds 1 extra byte of weight. Write i and all that changes is the name of the key, and the weight decreases by 1 byte. Do the same with pos → p and get rid of two more bytes (you can use indexes, but it’s less convenient).
  2. group changes by chunks for convenience (16x16x256)
  3. deleted obj is not “deleted” its just air so its not “deleted” its is is 0
  4. instead of pos = 1,2,3 u can do posId = x + zchunkSizeX + ychunkSizeX*chunkSizeZ (encode to one number using hash and store with a single number)
  5. Lazy loading

DONT use datastores

use saveplaceasync()

No.

This method overwrites the previous state of the place. To revert a save, publish an older version of the place.

createplaceasync() then saveplaceasync()…

logic

i will not reply because the documentation exists

No.

yes
Blockate - Roblox

this uses saveplaceasync and on startup requires a model that can be updated to give game functions

Literally just read what I just sent.

re-read what i just sent too

i said Blockate requires a model that can be used to give game functions on startup using saveplaceasync() so you don’t need to update every place

for example you can provide a building module using this, then update the created place using saveplaceasync() every 5 minutes or so

Again, it will save to your inventory/under group.
Even tho, OP said

All of this is overkill.