Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
3

Player is nil in Module Script?

Asked by 8 years ago

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?

0
Whoops, seems like my output is outdated LegitimatlyMe 519 — 8y

1 answer

Log in to vote
6
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
8 years ago

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
Ad

Answer this question