Datastores not saving

I have a tycoon game where you can place props. But there is an issue where when you leave the game, it doesn’t save(also doesn’t error). It just loads the same save every single time.

I have looked online and stuff and nothing was there that helped me.

Here is the code that runs when you claim a plot:

--Loading
local DatastoreService = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local plotStore = DatastoreService:GetDataStore("PlotSaves")

script.Parent.Touched:Connect(function(t)
	if t.Parent:FindFirstChild("Humanoid") then
		local p = game.Players:GetPlayerFromCharacter(t.Parent)
		if script.Parent.Parent.Owner.Value == game.Players and p.PlotOwned.Value == nil then
			script.Parent.Parent.Owner.Value = p
			local message = Instance.new("Hint")
			message.Parent = p.PlayerGui
			message.Text = "You now own " .. script.Parent.Parent.Name
			task.wait(2.5)
			message.Text = "Loading your last progress()"
			
			local success, tables = pcall(function()
				return plotStore:GetAsync(p.UserId)
			end)
			
			if success then
				if tables ~= nil then
					local decodedtable = HttpService:JSONDecode(tables)
					message.Text = "Loading your last progress(1/" .. #decodedtable .. ")"
					for i = 1, #decodedtable do
						message.Text = "Loading your last progress(" .. i .. "/" .. #decodedtable .. ")"
						local rawtable = decodedtable[i]:split("|")
						print("Name:",rawtable[3],"Position:",rawtable[1],"Orientation:",rawtable[2])
						local part = game.ReplicatedStorage.Parts[rawtable[3]]
						part:SetPrimaryPartCFrame(CFrame.new(script.Parent.Parent.Plot1.Position + Vector3.new(rawtable[1]:match("(.+), (.+), (.+)"))))
						local orientvector = Vector3.new(rawtable[2]:match("(.+), (.+), (.+)"))
						part:SetPrimaryPartCFrame(part.PrimaryPart.CFrame * CFrame.Angles(math.rad(orientvector.X), math.rad(orientvector.Y), math.rad(orientvector.Z)))
						part.Parent = script.Parent.Parent.blocks
						task.wait(0.1)
					end
				end
			end
			
			p.PlotOwned.Value = script.Parent.Parent
			wait(1)
			message:Destroy()
		else
			print("someone owns this plot")
		end
	end
end)

Here is the code that runs when you leave the game:

local DatastoreService = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local plotStore = DatastoreService:GetDataStore("PlotSaves")


game.Players.PlayerRemoving:Connect(function(p)
	print("player left")
	if p.PlotOwned.Value ~= nil then
		print("they own a plot")
		p.PlotOwned.Value.Plot1Sign.SurfaceGui.TextLabel.Text = "Owned By: No One"
		p.PlotOwned.Value.Owner.Value = game.Players
		local testArray = {}
		local blocks = p.PlotOwned.Value.blocks:GetChildren()

		for i = 1, #blocks do
			p.PlotOwned.Value.Plot1.Attachment.WorldPosition = blocks[i].PrimaryPart.Position
			table.insert(testArray, i, tostring(p.PlotOwned.Value.Plot1.Attachment.Position) .. "|" .. tostring(blocks[i].PrimaryPart.Orientation).. "|" .. tostring(blocks[i].Name))
			print(testArray[i])
			blocks[i]:Destroy()
			task.wait(0.5)
		end
		
		
		
		local success, errorMessage = pcall(function()
			plotStore:SetAsync(p.UserId, HttpService:JSONEncode(testArray))
			print(testArray)
		end)
		
		if not success then
			print(errorMessage)
		end
	end
end)

I have a feeling that my script is fine as it saved the first time. But now it loads the same thing every single time.
Ask me any questions as I have a feeling some people are gonna say its messy.

1 Like

PlayerRemoving rarely fires in Studio, you should instead hook it up into game:BindToClose, when saving.

should I use bindtoclose when not in studio?

Yes, since if your server shuts down, it would connect to game:BindToClose and ensure that data isn’t lost.

yeah this didn’t seem to fix it. this also happens ingame as well

local success, tables = pcall(function()
	return plotStore:GetAsync(p.UserId)
end)

This is wrong, success actually returns the table. Try this:

local tables
local success, err = pcall(function()
	tables = plotStore:GetAsync(p.UserId)
end)

if success and tables then

still doesn’t work…
30charrrrrs

I think you are trying to convert position into string and saving it?
Theres 2 things I’ve noticed as a problem, it can be position and orientation that are not saving or wait(0.5)

Possible Problem 1: Position & Orientation

Position and Orientation cannot be saved in datastore, only strings,tables,bools,numbers
You can try serializing the part position and orientation

Example:

local testArray = {}
local blocks = p.PlotOwned.Value.blocks:GetChildren()

for index, part in pairs(blocks) do
    if part:IsA("BasePart") or part:IsA("MeshPart") or part:IsA("Part") then
      testArray[part.Name] = {
        Position = {X = part.Position.X, Y = part.Position.Y, Z = part.Position.Z},
        Orientation = {X = part.Orientation.X, Y = part.Orientation.Y, Z = part.Orientation.Z}
       }
      part:Destroy()
      -- Don't use `wait()` since it will loop through parts one by one and it will take a lot of time to add items in the table.
   end
end

local success, err = pcall(function()
    plotStore:UpdateAsync(p.UserId, function(oldData)
         return HttpService:JSONEncode(testArray)
    end)
end)

if err then error(err) end
Possible Problem 2: task.wait(0.5)

You have used task.wait(.5) this means each items needs 0.5 seconds get inserted in the table so if player leaves before all the parts data saving in the table it will not save since your data saves until the loop ends

so try removing wait:

local testArray = {}
		local blocks = p.PlotOwned.Value.blocks:GetChildren()

		for i = 1, #blocks do
			p.PlotOwned.Value.Plot1.Attachment.WorldPosition = blocks[i].PrimaryPart.Position
			table.insert(testArray, i, tostring(p.PlotOwned.Value.Plot1.Attachment.Position) .. "|" .. tostring(blocks[i].PrimaryPart.Orientation).. "|" .. tostring(blocks[i].Name))
			print(testArray[i])
			blocks[i]:Destroy()
		end

thats why I converted them into strings as seen here:

table.insert(testArray, i, tostring(offsetvector) .. "|" .. tostring(blocks[i].PrimaryPart.Orientation).. "|" .. tostring(blocks[i].Name))

and i convert back into vector3’s here:

part:SetPrimaryPartCFrame(CFrame.new(script.Parent.Parent.Plot1.Position + Vector3.new(rawtable[1]:match("(.+), (.+), (.+)"))))

OK GUYS ITS FINALLY WORKING! Thanks for all your help.

Can you please mention what was the problem? so others who have the same problem will get the solution.

1 Like

Idk why but it just started working out of nowhere. I changed it to updateasync so maybe it’s that