Calling SetAsync in a pcall() causes server script to stop

I’ve spend two evenings trying to fix a completely random error, that is stopping me from testing my first DataStore save and load code.

Server init script:

local DataService = game:GetService("DataStoreService")
local HttpsService = game:GetService("HttpService") -- Required to write dictionaries to datastore
local PlrsDS = DataService:GetDataStore("PlrsData") --TESTING

local plrDatas = {}		-- one entry per current player

Players.PlayerAdded:Connect(function (plr)
	local lPlrData = { -- example property
		startDate = 		DateTime.now()
	}

	local lSerialized = HttpsService:JSONEncode(lPlrData)
	
	local lResult, lErr = pcall(function()
		print("ONE") --prints successfully
		PlrsDS:SetAsync(plr.UserId, lSerialized) -- Testing this line. Just added.
	end)
	print(46,lResult,lErr,"LENGTH:",#lSerialized) -- doesn't get this far

	plrDatas[plr] = lPlrData -- doesn't run this line, or more specifically, plrDatas[plr] remains nil.
end)

If I rem out the SetAsync line, everything runs fine, and the whole game loads.

If I un-rem it and let the ASync line run, it never gets to the “Print 46” line above. It never assigns plrDatas[plr], and therefore later, can’t send the player data to the client because plrDatas[plr] comes out nil. The client-side then fails irrecorably.

Before the client-side errors (caused by plrData being nil), there are no other errors.

The “Allow HTTP Requests” and “Enable Studio Access to API Services” are both enabled in Game Settings. (I’ve read somewhere that that’s required, but it doesn’t say so on GlobalDataStore | Documentation - Roblox Creator Hub ).

Can you please try removing the pcall surrounding it? It’s possible that there is something going majorly wrong that is stopping the script, but isn’t sending the message because of the pcall.

(IK PCall is intended to prevent script breaking stuff, but just to be sure)

1 Like

Done; the same symptoms persist. I actually forgot to change my print(46) statement. It should have errored because lResult and lErr would be unknown variables:

--local lResult, lErr = pcall(function()
	print("WHAT")
	PlrsDS:SetAsync(plr.UserId, lSerialized) -- Saves Data
--end)

print(46,lResult,lErr,"LENGTH:",#lSerialized)

But it didn’t get as far as the print.

Removing the SetAsync:

	--local lResult, lErr = pcall(function()
	print("WHAT")
	--PlrsDS:SetAsync(plr.UserId, lSerialized) -- Saves Data
	--end)
	
	--print(46,lResult,lErr,"LENGTH:",#lSerialized)
	print("Here")
    plrDatas[plr] = lPlrData

It of course prints “WHAT” and “Here” and the server-side and client-side all run fine. The all-important plrDatas assignment correct runs, which is what is sent to the client later (when the client requests it, after playerChar is loaded).

Can you quickly try replacing the SetAsync stuff with :GetAsync, to see if that has any impact (ik it causes the script to behave differently regarding saving, but it’s odd that it’s not progressing past the set async.

1 Like

The code above is simplified, I done a print of the lSerialized data, to make sure nothing weird was happening. It correctly printed out a serialized string:

{"regions":[],"s_startRegionXYZ":[1003,998,1],"pockets":{"title":"Pouches & Pockets","s_contents":[{"q":0},{"q":0},{"q":0},{"q":0},{"q":0},{"q":0}]},"lastUsedWeaponId":"","startDate":null,"displayDepths":{"0":1}}

It looks ordinary to me (this is my first test code for serializing and saving, so I don’t have experience of what it SHOULD look like). It’s interesting that ‘startDate’ is ‘null’, when in fact it is set to this:

local lPlrData = {
	startDate = 			DateTime.now(), -- since epoch or whatever, like '1732574879119'

I don’t know why it’s serialized this as ‘null’, but I doubt that it’s causing the entire server to quietly go bonkers.

I think I’ve worked it out.

Firstly, I done as you suggested and tried a Get, and it worked, and printed the data I’d expect to have saved (if the SetAsync worked… which it does):

print(PlrsDS:GetAsync(plr.UserId)) --TEST

And now it runs the subsequent line, BUT… the client still fails. I added some (more) prints to the client code. What is happening:

With Async line:

  1. Server init is running the ‘player added’ code. It gets up to the Get or Set sync line.
  2. Client buts in, and requests player data. Server responds with nil, because it hasn’t got anything yet.
  3. Client splurges out a load of errors.
  4. Server never completes the lines after the Async line which it fired off, but then client errors stopped Studio from continuing with subsequent lines.

Without Async line:

  1. Server init is running the ‘player added’ code. It quickly stores player data.
  2. Client requests player data. Server responds with player data. All is well.

In other words… because the Async is causing a delay, I need the client to not run CharAdded until after the server has had a chance to do things. It’s already got a wait(2)… I need to stall the client longer, until the server has finished its initial code.

I thought the server MUST finish loading its first scripts before the client would even start!

This returns a datetime object:
image

You should do DateTime.now().UnixTimestamp

1 Like

I editted my (initially very brief) reply above with a synopsis.

Adding this to client start script has ‘fixed’ it:

-- **********************************************************************************
-- (Step 2) Get plr data
-- **********************************************************************************

wait(2) -- ADDED A WAIT, whilst server gets from data store
local lStartRegion, lStartRoom
Gl.plrData,lStartRegion,lStartRoom = RS.rf:InvokeServer({q="getPlrData"})

It’s a rubbish solution. I need to make it so the server only replies to the InvokeServer WHEN IT IS READY. So, at the start of Server init script, I need “readyForClient = false”. Once data is retrieved, I can set “readyForClient = true”.

And the server-side of RS.rf:InvokeServer({q=“getPlrData”}) will just wait itself until readyForClient =true. Therefore, it’ll make the client wait until server is ready with data.

(If server doesn’t HAVE data, it makes a new player data dictionary. That part already works).

I want to put in a feature request.

Player Auto Data Store

Every plr object has an inbuilt .autostore = {} dictonary. It gets auto-loaded along with the player, with no code required at all. It auto-serializes and de-serializes.

The server sets player.autostore.points = 100 (etc) and calls player:saveAutoStore when it thinks it should do a save, and there’s a setting for ‘autoSaveOnExit’ or something.

Imagine how many kilometres of code (and headaches) would be saved by that. An inbuilt autoloaded data store that comes with the player object on the server. (Only writeable by server).

That used to be a thing IIRC. It was removed though

Instead of a task.wait(2) on the client side, try changing the retrieval function on the server side.

e.g.

while not playerData[plr] do
 task.wait()
end
return playerData[plr]
1 Like

What! Why do they hate us so…

Because it was awful :smiley:

Not as awful as the code I’m trying to write to replace it. ;p

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