When player leaves the data does get collected correctly but when the player rejoins the :GetAsync returns nil

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local ServerStorage = game:GetService("ServerStorage")
local carCustomizationsData = DataStoreService:GetDataStore("CarCustomizationsData")
local events = ReplicatedStorage:WaitForChild("Events")

local gameData = require(ReplicatedStorage:WaitForChild("GameData"))
local saveCarEvent = events.SaveCar
local purchasedCarsFolder = ServerStorage.PurchasedCars

events.BuyCarUpgrade.OnServerEvent:Connect(function(player, amount, selectedPart, car, whatToChange)
	local moneyValue = player:WaitForChild("leaderstats"):WaitForChild("$")

	for _, carPart in car:GetDescendants() do
		if carPart.Name ~= selectedPart then continue end

		if carPart:IsA("Model") then
			for _, bpart in carPart:GetDescendants() do
				if not bpart:IsA("BasePart") or not bpart:IsA("MeshPart") then continue end
				for propertyName, value  in whatToChange do
					bpart[propertyName] = value
				end
			end
		else
			for propertyName, value  in whatToChange do
				selectedPart[propertyName] = value
			end
		end
	end

	moneyValue.Value -= amount
end)

local function FindFirstSimilarChild(haystack, needle)
	local wordsTable = string.split(needle, " ")
	for _, car in haystack:GetChildren() do
		local similarities = 0
		local carWordsTable = string.split(car.Name, " ")

		for _, word in carWordsTable do
			if table.find(wordsTable, word) then
				similarities += 1
			end
		end
		if similarities > 1 then
			return car
		end
		similarities = 0
	end
	return nil
end

game.Players.PlayerAdded:Connect(function(player)
	local playerCarInventory = purchasedCarsFolder:WaitForChild(player.Name)

	local playerUserId = "Player_"..player.UserId
	local dataToLoad = {}
	local success, errorMessage = pcall(function()
		dataToLoad = carCustomizationsData:GetAsync(playerUserId)
	end)
	
	if success and dataToLoad then
		for carName, carParts in dataToLoad do

			local carToModify = playerCarInventory:FindFirstChild(carName)
			if not carToModify then warn("The car that was saved last time doesnt exist in player's inventory no more") continue end
			print(carName)

			for carPartName, properties in carParts do
				for _, carPart in carToModify:GetDescendants() do
					if carPartName ~= carPart then continue end

					print(carPart.Name)
					if carPart:IsA("BasePart") or carPart:IsA("MeshPart") then

						for propertyName, propertyValue in properties do
							carPart[propertyName] = propertyValue
							print("Changed Property of: "..carPart.Name..", and the property was: "..propertyName.." to the ".. propertyValue)
						end
					elseif carPart:IsA("Model") then

						for _, otherCarPart in carPart:GetChildren() do
							if not otherCarPart:IsA("BasePart") and not otherCarPart:IsA("MeshPart") then continue end

							for propertyName, propertyValue in properties do
								otherCarPart[propertyName] = propertyValue
							end
						end
					end
				end
			end
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local playerCarInventory = purchasedCarsFolder:WaitForChild(player.Name)

	local playerUserId = "Player_"..player.UserId
	local dataToSave = {}
	for _, car in playerCarInventory:GetChildren() do
		dataToSave[car.Name] = {}

		for _, carPart in car:GetDescendants() do
			if dataToSave[car.Name][carPart.Name] or not string.find(string.lower(carPart.Name), "paint") then continue end
			dataToSave[car.Name][carPart.Name] = {}

			if carPart:IsA("BasePart") or carPart:IsA("MeshPart") then
				for _, propertyName in gameData.ChangableProperties do
					dataToSave[car.Name][carPart.Name][propertyName] = carPart[propertyName] -- the value of the property
				end
			elseif carPart:IsA("Model") then
				for _, otherCarPart in carPart:GetChildren() do
					if otherCarPart:IsA("MeshPart") or otherCarPart:IsA("BasePart") then 

						for _, propertyName in gameData.ChangableProperties do
							dataToSave[car.Name][carPart.Name][propertyName] = otherCarPart[propertyName] -- the value of the property
						end
					end
					break
				end
			end
		end
	end

	print(dataToSave)
	pcall(function()
		carCustomizationsData:SetAsync(playerUserId, dataToSave)
	end)
end)

saveCarEvent.OnServerInvoke = function(player, carToSave, exitTpPart)
	local character = player.Character
	local humanoid = character:WaitForChild("Humanoid")

	local playerCarInventory = purchasedCarsFolder:FindFirstChild(player.Name)
	if not playerCarInventory then warn(`Inventory for {player.Name} in purchasedCars doesn't exist.`) return end

	local carToUpdate = FindFirstSimilarChild(playerCarInventory, carToSave:FindFirstChild("CarName").Value)
	if not carToUpdate then warn(`Couldn't find {carToSave.Name} in player's inventory`) return end

	local updatedParts = {}
	for _, carPart in carToUpdate:GetDescendants() do
		if not string.find(string.lower(carPart.Name), "paint") or table.find(updatedParts, carPart) then continue end
		if carPart:IsA("BasePart") or carPart:IsA("MeshPart") then

			for _, carPartOld in carToSave:GetDescendants() do
				if carPartOld.Name ~= carPart.Name then continue end
				if carPartOld:IsA("BasePart") or carPartOld:IsA("MeshPart") then
					carPart.Color = carPartOld.Color
					carPart.Transparency = carPartOld.Transparency
					carPart.Reflectance = carPartOld.Reflectance
				elseif carPartOld:IsA("Model") then
					for _, otherCarPart in carPartOld:GetChildren() do
						if not carPartOld:IsA("BasePart") or not carPartOld:IsA("MeshPart") then continue end
						carPart.Color = carPartOld.Color
						carPart.Transparency = carPartOld.Transparency
						carPart.Reflectance = carPartOld.Reflectance
					end
				end
			end
		elseif carPart:IsA("Model") then
			for _, moreOtherCarPart in carPart:GetChildren() do

				for _, carPartOld in carToSave:GetDescendants() do
					if carPartOld.Name ~= moreOtherCarPart.Name then continue end
					if carPartOld:IsA("BasePart") or carPartOld:IsA("MeshPart") then
						moreOtherCarPart.Color = carPartOld.Color
						moreOtherCarPart.Transparency = carPartOld.Transparency
						moreOtherCarPart.Reflectance = carPartOld.Reflectance
					elseif carPartOld:IsA("Model") then
						for _, otherCarPart in carPartOld:GetChildren() do
							if not carPartOld:IsA("BasePart") or not carPartOld:IsA("MeshPart") then continue end
							moreOtherCarPart.Color = carPartOld.Color
							moreOtherCarPart.Transparency = carPartOld.Transparency
							moreOtherCarPart.Reflectance = carPartOld.Reflectance
						end
					end
				end
			end
		end
	end

	player:LoadCharacter()
	carToSave:Destroy()

	player.CharacterAppearanceLoaded:Wait()
	character.HumanoidRootPart.CFrame = exitTpPart.CFrame
end
1 Like

Have you tried printing out the data to save or seeing if there is an error when saving?

Use a BindToClose() function. This ensures the game has enough time to save.

locak runService = game:GetService("RunService")
game:BindToClose(function()
    if runService:IsStudio() then task.wait(5) return end
    for _, player in pairs(game.Players:GetPlayers() do
        --save
    end
    task.wait(5)
end)

It’s also worth putting your save and load functions as seperate functions so they can be called more easily from the closing function.

local function save(player:Player)
end

local function load(player:Player)
end

game.Players.PlayerAdded:Connect(load)
game.Players.PlayerRemoving:Connect(save)

game:BindToClose(function()
    if runService:IsStudio() then task.wait(5) return end
    for _, player in pairs(game.Players:GetPlayers()) do
        save(player)
    end
end)

i did, there are no errors during :SetAsync function

And what does the data to save variable print out?

it prints out everything correctly, here is the picture:
image

the first two messages are from playerAdded function

could you match it with my script please

Like I said above, you need a BindToClose() function. No errors and no save data sounds like the game shuts down before it has time to write the data into the store.

1 Like

When you say the player leaves and it collects their data correctly, that makes sense to me. But what do you mean by the GetAsync method from PlayerAdded returns nil? Have you printed the data and it returns nil?

Also maybe let me know real quick all of the things you’ve tried so we can figure out what else the problem may be.

Also a few questions:

  1. Have you tried printing the data retrieved from playeradded to see if it loaded all the data
  2. Have you tested in studio only? Studio may close too fast for a PlayerRemoved function to run everything.

i tried printing the error message and the data gotten from getAsync()
image
and i have tried testing in the actual game too. it does the same thing

image
still returns nil.

current script:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local ServerStorage = game:GetService("ServerStorage")
local RunService = game:GetService("RunService")
local carCustomizationsData = DataStoreService:GetDataStore("CarCustomizationsData")
local events = ReplicatedStorage:WaitForChild("Events")

local gameData = require(ReplicatedStorage:WaitForChild("GameData"))
local saveCarEvent = events.SaveCar
local purchasedCarsFolder = ServerStorage.PurchasedCars

events.BuyCarUpgrade.OnServerEvent:Connect(function(player, amount, selectedPart, car, whatToChange)
	local moneyValue = player:WaitForChild("leaderstats"):WaitForChild("$")

	for _, carPart in car:GetDescendants() do
		if carPart.Name ~= selectedPart then continue end

		if carPart:IsA("Model") then
			for _, bpart in carPart:GetDescendants() do
				if not bpart:IsA("BasePart") or not bpart:IsA("MeshPart") then continue end
				for propertyName, value  in whatToChange do
					bpart[propertyName] = value
				end
			end
		else
			for propertyName, value  in whatToChange do
				selectedPart[propertyName] = value
			end
		end
	end

	moneyValue.Value -= amount
end)

local function FindFirstSimilarChild(haystack, needle)
	local wordsTable = string.split(needle, " ")
	for _, car in haystack:GetChildren() do
		local similarities = 0
		local carWordsTable = string.split(car.Name, " ")

		for _, word in carWordsTable do
			if table.find(wordsTable, word) then
				similarities += 1
			end
		end
		if similarities > 1 then
			return car
		end
		similarities = 0
	end
	return nil
end

local function save(player)
	local playerCarInventory = purchasedCarsFolder:WaitForChild(player.Name)

	local playerUserId = "Player_"..player.UserId
	local dataToSave = {}
	for _, car in playerCarInventory:GetChildren() do
		dataToSave[car.Name] = {}

		for _, carPart in car:GetDescendants() do
			if dataToSave[car.Name][carPart.Name] or not string.find(string.lower(carPart.Name), "paint") then continue end
			dataToSave[car.Name][carPart.Name] = {}

			if carPart:IsA("BasePart") or carPart:IsA("MeshPart") then
				for _, propertyName in gameData.ChangableProperties do
					dataToSave[car.Name][carPart.Name][propertyName] = carPart[propertyName] -- the value of the property
				end
			elseif carPart:IsA("Model") then
				for _, otherCarPart in carPart:GetChildren() do
					if otherCarPart:IsA("MeshPart") or otherCarPart:IsA("BasePart") then 

						for _, propertyName in gameData.ChangableProperties do
							dataToSave[car.Name][carPart.Name][propertyName] = otherCarPart[propertyName] -- the value of the property
						end
					end
					break
				end
			end
		end
	end

	print(dataToSave)
	pcall(function()
		carCustomizationsData:SetAsync(playerUserId, dataToSave)
	end)
end

local function load(player)
	local playerCarInventory = purchasedCarsFolder:WaitForChild(player.Name)

	local playerUserId = "Player_"..player.UserId
	local dataToLoad = {}
	local success, errorMessage = pcall(function()
		dataToLoad = carCustomizationsData:GetAsync(playerUserId)
	end)
	warn("Error message: "..tostring(errorMessage))
	print(dataToLoad)

	if success and dataToLoad then
		for carName, carParts in dataToLoad do

			local carToModify = playerCarInventory:FindFirstChild(carName)
			if not carToModify then warn("The car that was saved last time doesnt exist in player's inventory no more") continue end
			print(carName)

			for carPartName, properties in carParts do
				for _, carPart in carToModify:GetDescendants() do
					if carPartName ~= carPart then continue end

					print(carPart.Name)
					if carPart:IsA("BasePart") or carPart:IsA("MeshPart") then

						for propertyName, propertyValue in properties do
							carPart[propertyName] = propertyValue
							print("Changed Property of: "..carPart.Name..", and the property was: "..propertyName.." to the ".. propertyValue)
						end
					elseif carPart:IsA("Model") then

						for _, otherCarPart in carPart:GetChildren() do
							if not otherCarPart:IsA("BasePart") and not otherCarPart:IsA("MeshPart") then continue end

							for propertyName, propertyValue in properties do
								otherCarPart[propertyName] = propertyValue
							end
						end
					end
				end
			end
		end
	end
end

game.Players.PlayerAdded:Connect(load)
game.Players.PlayerRemoving:Connect(save)

game:BindToClose(function()
	if RunService:IsStudio() then task.wait(5) return end
	for _, player in pairs(game.Players:GetPlayers()) do
		save(player)
	end
end)

saveCarEvent.OnServerInvoke = function(player, carToSave, exitTpPart)
	local character = player.Character
	local humanoid = character:WaitForChild("Humanoid")

	local playerCarInventory = purchasedCarsFolder:FindFirstChild(player.Name)
	if not playerCarInventory then warn(`Inventory for {player.Name} in purchasedCars doesn't exist.`) return end

	local carToUpdate = FindFirstSimilarChild(playerCarInventory, carToSave:FindFirstChild("CarName").Value)
	if not carToUpdate then warn(`Couldn't find {carToSave.Name} in player's inventory`) return end

	local updatedParts = {}
	for _, carPart in carToUpdate:GetDescendants() do
		if not string.find(string.lower(carPart.Name), "paint") or table.find(updatedParts, carPart) then continue end
		if carPart:IsA("BasePart") or carPart:IsA("MeshPart") then

			for _, carPartOld in carToSave:GetDescendants() do
				if carPartOld.Name ~= carPart.Name then continue end
				if carPartOld:IsA("BasePart") or carPartOld:IsA("MeshPart") then
					carPart.Color = carPartOld.Color
					carPart.Transparency = carPartOld.Transparency
					carPart.Reflectance = carPartOld.Reflectance
				elseif carPartOld:IsA("Model") then
					for _, otherCarPart in carPartOld:GetChildren() do
						if not carPartOld:IsA("BasePart") or not carPartOld:IsA("MeshPart") then continue end
						carPart.Color = carPartOld.Color
						carPart.Transparency = carPartOld.Transparency
						carPart.Reflectance = carPartOld.Reflectance
					end
				end
			end
		elseif carPart:IsA("Model") then
			for _, moreOtherCarPart in carPart:GetChildren() do

				for _, carPartOld in carToSave:GetDescendants() do
					if carPartOld.Name ~= moreOtherCarPart.Name then continue end
					if carPartOld:IsA("BasePart") or carPartOld:IsA("MeshPart") then
						moreOtherCarPart.Color = carPartOld.Color
						moreOtherCarPart.Transparency = carPartOld.Transparency
						moreOtherCarPart.Reflectance = carPartOld.Reflectance
					elseif carPartOld:IsA("Model") then
						for _, otherCarPart in carPartOld:GetChildren() do
							if not carPartOld:IsA("BasePart") or not carPartOld:IsA("MeshPart") then continue end
							moreOtherCarPart.Color = carPartOld.Color
							moreOtherCarPart.Transparency = carPartOld.Transparency
							moreOtherCarPart.Reflectance = carPartOld.Reflectance
						end
					end
				end
			end
		end
	end

	player:LoadCharacter()
	carToSave:Destroy()

	player.CharacterAppearanceLoaded:Wait()
	character.HumanoidRootPart.CFrame = exitTpPart.CFrame
end

Instead of just a basic pcall(), try using that to check for an error.

local success, err = pcall(function()
    --code here
end)

if success then
    print("it worked")
elseif not success and err then
    warn(err)
    warn("Failed")
end

Sometimes I find that internal server errors don’t display.
Let me know on what the output is.


i found one of the prblems

Oh, I know the issue. The DataStore does accept dictionaries, but they can only contain numbers, strings, or booleans. Check if you are trying to save any instances or anything else that isn’t one of those things.

image

It might be the colour. Are you trying to save a Color3 value?

you are right. i am trying to store color3. my friend recommended me to convert it into a table

Yeah, that’s the problem. I’d suggest saving each value of the Color3 in a string (then concatenate it all into one string with spaces between the colour values), saving that, and loading it back on join.

Say you had a string of the colours (RGB example):
local colourString = "24 56 79"
you can split it to reconfigure the colour:

local splitted = colourString:Split(" ")
local c1 = tonumber(splitted[1])
local c2 = tonumber(splitted[2])
local c3 = tonumber(splitted[3])
local newColour = Color3.fromRGB(c1, c2, c3)

I’m pretty sure you could do the same thing with your decimal numbers, but you might just need to do Color3.new()

1 Like

fixing right now.

1 Like