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)
`
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 ) - it is only setting player parent to nil to trigger the AncestryChanged event that will trigger the save operation here:
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 all the code you need for saveOnLeave should be in the playerLeaveConnection event function
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)
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).
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.
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.
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
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.
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.