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?
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
.
GetData
can be simplified a lot:
function Methods.GetData(plr, key, defaultValue) return DS:GetAsync(plr.player.userId .. ("_" .. key)) or defaultValue end