Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Player is nil in Module Script?

Boom boom complicated metatables ahead!

Great, I'm inexperienced in writing questions for I have only written answers, ain't that dandy?

My issue is that whenever I try to access the player in a module script, it thinks it's nil, here is the source code

local DS = game:GetService("DataStoreService"):GetDataStore("Stuff_DS_V1");
local PM = {} do
	PM.RegisteredPlayers = {};
	local Methods = {};	
	
	Methods.GetData = function(plr, key, defaultVal)
		local data = DS:GetAsync(plr.player.userId.."_"..key);
		if data then
			if type(defaultVal) == "table"then
				return data;
			else
				return data;
			end
		else
			return defaultVal;
		end
	end
	
	Methods.SaveData = function(plr, key, val)
		DS:SetAsync(plr.Player.userId.."_"..key, val);
	end
	
	Methods.LoadData = function(plr)
		local data = plr:GetData("Stats", {Blox = 0, Lvl = 1, Exp = 0});
		plr.Blox = data.Blox;
		plr.Lvl = data.Lvl;
		plr.Exp = data.Exp;
		local backpack = plr:GetData("Backpack", {Backpack = {"Sword", "Classic"}, Equipped = {Melee = "Sword",Grave = "Classic"}});
		for i,v in pairs(backpack.Backpack) do
			table.insert(plr.Backpack, v);
		end
		for i,v in pairs(backpack.Equipped) do
			plr.Equipped[tostring(i)] = tostring(v);
		end
	end
	
	Methods.Destroy = function(plr)
		PM.RegisteredPlayers[tostring(plr.player.userId)] = nil;
	end
	
	PM.RegisterPlayer = function(plr)
		if PM.RegisteredPlayers[tostring(plr.userId)] == nil then
			local Data = {};
			Data.Blox = 0;
			Data.Lvl = 0;
			Data.Exp = 0;
			Data.Backpack = {};
			Data.Equipped = {};
			setmetatable(Data, {
				__index = function(tab, key)
					if key:lower() == "player" then
						return plr;
					elseif Methods[key] then
						return function(...)
							return Methods[key](...);
						end
					elseif Data[key] then
						return Data[key];
					else
						print("ERROR: "..key.." is nil.")				
					end
				end,
			});
			PM.RegisteredPlayers[tostring(plr.userId)] = Data;
			return Data;
		else
			print(plr.Name.." is already registered and will be allowed to access the custom methods.");
			return PM.RegisteredPlayers[tostring(plr.userId)];
		end
	end
	
	PM.Get = function(plr)
		if plr == nil then print("Argument 'plr' is nil.") return; end
		print("Getting the Player Table for: "..plr.Name);
		return PM.RegisterPlayer(plr);
	end
	
end

return PM;

That is the module script, as you can see, if the index is "player", then it returns the player object

And this is the normal handler script

local PM = require(game.ServerScriptService:WaitForChild("Modules"):WaitForChild("PlayerManager"));

game.Players.PlayerAdded:connect(function(plr)
	local makeshiftPlayer = PM.Get(plr);
	print(makeshiftPlayer.Player.userId);
	makeshiftPlayer:LoadData();
end)

game.Players.PlayerRemoving:connect(function(plr)
	local makeshiftPlayer = PM:Get(plr);
	makeshiftPlayer:SaveData("Stats", {Blox = makeshiftPlayer.Blox, Lvl = makeshiftPlayer.Lvl, Exp = makeshiftPlayer.Exp});
	makeshiftPlayer:SaveData("Backpack",{Backpack = plr.Backpack, Equipped = plr.Equipped});
	wait();
	makeshiftPlayer:Destroy();
end)

But what's interesting is, the error only happens when a player leaves, I've already tested if the player is nil if you send it to a module, it is not.

The very error happens in the PM.Get function, which isn't even all that complicated, but even the script can't find the player for some reason.

11:49:06.526 - ServerScriptService.Modules.PlayerManager:74: attempt to concatenate field 'Name' (a nil value) 11:49:06.527 - Stack Begin 11:49:06.529 - Script 'ServerScriptService.Modules.PlayerManager', Line 74 - method Get 11:49:06.530 - Script 'ServerScriptService.Scripts.Player', Line 10 11:49:06.531 - Stack End

If I remove the 'print("Getting the Player Table for: "..plr.Name)', it errors in the Methods.SaveData function, where it says userId is nil.

11:49:06.526 - ServerScriptService.Modules.PlayerManager:68: attempt to concatenate field 'userId' (a nil value) 11:49:06.527 - Stack Begin 11:49:06.529 - Script 'ServerScriptService.Modules.PlayerManager', Line 68 - method SaveData 11:49:06.530 - Script 'ServerScriptService.Scripts.Player', Line 11 11:49:06.531 - Stack End

And, like the other error, it only errors when you leave. I suspected that when you index 'player' it returns nil since the player left.

How can I access the player, without it being nil?

1 comment

0

Whoops, seems like my output is outdated

1 answer

✓ Accepted answer

ROBLOX Objects shouldn't be able to have fields like Name or userId set to nil, so this is a weird error. My instinct was that plr in PM.Get wasn't actually a Player object.

Adding this line of debugging:

        if plr == nil then print("Argument 'plr' is nil.") return; end
		print("This is the player: ", plr)
        print("Getting the Player Table for: "..plr.Name);

This is indeed the case:

This is the player: table: 0E0360B0

This is really weird, since I checked that plr was indeed being passed in as a Player object:

		print("PlayerRemoving(", plr, ")")
		local makeshiftPlayer = PM:Get(plr);
-- PlayerRemoving( Player1 )

Where is the table coming from?


Hint: one character is wrong PM:Get(plr).


You're calling PM.Get as a method, but it wasn't designed to be used as one! Thus the plr argument is actually being populated as PM!


A tweak that would have helped catch this is adding a metamethod to the PM module table to error if you try to grab a field (like Name or userId) that doesn't exist:

setmetatable(PM, {__index = function(_, key) error("`" .. key .. "` is not a function provided by this module", 2) end})

You can also add checks that the first parameter is never PM.

Cleanup Aside

GetData can be simplified a lot:

function Methods.GetData(plr, key, defaultValue)
	return DS:GetAsync(plr.player.userId .. ("_" .. key)) or defaultValue
end

This discussion is preserved as it was. New answers, comments, and votes are closed.