NullReferenceException at FormatMessageSolved

Hi! Am getting this error message when the server restarts

Failed to call hook 'OnSignUpdated' on plugin 'SignsMonitor v1.0.0' (NullReferenceException: Object reference not set to an instance of an object)
  at Oxide.Plugins.SignsMonitor.FormatMessage (System.String key, BasePlayer player, BasePlayer fireworkOwner, BaseEntity entity) [0x0000e] in <446797aad0e5475db9bb4a3086b3e458>:0 
  at Oxide.Plugins.SignsMonitor.SendDiscordEmbed (System.Byte[] image, BasePlayer player, System.UInt64 signageOwnerId, BaseEntity entity) [0x00007] in <446797aad0e5475db9bb4a3086b3e458>:0 
  at Oxide.Plugins.SignsMonitor.OnSignUpdated (ISignage signage, BasePlayer player, System.Int32 textureIndex) [0x00034] in <446797aad0e5475db9bb4a3086b3e458>:0 
  at Oxide.Plugins.SignsMonitor.DirectCallHook (System.String name, System.Object& ret, System.Object[] args) [0x00101] in <446797aad0e5475db9bb4a3086b3e458>:0 
  at Oxide.Plugins.CSharpPlugin.InvokeMethod (Oxide.Core.Plugins.HookMethod method, System.Object[] args) [0x00079] in <cc99cb05b42e4ea494cdf294badea406>:0 
  at Oxide.Core.Plugins.CSPlugin.OnCallHook (System.String name, System.Object[] args) [0x000d8] in <99d545163fdd4d57a562df7989f2ca0a>:0 
  at Oxide.Core.Plugins.Plugin.CallHook (System.String hook, System.Object[] args) [0x00060] in <99d545163fdd4d57a562df7989f2ca0a>:0 
 

Yep, just got this as well (not at restart, just randomly)

Same:

(and it keeps happening - is very spamming in the console when it does :()

Failed to call hook 'OnSignUpdated' on plugin 'SignsMonitor v1.0.0' (NullReferenceException: Object reference not set to an instance of an object)
  at Oxide.Plugins.SignsMonitor.FormatMessage (System.String key, BasePlayer player, BasePlayer fireworkOwner, BaseEntity entity) [0x0002e] in <a649cb2706fe4d3da690cda5e7491cd3>:0 
  at Oxide.Plugins.SignsMonitor.SendDiscordEmbed (System.Byte[] image, BasePlayer player, System.UInt64 signageOwnerId, BaseEntity entity) [0x00007] in <a649cb2706fe4d3da690cda5e7491cd3>:0 
  at Oxide.Plugins.SignsMonitor.OnSignUpdated (ISignage signage, BasePlayer player, System.Int32 textureIndex) [0x00034] in <a649cb2706fe4d3da690cda5e7491cd3>:0 
  at Oxide.Plugins.SignsMonitor.DirectCallHook (System.String name, System.Object& ret, System.Object[] args) [0x00101] in <a649cb2706fe4d3da690cda5e7491cd3>:0 
  at Oxide.Plugins.CSharpPlugin.InvokeMethod (Oxide.Core.Plugins.HookMethod method, System.Object[] args) [0x00079] in <b6af59acae274e5a94fb209dfc179b8f>:0 
  at Oxide.Core.Plugins.CSPlugin.OnCallHook (System.String name, System.Object[] args) [0x000d8] in <bae5f1223fce49c493b01571c99dce02>:0 
  at Oxide.Core.Plugins.Plugin.CallHook (System.String hook, System.Object[] args) [0x00060] in <bae5f1223fce49c493b01571c99dce02>:0 

Ok I've found the issue here. Basically if the sign owner is offline or dead it'll return a null ID for that player and freak out.

I've had to change a bit to fix it (basically switching from checking for a baseplayer to an Iplayer as that covers dead, awake and sleeping), though I had to do it quickly and there's probably a simpler way. This works though:

// ReSharper disable CheckNamespace

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.Networking;
using Oxide.Core.Libraries.Covalence;

namespace Oxide.Plugins
{
    [Info("Signs Monitor", "Mr. Blue", "1.0.1")]
    [Description("Send a message to discord with an image of the signage content set by the user")]
    public class SignsMonitor : RustPlugin
    {
        #region Configuration

        private const string WEBHOOK_INTRO =
            "https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks";

        private static int? _embedColor;

        private class PluginConfiguration
        {
            [JsonProperty("Discord Webhook")] public string DiscordWebhook;
            [JsonProperty("Embed Color")] public string EmbedColor;
        }

        private PluginConfiguration _config;

        private void Init()
        {
            _config = Config.ReadObject<PluginConfiguration>();
            _embedColor = FromHex(_config.EmbedColor);

            if (_config.DiscordWebhook == WEBHOOK_INTRO)
            {
                PrintWarning($"Please set the discord webhook in the configuration file! ({WEBHOOK_INTRO})");
                Unsubscribe(nameof(OnSignUpdated));
                Unsubscribe(nameof(OnItemPainted));
            }
        }

        protected override void LoadDefaultConfig()
        {
            Config.WriteObject(GetDefaultConfig(), true);
        }

        private PluginConfiguration GetDefaultConfig()
        {
            return new PluginConfiguration
            {
                DiscordWebhook = WEBHOOK_INTRO,
                EmbedColor = "#54a8fc"
            };
        }

        #endregion

        #region Localization

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["EmbedTitle"] = "Signage changed!",
                ["EmbedBody"] =
                    "**Player:**\n{playerName}\n{playerId}\n[Steam Profile](https://steamcommunity.com/profiles/{playerId})\n\n" +
                    "**Signage owner:**\n{ownerName}\n{ownerId}\n[Steam Profile](https://steamcommunity.com/profiles/{ownerId})\n\n" +
                    "**Signage info:**\nPosition: {signagePosition}\nType: {signageType}\n\n" +
                    "**Server:\n**{serverName}"
            }, this);
        }

        private string FormatMessage(string key, IPlayer player, IPlayer fireworkOwner, BaseEntity entity)
        {
            return lang.GetMessage(key, this)
                .Replace("{playerName}", player.Name)
                .Replace("{playerId}", player.Id)
                .Replace("{ownerName}", fireworkOwner.Name)
                .Replace("{ownerId}", fireworkOwner.Id)
                .Replace("{signagePosition}", entity.transform.position.ToString().Replace(" ", string.Empty))
                .Replace("{signageType}", entity.ShortPrefabName)
                .Replace("{serverName}", covalence.Server.Name);
        }

        #endregion

        #region Signage Logic

        private void OnSignUpdated(ISignage signage, BasePlayer player, int textureIndex = 0)
        {
            uint crc = signage.GetTextureCRCs()[textureIndex];
            var encodedPng = FileStorage.server.Get(crc, FileStorage.Type.png, signage.NetworkID, (uint)textureIndex);
            if (encodedPng == null) return;

            var entity = signage as BaseEntity;
            SendDiscordEmbed(encodedPng, player.IPlayer, entity.OwnerID, entity);
        }

        private void OnItemPainted(PaintedItemStorageEntity entity, Item item, BasePlayer player, byte[] encodedPng)
        {
            if (entity._currentImageCrc == 0)
            {
                return;
            }

            SendDiscordEmbed(encodedPng, player.IPlayer, entity.OwnerID, entity);
        }

        private void SendDiscordEmbed(byte[] image, IPlayer player, ulong signageOwnerId, BaseEntity entity)
        {
            var signOwner = covalence.Players.FindPlayer(signageOwnerId.ToString());

            var title = FormatMessage("EmbedTitle", player, signOwner, entity);
            var description = FormatMessage("EmbedBody", player, signOwner, entity);

            var payload = new
            {
                embeds = new[]
                {
                    new
                    {
                        title,
                        description,
                        color = _embedColor,
                        image = new
                        {
                            url = "attachment://image.png"
                        },
                        timestamp = DateTime.Now
                    }
                }
            };

            var form = new WWWForm();
            form.AddBinaryData("file", image, "image.png");
            form.AddField("payload_json", JsonConvert.SerializeObject(payload));

            ServerMgr.Instance.StartCoroutine(HandleUpload(_config.DiscordWebhook, form));
        }

        private IEnumerator HandleUpload(string url, WWWForm data)
        {
            var www = UnityWebRequest.Post(url, data);
            yield return http://www.SendWebRequest();

            if (www.isNetworkError || http://www.isHttpError)
            {
                Puts($"Failed to post sign image to discord: {www.error}");
            }
        }

        #endregion

        #region Helpers

        private static int? FromHex(string value)
        {
            var match = Regex.Match(value, "#?([0-9a-f]{6})");
            if (!match.Success)
            {
                return null;
            }

            return int.Parse(match.Groups[1].Value, System.Globalization.NumberStyles.HexNumber);
        }

        #endregion
    }
}​

Should be fixed in the latest update!

Thanks!

Thank you!

Locked automatically