Datastore value returning nil

i was trying to make it so everytime u join back u get different starting dialogue, and to achieve this i tried using datastore system saving a numeric value that increases everytime the player disconnects, and if its nil make it 1. the problem is that the datastore value (which in the script below is n) returns nil even if it should be set to 1, which i dont get how its possible. i tried everything to fix it but nothing works please help

local dss = game:GetService("DataStoreService")
local d = dss:GetDataStore("number")
local n

local function key(p)
	return "number" .. p.UserId
end

local function load(p)
	local pkey = key(p)
	local success, number = pcall(function()
		return d:GetAsync(pkey)
	end)
	if success then
		return number or 0
	else
		warn("didnt load")
		return 0
	end
end

local function save(p, value)
	local pkey = key(p)
	local success, errorMessage = pcall(function()
		d:SetAsync(pkey, value)
	end)
	if not success then
		warn("didnt save")
	end
end

game.Players.PlayerAdded:Connect(function(p)
	n = load(p)
	if n == nil then
		n = 1
		save(p, n)
	end
	print(p.Name, n)
end)

local function add(p)
	n = n + 1
	save(p, n)
end

game.Players.PlayerRemoving:Connect(add)

first change your variables to be “DataStoreService”, “DataStore”, and whatever n is, change it so we can actually read this

Are any of the print statements firing?

nothing is printing smh thats y i cant troubleshoot this

n is the value that has to be both extracted and saved from datastore

i mean the rest is not that complicated is it? u got dds which is the service and d that is the datastore

I had a hard time reading this, but the issue might be related to how you’re handling the load function. If the GetAsync operation fails for any reason, the pcall block will catch the error, and in that case, you are returning 0. However, if the operation fails, number will not be assigned, and you’ll effectively be returning nil

To address this, you should explicitly return 0 when an error occurs
You could try something like

local function load(p)
    local pkey = key(p)
    local success, number = pcall(function()
        return d:GetAsync(pkey)
    end)

    if success then
        return number or 0
    else
        warn("didn't load")
        return 0
    end
end

This ensures that if GetAsync fails, the function will return 0 instead of nil

Also if you want to initialize n to 1 when the value is nil , you can modify your PlayerAdded event handler by doing something like

game.Players.PlayerAdded:Connect(function(p)
    n = load(p)
    
    if n == 0 then
        n = 1
        save(p, n)
    end
    
    print(p.Name, n)
end)

This checks if n is explicitly 0 and initializes it to 1 in that case.

Of course, I didn’t test any of this, but something you can try, Hope it helps!

I say this on most DS posts but try these two things

  • Add this to the end of your script to give the game time to save
game:BindToClose(function() 
    task.wait(5)
end)
  • If that doesn’t work, try testing your game outside of Studio. I have seen a few posts that say the game will only save if it is in the client version of Roblox.

Edit: Clarification

interestingly, when I copied and pasted the code into my Roblox Studio, it worked perfectly. Somehow.

There are a couple things I want to point out about the code.

First, I agree with everyone that the variable names could be improved for reading. Usually that’s fine when only you have to read the code, but in this case it’s hard to read when trying to help you. The best practice is always to write your code like you will have to show someone else.

Second, Never use a global local variable (singular memory location) to store a players data. If two players join the game, the second player to join will override the n variable. You need to create a new variable (new memory location) for each player’s data, so data does not get overwritten. The easiest way is to put the data into a table.

Finally, as @PolyLacticSugarcane said, it could be that there is not enough time to save when you exit, and the server automatically starts to shut down. However, there may be cases where even 5 extra seconds are not enough (though usually it is). One thing you can do to always ensure data saves is to keep a counter for how many people still need to save when the server starts shutting down and waiting until that counter hits 0.

Here’s your code updated (in your syntax mostly) with some quick fixes.

local dss = game:GetService("DataStoreService")
local d = dss:GetDataStore("number")
local nList = {}
local playersNeedingSaved = 0

local function key(p)
	return "number" .. p.UserId
end

local function load(p)
	local pkey = key(p)
	local success, number = pcall(function()
		return d:GetAsync(pkey)
	end)
	if success then
		return number or 0
	else
		warn("didnt load")
		return 0
	end
end

local function save(p, value)
	local pkey = key(p)
	local success, errorMessage = pcall(function()
		d:SetAsync(pkey, value)
	end)
	if not success then
		warn("didnt save")
	end
end

game.Players.PlayerAdded:Connect(function(p)
	nList[p.UserId] = load(p)
	local n = nList[p.UserId] 
	if n == nil then
		n = 1
		nList[p.UserId] = n
	end
	playersNeedingSaved += 1
	print(p.Name, n)
end)

local function add(p)
	local n = nList[p.UserId] 
	n = n + 1
	nList[p.UserId] = nil
	save(p, n)
end

local function onLeave(p)
	add(p)
	playersNeedingSaved -= 1
end

game:BindToClose(function()
	while playersNeedingSaved > 0 do
		task.wait(.25)
	end
end)

game.Players.PlayerRemoving:Connect(onLeave)

Though in the end my biggest recommendation is to just randomly select a starting dialogue when the player joins, and not worry about if it chooses the same one twice in a row. Simpler, and way more reliable with only minimal downsides.

1 Like

about that, i had to clarify a few things i forgot to include in the post like: the game had to be singleplayer, that is why i only checked for one n and not multiple since no other player is able to join the same server as another, second my code is a nuisance to read ik sorry i havent rewrote it correctly and third here the problem is it somehow doesnt work when i try to load it, and even with the precautions that take place if its nil it still returns to nil. the rest of my code treats n as an integer value btw

the error i encounter in the log is i tried to compare n with an integer while n = nil, but that doesnt make any sense

attempt to compare number <= nil

nvm i was just firing everything when playeradded while the script was under the player, now everything works

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