Data not saved when using game.BindToClose() and TeleportAsync()

Hi guys. I have game with multiple DataStores active. In my case, there’s 3: Inventory, RoundIsland, LobbyIsland. Inventory uses 1 principle, Islands another one. There’s their saving code part:

Saving codes
--ISLAND SAVING

--TestInStudio is false for live game, used to load data on RobloxStudio
--LoadAsPlayer used only in RobloxStudio to inspect other people's saves
function ModuleMeta:Save(PlayerLeft, FastSave)
	if TestInStudio or not RunService:IsStudio() then
		local PlayerName = self.Player.Name
		local PlayerId = self.Player.UserId 
		local DataStoreKey
		if RunService:IsStudio() then
			if LoadAsPlayer then
				DataStoreKey = tostring(LoadAsPlayer) .. "/"  .. tostring(self.SaveSlot) .. "/" .. tostring(self.PlotType) .. "/" .. tostring(self.ActiveIsland)
			else
				DataStoreKey = "Studio/" .. tostring(PlayerId) .. "/" .. tostring(self.SaveSlot) .. "/" .. tostring(self.PlotType) .. "/" .. tostring(self.ActiveIsland)
			end
		else
			DataStoreKey = tostring(PlayerId) .. "/" .. tostring(self.SaveSlot) .. "/" .. tostring(self.PlotType) .. "/" .. tostring(self.ActiveIsland)
		end
		local Success

		repeat
			if not FastSave then
				WaitForRequestBudget(Enum.DataStoreRequestType.SetIncrementAsync)
			end
			self:PrepareObjects()
			local DataToSave = TableDeepclone(self.Data)
			for Key, Data in pairs(DataToSave) do
				if Data.ActivatedController then
					if Data.ActivatedController.GetDataForSave then
						Data.SavedData = Data.ActivatedController:GetDataForSave()
					end
					Data.ActivatedController = nil
				end
			end
			local SerializedData = Serializator.Serealize(DataToSave, self.DataVersion)
			Success = pcall(function()
				DataStore:SetAsync(DataStoreKey, HttpService:JSONEncode({
					Data = SerializedData,
					Session = not PlayerLeft and os.time() or nil,
					DataVersion = self.DataVersion,
				}), {})
			end)
		until Success
	else
		warn("Testing data saving is not enabled.")
	end
end
--Used to get data of various objects, like chests.
function ModuleMeta:PrepareObjects()
	if self.PlotType == "Lobby" then
		for Index, ObjectData in pairs(self.Data) do
			if ObjectData.SavedData and ObjectData.ActivatedController and ObjectData.ActivatedController.GetDataForSave then
				ObjectData.SavedData = ObjectData.ActivatedController:GetDataForSave()
			end
		end
	end
end

Inventory saving is almost identical. Only diffirence is values saved:

		repeat
			if not FastSave then
				WaitForRequestBudget(Enum.DataStoreRequestType.SetIncrementAsync)
			end
			Success = pcall(function()
				return DataStore:SetAsync(DataStoreKey, HttpService:JSONEncode({
					Data = Serializator.Serealize(self.Data, self.DataVersion),
					Gamepasses = self.Gamepasses,
					Pentons = self.Pentons,
					Septons = self.Septons,
					Session = not PlayerLeft and os.time() or nil,
					DataVersion = self.DataVersion,
					ActiveIsland = self.ActiveIsland,
					Level = self.Level,
					Experience = self.Experience,
					Blueprints = self.Blueprints,
					CraftingTool = self.CraftingTool,
					ActiveQuests = CopiedQuests,
					CompletedQuests = self.CompletedQuests,
					IslandData = self.IslandData,
				}))
			end)
		until Success

This 2 code pieces work well. They save all data I need. And they are called from another script, at this code piece:

Load and Save code
local PlayersData = {}

local function LoadData(Player)
	local InventoryData = Modules.Inventory.Initialize(Player)
	PlayersData[Player] = {
		Inventory = InventoryData,
	}
end

local function SaveData(Player, PlayerLeft, Immediate, Subject)
	if string.match(Subject, "Inventory") then
		PlayersData[Player].Inventory:Save(PlayerLeft, Immediate)
	end
	if string.match(Subject, "Island") then
		for Key, Value in pairs(PlayersData[Player]) do
			-- there's only DataStores and strings possible, I need to save DataStores only
			if Key ~= "Inventory" and typeof(Value) ~= "string" then 
				Value:Save(PlayerLeft, Immediate)
			end
		end
	end
end

for _, Player in ipairs(Players:GetPlayers()) do
	coroutine.wrap(LoadData)(Player)
end

local function ServerShutdown()
	if RunService:IsStudio() then
		for _, Player in ipairs(Players:GetPlayers()) do
			coroutine.wrap(SaveData)(Player, true, true, "InventoryIsland")
		end
		wait(5)
	else
		local MainThread = coroutine.running()
		local ThreadsRunning = 0
		for Player, Data in pairs(PlayersData) do
			if Data == nil then continue end
			ThreadsRunning += 1
			coroutine.wrap(function()
				SaveData(Player, true, true, "InventoryIsland")
				PlayersData[Player] = nil
				ThreadsRunning -= 1
				if ThreadsRunning == 0 then
					coroutine.resume(MainThread)
				end
			end)()
		end
		if ThreadsRunning > 0 then
			coroutine.yield()
		end
	end
end

Players.PlayerAdded:Connect(LoadData)
Players.PlayerRemoving:Connect(function(Player)
	coroutine.wrap(function()
		SaveData(Player, true, false, "InventoryIsland")
		PlayersData[Player] = nil
	end)()
end)
game:BindToClose(ServerShutdown)

coroutine.wrap(function()
	while true do
		task.wait(180)
		for _, Player in ipairs(Players:GetPlayers()) do
			coroutine.wrap(SaveData)(Player, false, false, "InventoryIsland")
		end
	end
end)

But I have found problem - when I TELEPORT to round place, all data gets SessionLocked, what means that it has failed to save. If I leave game instead, then all data is saved correctly.
I have tried a lot of diffirent ways to fix that, but they all not helped, that’s why I now asking here.

Sounds like you’ll need to save and then Teleport.
Possibly, set a flag to skip the auto save(s) you have in place now when doing the teleport save before teleporting…

1 Like

I have found a problem which caused that - I accidently tried to save Instance, which is impossible. But it took hellish amount of debugging.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.