Script just literally stops when it tries to save data

game.Players.PlayerRemoving:Connect(function(plr)
	print(spbh.Value, plr)
		if plr == player  -- only saves one specific player in the server. ignores others
and spbh.Value ~= "" then
			local data = mok_rok[spbh.Value].bonche.playerhasit.savecode.Value
			print(data)
			if data ~= 0 then
				print("past the conditionals")
				local s,_ = pcall(function()
					print("inside the pcall")  -- the script stops here
					itemsave:SetAsync("spbh", data) 
					print("datasave done")
				end)
			end
			print("outside the pcall")
		end
	end)
	print("plrremoving reached")

I made a script that saves data when a specific player leaves the game. I tried both kind of studio tests and also tried it In-game after publishing it. All the other lines of the script functioned properly except the part I wrote above(failed in all 3 tests). So, I added print all over it to find out what went wrong, and tested again.

the output was like this:
plrremoving reached
1 nofair2002
1
past the conditionals
inside the pcall

and nothing appeared after that. It was obvious that the script stopped there. But I couldn’t figure out why.

I tried removing the pcall, nothing changed. No errors.
I erased the part that called game:BindToClose()(not written here), nothing changed.
PlayerRemoving is only called here once. No other scripts call it.
PlayerRemoving is not inside a function
studio API and Http requests are both on.
I tried placing the itemsave:SetAsync part outside the PlayerRemoving function. It worked. But when I place it back in, it doesn’t work again.

Please help me.

3 Likes

This is why you actually need to use pcall for what it’s for, which is an error handler. Calling things in protected mode isn’t enough when it comes to DataStore code; if you’re experiencing unexpected behaviour, then you need to use the return values to debug.

local success, result = pcall(function ()
    return itemsave:SetAsync("spbh", data)
end)

if not success then
    warn(result)
end

Prints along the way aren’t enough to debug, printing actual values (which you do already do except for the returns of the pcall) will further help you debug as will pcall itself since it returns both a success value and error information or the values you explicitly return if the function successfully runs.

It’s clear that the pcall is still running but stopping at SetAsync, so there must be some kind of exception being thrown. You aren’t receiving an error because pcall is catching it.

2 Likes

Thanks for your help!
But even if I remove pcall to check what it’s catching, no errors are thrown at all.
The script won’t print anything after that line, even it’s outside pcall.

print(“ouside the pcall”)

didn’t print anything. The script just stops there and does nothing afterwards.

2 Likes

I tried your script too, but no errors were executed. The script really just stops there.

2 Likes

I don’t see where you set your GlobalDataStore (itemsave) Perhaps it is nil?

1 Like

It’s on the script. I just skipped that part.

local datastore = game:GetService("DataStoreService")
local itemsave = datastore:GetDataStore("tem", player.UserId)

1 Like

I’m not sure if my video would help you but it’s worth sharing.

2 Likes

Did you try:
local itemsave = datastore:GetDataStore("tem", ""..player.UserId)
?

I tried it, it didn’t work.
The problem appears when I try to save at PlayerRemoving. the datastore itself causes no problem. I think the player is being disconnected before the script finishes its job.

This might help you. Its what I use to save data, may need tweaking a bit to work with your game. This will save values inside of a leaderstats folder within the player.

game.Players.PlayerRemoving:connect(function(player)
	
	local datastore = game:GetService("DataStoreService"):GetDataStore(player.Name.."Stats")

	local statstorage = player:FindFirstChild("leaderstats"):GetChildren()
	for i =  1, #statstorage do
		
		datastore:SetAsync(statstorage[i].Name, statstorage[i].Value)
		print("saved data number "..i)
		
	end
	
	print("Stats successfully saved")	

end)

It should be placed in ServerScriptService.

2 Likes

And that would matter because? You’re running in Studio and the game is shutting down?
You may want to use game:BindToClose() with player kicks to ensure DataStore writes on shutdown.
I know in your original post you said you tried in studio and in published,
but have you tried all changes/tests in all as well?
I assume you have “Enable Studio Access to API Services” checked in your Game Configuration, as
@Snoopy1333 suggests (I mention it for completeness and for others.)
To add to what @446576656C6F706572 suggests, are you checking for errors on the other earlier
DataStore accesses in your code? There may be some hints there.
Have you tried writing “blah” for instance instead of data as a test?
Finally, as a test, have you tried changing the store you are using? (Maybe there is corruption.)

Little note about your suggestion here: if you are trying to coerce UserId into a string by concatenating it to a blank string (you can just use tostring here), that wouldn’t be necessary. Anything related to a DataStore - name, scope, key and value - are coerced into string formats internally before being passed to DynamoDB. In the case of value, if a non-string format is passed, then the value will be coerced into a string format - for example, for tables, through JSON encoding.

Hi, I think the OP know this, and yes to coerce, but I mainly did the concat that way to intentionally not
use tostring() as it will turn nil into “nil”, to demonstrate for testing purposes, it fails immediately
on concat with a nil

You don’t even need any demonstration nor testing purposes here. UserId is guaranteed to be non-nil and the default value is 0. If it’s anything other than an integer, you’ve ran into an edge case and should file a bug report with appropriate information.

This was mainly directed to you, but it can also be used for information should anyone else want to adopt this. I’m also confused on why you used string coercion on scope when that neither has any bearing on the problem listed in the thread nor differentiates what happens with GetDataStore, it just looks messier from a readability standpoint.

I know that. Just for general extra testing of this stuff for the OP, he may find it useful. Yes messier–Simply
as a testing method.

Sorry Im late.
Yes. I tried everything that I could think of.
I invited my friends to the game and checked the console when they left.
I removed the other earlier datastore accesses to check if it works without them.
Changing the name of the variable didn’t make any change.
I even put 1 instead of data:

itemsave:SetAsync("spbh", 1)

to check if there was any problem in calling the value of the variable
And finally, I have been changing the store like tem2, tem3 etc every time I wanted to reset data. I tried it once again, but it still doesn’t work.

So is that a yes that you’ve tried game:BindToClose() to try to rule out your shutdown / player leaving
theory?

yes. It just waits and does nothing until timeout.

I give up now. I’ll just find an alternative way to save data instead of playerleaving.

Thanks again for all the attention and help though.

Try autosaving maybe? By this point clearly many experienced people have weighed in and no one really knows what’s going on. This could qualify for a bug report.

1 Like

I would encourage you to keep trying–Have you tried making a new minimal rbxl?
There’s a funny old thing about “real programmers” and part of it goes:
'‘Real Programmers’ programs never work right the first time. But if you throw them on the machine they can be patched into working in “only a few” 30-hour debugging sessions. ‘’
Often when this type of thing happens, you’re “barking up the wrong tree.”
To be clear, I meant keep your use of BindToClose() and within the listener, loop through the players
and kick them.
I’m still unclear as to how you’re testing; if it’s a leave, shutdown, etc. But I will suggest that first be sure
your game can handle negative UserIds and non-player-real-name-matching character names, then
in Studio, use Test->Start, but prior to Start, select at least 2 Players. (As opposed to using Home->Play)
Then in the game in Studio, have just one of the players leave. And see what happens then.

1 Like