DataStore2 setting player parent to nil on shutdown all servers

So I have noticed that using datastore2 in my game causes the players parent to be set to nil everytime I shutdown all servers which I really dont want to happen at all!

basically this is it, if I comment that part where it spawns the function it works but I dont want to risk it, it might cause data loss, please tell me how I can prevent that from happening!

game:BindToClose(function()
		if not fired then
			spawn(function()
				player.Parent = nil -- Forces AncestryChanged to fire and save the data
			end)

			event.Event:wait()
		end

		local value = dataStore:Get(nil, true)

		for _, bindToClose in pairs(dataStore.bindToClose) do
			bindToClose(player, value)
		end
	end)
`
1 Like

Yeah I ran into the same issue. I just edited the DataStore2 code to fix it.

The datastore code is kind of sloppy (enjoy my code comments :wink:) - it is only setting player parent to nil to trigger the AncestryChanged event that will trigger the save operation here:
image
I extracted that code and put it into a function called ā€œsaveOnLeave()ā€, which I also call in the spawned function that used to parent the player to nil. Thereā€™s logic inside saveOnLeave to make sure it only gets called once per player.

PS: I would include the whole change, but unfortunately I have other custom edits in DataStore2 that may be confusing/interfere with just this functionality you desire. This info should be enough :+1: all the code you need for saveOnLeave should be in the playerLeaveConnection event function

2 Likes

Sorry for the late reply, would this do the trick

	local event, fired = Instance.new("BindableEvent"), false
	local playerLeavingConnection
	local function saveOnLeave()
		playerLeavingConnection:Disconnect()
		dataStore:SaveAsync():andThen(function()
			print("player left, saved " .. dataStoreName)
		end):catch(function(error)
			-- TODO: Something more elegant
			warn("error when player left! " .. error)
		end):finally(function()
			event:Fire()
			fired = true
		end)

		delay(40, function() --Give a long delay for people who haven't figured out the cache :^(
			DataStoreCache[player] = nil
		end)
	end
	game:BindToClose(function()
		if not fired then
			spawn(function()
				saveOnLeave()
				--player.Parent = nil -- Forces AncestryChanged to fire and save the data
			end)

			event.Event:wait()
		end

		local value = dataStore:Get(nil, true)

		for _, bindToClose in pairs(dataStore.bindToClose) do
			bindToClose(player, value)
		end
	end)

	playerLeavingConnection = player.AncestryChanged:Connect(function()
		if player:IsDescendantOf(game) then return end
		saveOnLeave()
	end)
1 Like

The question is why you want to prevent the player from being set to nil. Whatā€™s your use case here that requires the player to be in the DataModel? Itā€™d be more informative to know what youā€™re trying to do and if thereā€™s an alternative to how youā€™re doing it (which there will be).

Well I made a soft shutdown script which loops through the players and teleport them to a reserved server on shutdown

If you didnt get the point, well when it loops to teleport them it isnt finding any player due to the player parent being set to nil, plus players think the game crashes whenever that happens since its not like that in other games they played, last of all sometimes it doesnt even show the shutdown message

Iā€™ve personally never experienced what occurs when a server shutdown happens with games using DataStore2 but it seems thereā€™s a member function called BindToClose which passes the player and the value in the DataStore. Try integrating this with your system.

Connect your own BindToClose function that reserves a private server and makes it accessible somewhere to other BindToClose functions. From there, use DataStore2ā€™s BindToClose to get the reserved server (or wait until the server is reserved first) and teleport the player accordingly.

-- In one script
game:BindToClose(function ()
    -- Reserve a private server
    -- Set this accessible somewhere
    -- Can be an attribute to the workspace, a module or ValueObject
end)

-- In your data manager or somewhere accessible
-- Point is to have a DataStore made by DataStore2 accessible
DataStore2DataStore:BindToClose(function (player)
    -- Get the private server made from the above BindToClose
    -- Teleport the player
end)

This should be sufficient enough to cover your use case, though I wonā€™t say that for certain. Iā€™ve looked around and it doesnā€™t seem like thereā€™s much on this.

Worst case scenario is to fork and handle saves yourself in BindToClose.

1 Like

You think changing the part where it sets the parent to nil into that has downsides?

DataStore2 looks to be using AncestryChanged to force a data save so probably yes it might be problematic. I donā€™t really use DataStore2 so I donā€™t know: I prefer ProfileService or (especially) my own data layer so Iā€™m not pigeon holed into any weird problems.

I would alternatively suggest removing the BindToClose part, saving data manually and then teleporting players out of the server but Iā€™m not sure DataStore2 is meant to be used with soft shut down servers so frankly I donā€™t know what your best solution is here.

1 Like

I did a few tests and it seems to be working fine and the saving is also doing good, @TheGreat_Scott seems to have some experience with datastore2 and apparently he didnt have any problems with his solution so far so Iā€™ll go with it, if I get a report of any data loss Iā€™ll try another solution

Looks good to me! @Skan_Dev
You handled the scope/level of function correctly n all :+1:

Note I would recommend adding some personal code comments to the top + at the line changes, so you can remember what you changed and why, if you ever want to re-use/copy this DataStore2 to a different game, or if you need to update DataStore2 later.

If DataStore2 ever has a big update you want to integrate, I use a ā€˜diffā€™ program like Visual Studio Code or Notepad++ to compare the changes in the new module and bring over my own changes into the updated module script.

@colbert2677

why you want to prevent the player from being set to nil

Like Skan said, if you are trying to teleport players during the shutdown process, and they are parented to nil, they wonā€™t get teleported to a new place, instead they will just get disconnected from the server.

DataStore2 looks to be using AncestryChanged to force a data save so probably yes it might be problematic

This custom implementation that Skan showed still does the save call on player ancestry changed (when the player leaves), the only difference is that you are explicitly calling the saveOnLeave functionality during the BindToClose function, rather than setting player parent to nil to implicitly fire it.

2 Likes