How could I make a datastore that saves a folder (more like several folders)
and loads it back in properly when rejoined without having to use a third party datastore?
I’ve tried things like JSONEncoding/Decoding and just basic table saving but you need UTF-8 characters. When I tried it with JSONEncoding/Decoding it obviously said that it’s over the max limit of characters it can handle.
Here’s my current code:
local dss = game:GetService("DataStoreService"):GetDataStore("****")
local BaseData = game.Workspace.IslandFolder.TerrainParts.Grass
local BaseDataSaving = {}
game.Players.PlayerAdded:Connect(function(player)
local GenerateTerrainUI = player.PlayerGui:WaitForChild("GenerateIslandUI").TerrainSettings.GenerateTerrain
local success, err = pcall(function()
return dss:GetAsync(player.UserId)
end)
if success then
GenerateTerrainUI.Visible = false
for i,v in pairs(err) do
local GrassPart = Instance.new("Part", BaseData)
GrassPart.Name = v.Name
GrassPart.Size = v.Size
GrassPart.Position = v.Position
end
else
GenerateTerrainUI.Visible = true
print(err)
end
end)
game.Players.PlayerRemoving:Connect(function(player)
for i,v in pairs(BaseData:GetChildren()) do
local PartInfo = {
["Name"] = v.Name;
["Size"] = v.Size;
["Position"] = v.Position
}
table.insert(BaseDataSaving,PartInfo)
end
dss:SetAsync(player.UserId, BaseDataSaving)
end)
For every single block you’re storing the strings “Name”, “Size” and “Position”, as well as whatever v.Name is. To save a string you need 1 byte per character. This is incredibly wasteful. Instead of the name, store the “block type” as an integer. With just 1 byte you can then save 2^8 = 256 different block types. You don’t need to have the keys be strings either, you just need the first value to be the name, second value to be the size and third value to be the position.
All you need to save is a number and a serialized cframe. The number will represent the block type and the size. You can make a table that matches each number to their block type like this:
local items = {blockType1Size1, blockType1Size2, blockType2Size1} -- etc
-- and then access it like this:
local block = items[3] -- example number
-- which is the key of the actual block in the items table
Then you have to save the CFrame. The easiest (not the best) way to do this is to add the components to a table like this:
local serialized = {cframe:GetComponents()}
-- turn it back into a cframe:
CFrame.new(table.unpack(serialized))
If you’re not rotating any of the blocks and you only need a position instead of a CFrame, you can use a Vector3 instead. Use Vector3 if you can because it takes up less data (it’s 3 numbers instead of 12 in the table).
local serialized = {vector.X, vector.Y, vector.Z}
-- turn it back into a Vector3:
Vector3.new(table.unpack(serialized))
To save the part’s full data, you can make the key the type in the final table and the value the serialized position/cframe. In this example i’ll use position (Vector3).
local items = {name1Size1, name2Size2, name3Size1}
local savedObjects = {}
for _, part in ipairs(partfolder:GetChildren()) do
-- if all blocks are cubes then x = y = z so you only need x
local index = table.find(items, part.Name .. "Size" .. part.Size.X)
savedObjects[index] = {part.Position.X, part.Position.Y, part.Position.Z}
end
-- then to deserialize:
for i, position in pairs(savedObjects) do
local name = items[i]
local block = blahblahgetblockfromname(name):Clone()
block.Position = Vector3.new(table.unpack(position))
block.Parent = partfolder
end