Getting NullReferenceException on hook OnPlayerDeath and struggle to find out where the logic fails

Hi,

I'm having a hard time debugging an exception.

Failed to call hook 'OnPlayerDeath' on plugin 'WPBridge v1.2.0' (NullReferenceException: Object reference not set to an instance of an object)
  at Oxide.Plugins.WPBridge.OnPlayerDeath (BasePlayer basePlayer, HitInfo hitInfo) [0x000c5] in <c2adc3bad6934017bd27e6efd5086f7e>:0
  at Oxide.Plugins.WPBridge.DirectCallHook (System.String name, System.Object& ret, System.Object[] args) [0x00f3f] in <c2adc3bad6934017bd27e6efd5086f7e>:0
  at Oxide.Plugins.CSharpPlugin.InvokeMethod (Oxide.Core.Plugins.HookMethod method, System.Object[] args) [0x00079] in <60c318df79ed41688ea59335e48d61ad>:0 s in, 3kb/s out
  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​

This occures for example when a player falls, gets wounded and goes from wounded to dead.

My OnPlayerDeath method looks like this:

object OnPlayerDeath(BasePlayer basePlayer, HitInfo hitInfo)
{
    if (basePlayer == null) return null;
    BasePlayer attackingBasePlayer;
    if (basePlayer.IsNpc && hitInfo != null && hitInfo.Initiator != null)
    {
        attackingBasePlayer = hitInfo.Initiator as BasePlayer;
        if(attackingBasePlayer != null)
        {
            WPBPlayer attackingWPBPlayer = GetWPBPlayer(attackingBasePlayer);
            if(attackingWPBPlayer != null)
            {
                attackingWPBPlayer.Stats.NPCKills++;
                return null;
            }
        }
    }

    WPBPlayer deadPlayer = GetWPBPlayer(basePlayer);
    if (deadPlayer == null) return null;
    if (hitInfo != null && hitInfo.damageTypes != null && hitInfo.damageTypes.GetMajorityDamageType() == DamageType.Suicide)
    {
        deadPlayer.Stats.Suicides++;
        return null;
    } else
    {
        deadPlayer.Stats.Deaths++;
    }

    if(hitInfo.Initiator != null)
    {
        attackingBasePlayer = hitInfo.Initiator as BasePlayer;
        if (attackingBasePlayer != null)
        {
            if(attackingBasePlayer.IsNpc)
            {
                deadPlayer.Stats.KilledByNPC++;
                return null;
            }
            WPBPlayer attackingWPBPlayer = GetWPBPlayer(attackingBasePlayer);
            if (attackingWPBPlayer == null) return null;
            attackingWPBPlayer.Stats.Kills++;
        }
    }
    return null;
}

The GetWPBPlayer method looks like this:

private WPBPlayer GetWPBPlayer(BasePlayer basePlayer)
{
    if (basePlayer == null || WPBPlayerData == null || WPBPlayerData.Count == 0) return null;
    return WPBPlayerData.FirstOrDefault(bp => bp.SteamID == basePlayer.UserIDString);
}

The WPBPlayerData is a List<WPBPlayer>

WPBPlayer class

internal class WPBPlayer
{
    private string _steamID;
    private string _displayName;
    private Vector3 _position;
    private WPBPlayerStats _stats;
    public string SteamID { get { return this._steamID; } }
    public string DisplayName { get { return this._displayName; } }
    public Vector3 Position { get { return this._position; } }
    public WPBPlayerStats Stats { get { return this._stats; } set { _stats = value; } }
    ......
}

WPBPlayerStats class

internal class WPBPlayerStats
{
    public int Joins { get; internal set; }
    public int Leaves { get; internal set; }
    public int Deaths { get; internal set; }
    public int Suicides { get; internal set; }
    public int Kills { get; internal set; }
    public int Headshots { get; internal set; }
    public int Wounded { get; internal set; }
    public int Recoveries { get; internal set; }
    public int CraftedItems { get; internal set; }
    public int RepairedItems { get; internal set; }
    public int ExplosivesThrown { get; internal set; }
    public int VoiceBytes { get; internal set; }
    public int HammerHits { get; internal set; }
    public int Reloads { get; internal set; }
    public int Shots { get; internal set; }
    public int CollectiblesPickedUp { get; internal set; }
    public int GrowablesGathered { get; internal set; }
    public int Chats { get; internal set; }
    public int NPCKills { get; internal set; }
    public int MeleeAttacks { get; internal set; }
    .............
}

Anyone seeing something i don't see?

Easiest way to check would be to add debug output for each thing that isn't null checked to test what is null.

Thanks Wulf, and I found it. It was a silly mistake really, I wasn't checking if the hitInfo was null. 😄

It helps, if only temporary. ;)