Hat DataStore not saving all the time

I want to create a hat changing system through GUI, but I believe there’s a problem with my server script that has to do with DataStore.

When pressing on one of my GUI buttons, the hat applies to the player. But, sometimes when I respawn or rejoin, the hat disappears. The only way for me to fix it is by either going to get that specific hat again from the GUI, or respawning until it re-appears.

Is there a flaw in my DataStore approach?

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local HttpService = game:GetService("HttpService")

local HAT_DATASTORE_NAME = "HatCustomizationDataStore"
local HatChangeEvent = ReplicatedStorage:WaitForChild("HatChangeEvent")
local hatDataStore = DataStoreService:GetDataStore(HAT_DATASTORE_NAME)

local function ApplyHat(player, hatName, cframe)
	local character = player.Character
	if not character then return end

	local head = character:FindFirstChild("Head")
	if not head then return end

	for _, child in ipairs(head:GetChildren()) do
		if ServerStorage.HatStorage:FindFirstChild(child.Name) then
			child:Destroy()
		end
	end

	local hat = ServerStorage.HatStorage:FindFirstChild(hatName)
	if hat then
		local clonedHat = hat:Clone()
		clonedHat.Name = hatName
		clonedHat.Parent = head

		clonedHat.CFrame = head.CFrame * cframe

		local weld = Instance.new("WeldConstraint")
		weld.Parent = clonedHat
		weld.Part0 = head
		weld.Part1 = clonedHat
	else
		warn("Hat not found: " .. hatName)
	end
end

local function SaveHatCustomization(player, hatName, cframe)
	local data = {
		Name = hatName,
		Position = {cframe.Position.X, cframe.Position.Y, cframe.Position.Z},
		Rotation = {cframe:ToEulerAnglesXYZ()}
	}

	local success, errorMessage = pcall(function()
		hatDataStore:SetAsync(player.UserId .. "_Hat", HttpService:JSONEncode(data))
	end)

	if not success then
		warn("Failed to save hat for " .. player.Name .. ": " .. errorMessage)
	end
end

local function LoadHatCustomization(player)
	local success, dataString = pcall(function()
		return hatDataStore:GetAsync(player.UserId .. "_Hat")
	end)

	if success and dataString then
		local decodedData = HttpService:JSONDecode(dataString)
		if decodedData and decodedData.Name and decodedData.Position and decodedData.Rotation then
			local position = Vector3.new(unpack(decodedData.Position))
			local rotation = Vector3.new(unpack(decodedData.Rotation))
			local cframe = CFrame.new(position) * CFrame.Angles(rotation.X, rotation.Y, rotation.Z)
			return decodedData.Name, cframe
		end
	end

	return nil, CFrame.new(0, 0.5, 0)
end

Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		while not character or not character:FindFirstChild("HumanoidRootPart") do
			task.wait()
		end

		local hatName, cframe = LoadHatCustomization(player)
		if hatName then
			ApplyHat(player, hatName, cframe)
		end
	end)
end)

HatChangeEvent.OnServerEvent:Connect(function(player, hatName, cframe)
	if ServerStorage.HatStorage:FindFirstChild(hatName) then
		ApplyHat(player, hatName, cframe)
		SaveHatCustomization(player, hatName, cframe)
	else
		warn("Invalid hat name received: " .. hatName)
	end
end)

1 Like

This is most likely because you make too many datastore requests that some get dropped. Tbh you should have like a session data table that you update and then when they leave you can save their session data. This cuts down requests to only 2 per session (joining and leaving)

I’ve done something similar with session data stuff… Check this post out:

1 Like

Yeah, the reason was from too many datastore requests. I ended up using all the customization scripts and putting them together under one datastore, with each using different RemoteEvents. Thank you.

1 Like