Saving Models in a DataStore

I’m making a Character Customization UI and I’d like to know how I could save a model (specifically the character) in a DataStore.

You can only store numbers, strings, and tables in a datastore. That means in order to ‘save’ a model you would have to use a process called serialization where instead of saving the model itself you save all the information about it and then you need code that can decipher the information and turn it back into the model.

Check out this tutorial.

Like @RiccoMiller said, you can only save strings, numbers, tables to a DataStore (and Booleans). In order to serialise it into a table, you might want to consider:

  • Saving the MeshId for body parts
  • Saving clothing and accessory IDs, instance classes and names
  • Character scale

Or, you may be able to serialise just the humanoid’s description, which might make it easier.
You can get the description by using Humanoid:GetAppliedDescription(), and apply a description using Humanoid:ApplyDescription().

Whatever you may have heard, you do NOT need to HttpService:JSONEncode() a table to save it to a DataStore, thought I’d put that out there

1 Like

the easiest way is to set a number to 0 when the model isnt unlocked or achieved, and 1 for the opposite. but this is very simple and maybe not so efficient but it works

It’s not possible to save instances in a DataStore. The best alternative is to save the model name, and while unpacking the data to search the model in a folder with every usable customization thing and then apply it to the character.

What is the best way to serialize HumanoidDescription?

I’m trying to use :JSONEncode() but it doesn’t really seem to be working.

Try with unique ids, you have to generate an unique for all the players, so you have to search if the id is existent AND the owner (parent) of that id is the player.

Model:AddTag(numbers)
print(Model:GetTags())

To save character customization information in DataStore, you can try something like this.

Extract HumanoidDescription Properties with Validity Checks:

local function serializeHumanoidDescription(humanoidDescription)
	-- List of all properties that a HumanoidDescription can have, add more if I'm missing something.
	local properties = {
		"BackAccessory", "BodyTypeScale", "ClimbAnimation", "DepthScale", "Face", "FaceAccessory",
		"FallAnimation", "FrontAccessory", "GraphicTShirt", "HairAccessory", "HatAccessory", "Head",
		"HeadColor", "HeadScale", "HeightScale", "IdleAnimation", "JumpAnimation", "LeftArm",
		"LeftArmColor", "LeftLeg", "LeftLegColor", "NeckAccessory", "Pants", "ProportionScale",
		"RightArm", "RightArmColor", "RightLeg", "RightLegColor", "RunAnimation", "Shirt",
		"ShouldersAccessory", "SwimAnimation", "Torso", "TorsoColor", "WaistAccessory",
		"WalkAnimation", "WidthScale"
	}

	local serializedDescription = {}
	for _, property in ipairs(properties) do
		if humanoidDescription[property] ~= nil then
			serializedDescription[property] = humanoidDescription[property]
		end
	end

	return serializedDescription
end

-- Example usage:
local Humanoid = nil -- Path of Player's Humanoid (example workspace.Player1.Humanoid)
local humanoidDescription = Humanoid:FindFirstChildOfClass("HumanoidDescription")
if humanoidDescription then
	print(serializeHumanoidDescription(humanoidDescription))
else
	print("HumanoidDescription not found")
end

Convert to JSON and Save to DataStore:

local HttpService = game:GetService("HttpService")
local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("CharacterCustomizations")

local function saveCharacter(player, humanoidDescription)
    local serializedDescription = serializeHumanoidDescription(humanoidDescription)
    local json = HttpService:JSONEncode(serializedDescription)
    
    -- Save to DataStore
    local success, errorMessage = pcall(function()
        dataStore:SetAsync(player.UserId .. "_HumanoidDescription", json) -- Using PlayerID_HumanoidDescription as Key
    end)
    
    if not success then
        warn("Failed to save character: " .. errorMessage)
    end
end

Load from DataStore and Apply with Validity Checks:

-- Load info from datastore and then apply it on the character.

That won’t work. You used HttpService:JSONEncode() to turn the table into a JSON string, presumably to get around not being able to save things like Color3 values. However, those items within the JSON string are null, which means when you go to decode the JSON string, they aren’t included.

Pre-serialising Color3 values into their own table first and then de-serialising them with Color3.new() could solve this issue.

Building on your iteration:

for _, property:string in next, properties, nil do
    if not humanoidDescription[property] then continue end
    
    local data: any = humanoidDescription[property]
    
    if typeof(data) == "Color3" then
        data = {
            ["R"] = data.R,
            ["G"] = data.G,
            ["B"] = data.B
        } :: {[string]: number}
    end

    serialisedDescription[property] = data
end




On a side note, UpdateAsync would be better for data saving, even without implementing extra code into the transform function.

edit: ok so it turns out as soon as I provided this workaround edit you replied with your solution to this approach lol

You can try Serialization Approach:

Convert Color3 values into a table format that can be serialized using HttpService:JSONEncode(). For example:

-- Example
local vector = Vector3.new(1, 2, 3)
local serializedVector = {
    x = vector.x,
    y = vector.y,
    z = vector.z
}
local jsonVector = HttpService:JSONEncode(serializedVector)

Decoding Approach:

  • Reconstruct Color3, Vector3, and CFrame objects using the decoded values.

P.S: Seems like you already figured it out before I posted this reply.

Make various datastores. One for the characters hair, one for the characters face, etc. Save each of them seperately, then have a script that gives the player those avatar items when they join.

Using various data stores isn’t the best idea…

Not only will it eat up the data budget really fast, it will also create more places saving can error. It also slows down data saving and retrieval, and overall is a greater inconvenience. There’s no real benefit to this over serialisation, especially because it’s incredibly unlikely for a character’s information to exceed the 4MB limit on data stores.