NullReferenceException with custom plugin

Full error:

Failed to call hook 'OnEnterZone' on plugin 'ParkorCode v1.0.0' (NullReferenceException: Object reference not set to an instance of an object)
  at Oxide.Plugins.ParkorCode.OnEnterZone (System.String ZoneID, BasePlayer player) [0x0003b] in <691f8d7732f2449c9dcc1784f0de299a>:0 
  at Oxide.Plugins.ParkorCode.DirectCallHook (System.String name, System.Object& ret, System.Object[] args) [0x0008d] in <691f8d7732f2449c9dcc1784f0de299a>:0 
  at Oxide.Plugins.CSharpPlugin.InvokeMethod (Oxide.Core.Plugins.HookMethod method, System.Object[] args) [0x00079] in <60c318df79ed41688ea59335e48d61ad>:0 
  at Oxide.Core.Plugins.CSPlugin.OnCallHook (System.String name, System.Object[] args) [0x000d8] in <9882f28dc2204b4dba514a9ad18f5042>:0 
  at Oxide.Core.Plugins.Plugin.CallHook (System.String hook, System.Object[] args) [0x00060] in <9882f28dc2204b4dba514a9ad18f5042>:0
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)​

This error happens when you walk into the zone. Tried to get more of the error from logs file but didn't change unless i looked in the wrong log file?
This is my code:

 void OnEnterZone(string ZoneID, BasePlayer player) // Called when a player enters a zone
        {
            IPlayer iplayer = player as IPlayer;
            if (ZoneID == _pluginConfig.zoneID && !Codes.ContainsKey(iplayer.Id))
            {
                var code = GenerateCode(_pluginConfig.CodeLength, _pluginConfig.CodeLowercase);

                iplayer.Reply($"Congratulations! You have finished the parkour, To redeem your prize please join our <color=red>Discord</color>\n\n From there on, message our bot the code below!");
                iplayer.Reply($"Here is your code <color=red>{code}</color>");
                Codes.Add(iplayer.Id, code);
            }
            else return; 
            timer.In(_pluginConfig.CodeLifetime, () =>
            {
                if (Codes.ContainsKey(iplayer.Id))
                {
                    Codes.Remove(iplayer.Id);
                    if (player != null) iplayer.Reply("Code Expired");
                }
                else return;
            });   
        }

I would start by null checking your IPlayer usage at the top, being as NPCs are technically BasePlayers and could potentially trigger that, but they won't have an IPlayer.

Ok ive added a if (iplayer !=null) to the code, It compiles fine however the code doesn't seem to run, Like no messages get sent to the player

What is your current code?

Here is my current code 

        private void OnServerInitialized()
        {
            _client.Connect(guildID, GatewayIntents.Guilds | GatewayIntents.GuildMembers);
        }
        private string GenerateCode(int size, bool lowerCase)
        {
            var builder = new StringBuilder();
            var random = new System.Random();
            char ch;

            for (int i = 0; i < size; i++)
            {
                ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
                builder.Append(ch);
            }

            if (lowerCase)
                return builder.ToString().ToLower();
            code = builder.ToString();
            return builder.ToString();
        }
        void OnEnterZone(string ZoneID, BasePlayer player) // Called when a player enters a zone
        {
            IPlayer iplayer = player as IPlayer;
            if (iplayer != null)
            {
                if (ZoneID == _pluginConfig.zoneID && !Codes.ContainsKey(iplayer.Id))
                {
                    iplayer.Reply("Congratulations! You have finished the parkour, To redeem your prize please join our <color=red>Discord</color>\n\n From there on, message our bot the code below!", Codes[iplayer.Id]);
                    iplayer.Reply($"Here is your code <color=red>{code}</color>");
                    return;
                }
                else if (ZoneID == _pluginConfig.zoneID)
                {
                    var code = GenerateCode(_pluginConfig.CodeLength, _pluginConfig.CodeLowercase);

                    iplayer.Reply($"Congratulations! You have finished the parkour, To redeem your prize please join our <color=red>Discord</color>\n\n From there on, message our bot the code below!");
                    iplayer.Reply($"Here is your code <color=red>{code}</color>");
                    Codes.Add(iplayer.Id, code);

                }
                else return;
                timer.In(_pluginConfig.CodeLifetime, () =>
                {
                    if (Codes.ContainsKey(iplayer.Id))
                    {
                        Codes.Remove(iplayer.Id);
                        if (player != null) iplayer.Reply("Code Expired");
                    }
                });
            }
            else return;
        }
        [HookMethod(DiscordExtHooks.OnDiscordDirectMessageCreated)]
        private void OnDiscordDirectMessageCreated(DiscordMessage message)
        {
            if (message.Author.Bot == true)
                return;

            if (message.Content.Length != _pluginConfig.CodeLength)
                return;

            if (!Codes.ContainsValue(message.Content))
            {
                message.Reply(_client, "Invalid Code!");
                return;
            }

            if (_link.IsLinked(message.Author.Id))
            {
                message.Reply(_client, "Already Linked!");
                return;
            }

            DiscordChannel.GetChannel(_client, message.ChannelId, dm =>
            {
                // DM-check
                if (dm.Type != ChannelType.Dm)
                    return;

                Codes.Keys.ToList().ForEach(playerId =>
                {
                    if (Codes[playerId] != message.Content)
                        return;

                    IPlayer player = covalence.Players.FindPlayerById(playerId);
                    if (player == null)
                        return;

                    IPlayer member = message.Author.Player;
                    var DMember = message.Author;
                    _link.OnLinked(this, member, DMember);
                    DMember.SendDirectMessage(_client, "Congrats on finishing the parkour!");
                    member.AddToGroup(_pluginConfig.Group);
                });
                dm.CreateMessage(_client, "Authenticated!");
            });
        }​

Ah, you can't cast it that way. Use the IPlayer already available:

IPlayer iplayer = player.IPlayer;​

that'll use the BasePlayer player passed through the OnEnterZone method right?

Yes, "player" in your code is the BasePlayer, but the above gets the IPlayer that is set in the BasePlayer for you to use with your message handling.

As an alternative, you could send the message using BasePlayer.ChatMessage.

Thank you! Now I'm getting this message, the dictionary should work? Unless I'm missing something.

Failed to call hook 'OnEnterZone' on plugin 'ParkorCode v1.0.0' (KeyNotFoundException: The given key was not present in the dictionary.)
  at System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) [0x0001e] in <eae584ce26bc40229c1b1aa476bfa589>:0 
  at Oxide.Plugins.ParkorCode.OnEnterZone (System.String ZoneID, BasePlayer player) [0x0007d] in <03dcfb73d8e24080b819c213cb2b22b3>:0 
  at Oxide.Plugins.ParkorCode.DirectCallHook (System.String name, System.Object& ret, System.Object[] args) [0x0008d] in <03dcfb73d8e24080b819c213cb2b22b3>:0 
  at Oxide.Plugins.CSharpPlugin.InvokeMethod (Oxide.Core.Plugins.HookMethod method, System.Object[] args) [0x00079] in <60c318df79ed41688ea59335e48d61ad>:0 
  at Oxide.Core.Plugins.CSPlugin.OnCallHook (System.String name, System.Object[] args) [0x000d8] in <9882f28dc2204b4dba514a9ad18f5042>:0 
  at Oxide.Core.Plugins.Plugin.CallHook (System.String hook, System.Object[] args) [0x00060] in <9882f28dc2204b4dba514a9ad18f5042>:0
(Filename: ./Runtime/Export/Debug/Debug.bindings.h Line: 39)

 

You aren't checking if an entry exists for each player, you are assuming.

if (Codes[playerId] != message.Content)​

I'm not too sure what you mean by this, could you make it more clearer please?

Merged post

So you're saying i should add something to check if an entry doesn't exist?

I gave the line above that is most likely causing the issue. You'd need to check if your dictionary already contains that key before trying to add it again.

Would this work?

If(Codes.ContainsKey(player.Id))
{
   if (Codes[playerId] != message.Content)
   {

    }
}  ​

 

Else return;
What i see from this is, it checks if the dictionary for each player to see if it contains the key, which in this instance is the player.Id and if it's not in there it will return but should probably add a message there  aswell. Is this what you ment? 

Yes, that should work.

Hm, odd. I'm still getting the same error, its when you walk into the zone. Nothing happens but this error gets sent 

Failed to call hook 'OnEnterZone' on plugin 'ParkorCode v1.0.0' (KeyNotFoundException: The given key was not present in the dictionary.)
  at System.Collections.Generic.Dictionary`2[TKey,TValue].get_Item (TKey key) [0x0001e] in <eae584ce26bc40229c1b1aa476bfa589>:0 
  at Oxide.Plugins.ParkorCode.OnEnterZone (System.String ZoneID, BasePlayer player) [0x0007d] in <cee1f2a2082c4ade8d508c53ca7e2772>:0 
  at Oxide.Plugins.ParkorCode.DirectCallHook (System.String name, System.Object& ret, System.Object[] args) [0x0008d] in <cee1f2a2082c4ade8d508c53ca7e2772>:0 
  at Oxide.Plugins.CSharpPlugin.InvokeMethod (Oxide.Core.Plugins.HookMethod method, System.Object[] args) [0x00079] in <60c318df79ed41688ea59335e48d61ad>:0 
  at Oxide.Core.Plugins.CSPlugin.OnCallHook (System.String name, System.Object[] args) [0x000d8] in <9882f28dc2204b4dba514a9ad18f5042>:0 
  at Oxide.Core.Plugins.Plugin.CallHook (System.String hook, System.Object[] args) [0x00060] in <9882f28dc2204b4dba514a9ad18f5042>:0