Decompiled source of Signs v0.5.3

jcdcdev.Valheim.Signs/jcdcdev.Valheim.Signs.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using SimpleJson;
using TMPro;
using UnityEngine;
using jcdcdev.Valheim.Core;
using jcdcdev.Valheim.Core.Extensions;
using jcdcdev.Valheim.Core.RPC;
using jcdcdev.Valheim.Signs.Converters;
using jcdcdev.Valheim.Signs.Extensions;
using jcdcdev.Valheim.Signs.Models;
using jcdcdev.Valheim.Signs.RPC;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: ComVisible(false)]
[assembly: Guid("915F70BA-D5B8-46C3-9D78-BCD7472FBE25")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("jcdcdev.Valheim.Signs")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © James Carter 2024")]
[assembly: AssemblyFileVersion("0.5.3")]
[assembly: AssemblyInformationalVersion("0.5.3+3fe54694c190f8acffd67e10d705b42dd06a9038")]
[assembly: AssemblyProduct("jcdcdev.Valheim.Signs")]
[assembly: AssemblyTitle("jcdcdev.Valheim.Signs")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.5.3.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace jcdcdev.Valheim.Core
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public abstract class BasePlugin<TPlugin> : BaseUnityPlugin where TPlugin : class
	{
		private static TPlugin? _instance;

		private readonly IDictionary<string, object?> _cache = new Dictionary<string, object>();

		public readonly ISimpleRPC BadRequest = new BadRequest();

		public readonly ManualLogSource Logger = Logger.CreateLogSource("Signs");

		private Harmony? _harmony;

		protected abstract string PluginId { get; }

		protected string ConfigBasePath => $"{Paths.ConfigPath}{Path.DirectorySeparatorChar}{PluginId}{Path.DirectorySeparatorChar}";

		public static TPlugin Instance => _instance ?? throw new InvalidOperationException("Plugin is not loaded");

		[UsedImplicitly]
		private void Awake()
		{
			_instance = (this as TPlugin) ?? throw new InvalidOperationException("Plugin " + PluginId + " is not initialised correctly");
			EnsureConfigDirectoryExists();
			BadRequest.Initialise(NetworkManager.Instance);
			OnAwake();
			_harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), PluginId);
		}

		[UsedImplicitly]
		private void OnDestroy()
		{
			_instance = null;
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void EnsureConfigDirectoryExists()
		{
			if (!Directory.Exists(ConfigBasePath))
			{
				Directory.CreateDirectory(ConfigBasePath);
			}
		}

		public void AddCacheItem(string key, object? value)
		{
			_cache[key] = value;
		}

		protected TItem? GetCacheItem<TItem>(string key) where TItem : class
		{
			if (!_cache.TryGetValue(key, out object value))
			{
				return null;
			}
			if (value is TItem result)
			{
				return result;
			}
			return null;
		}

		protected virtual void OnAwake()
		{
		}

		protected T? ReadJsonFromFile<T>(string path) where T : class
		{
			try
			{
				return JsonHelper.FromJson<T>(File.ReadAllText(path));
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
				return null;
			}
		}

		protected void WriteJsonToFile(object model, string path)
		{
			try
			{
				string contents = JsonHelper.ToJson(model);
				File.WriteAllText(path, contents);
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
			}
		}
	}
	public static class JsonHelper
	{
		public static string? ToJson(object? obj)
		{
			if (obj == null)
			{
				return null;
			}
			return SimpleJson.SerializeObject(obj);
		}

		public static T? FromJson<T>(string? json) where T : class
		{
			if (json == null || string.IsNullOrWhiteSpace(json))
			{
				return null;
			}
			return SimpleJson.DeserializeObject<T>(json);
		}
	}
}
namespace jcdcdev.Valheim.Core.RPC
{
	public class BadRequest : SimpleRPC
	{
		public override IEnumerator Client(long sender, ZPackage? pkg)
		{
			if (sender != ZRoutedRpc.instance.GetServerPeerID())
			{
				Logger.LogWarning((object)"BadRequest called with invalid sender.");
				yield break;
			}
			if (pkg == null || pkg.Size() <= 0)
			{
				Logger.LogWarning((object)"BadRequest called with no payload.");
				yield break;
			}
			string text = pkg.ReadString();
			if (text == "")
			{
				Logger.LogWarning((object)"BadRequest called with empty payload.");
				yield break;
			}
			Logger.LogWarning((object)("BadRequest called: " + text));
			((Terminal)Chat.instance).AddString("Server", "<color=\"red\">" + text + "</color>", (Type)1, false);
		}

		public override IEnumerator Server(long sender, ZPackage? pkg)
		{
			return SimpleRPC.Noop(sender, pkg);
		}
	}
	public interface ISimpleRPC
	{
		void Send(long peerId, ZPackage pkg);

		void Send(long peerId, string? message);

		void Send(long peerId, object? model);

		void SendAll(ZPackage pkg);

		void SendAll(string? message);

		void SendAll(object? model);

		void SendToServer(ZPackage pkg);

		void SendToServer(string? message);

		void SendToServer(object? model);

		IEnumerator ClientInternal(long sender, ZPackage? pkg);

		IEnumerator ServerInternal(long sender, ZPackage? pkg);

		void Initialise(NetworkManager instance);
	}
	public abstract class SimpleRPC : ISimpleRPC
	{
		private CustomRPC _rpc;

		public void Send(long peerId, ZPackage pkg)
		{
			Logger.LogDebug((object)$"\n\nRPC SENT - {_rpc.Name}\n\nRecipient: {peerId}\n");
			_rpc.SendPackage(peerId, pkg);
		}

		public void Send(long peerId, string? message)
		{
			Send(peerId, CreatePackage(message));
		}

		public void Send(long peerId, object? model)
		{
			Send(peerId, JsonHelper.ToJson(model));
		}

		public void SendAll(ZPackage pkg)
		{
			Send(ZRoutedRpc.Everybody, pkg);
		}

		public void SendAll(string? message)
		{
			Send(ZRoutedRpc.Everybody, message);
		}

		public void SendAll(object? model)
		{
			Send(ZRoutedRpc.Everybody, model);
		}

		public void SendToServer(ZPackage pkg)
		{
			Send(ZRoutedRpc.instance.GetServerPeerID(), pkg);
		}

		public void SendToServer(string? message)
		{
			SendToServer(CreatePackage(message));
		}

		public void SendToServer(object? model)
		{
			SendToServer(JsonHelper.ToJson(model));
		}

		public IEnumerator ClientInternal(long sender, ZPackage? pkg)
		{
			Logger.LogDebug((object)$"\n\nRPC RECEIVED - {_rpc.Name}\n\nRecipient Type: CLIENT\nSender: {sender}\n");
			yield return Client(sender, pkg);
		}

		public IEnumerator ServerInternal(long sender, ZPackage? pkg)
		{
			Logger.LogDebug((object)$"\n\nRPC RECEIVED - {_rpc.Name}\n\nRecipient Type: SERVER\nSender: {sender}\n");
			yield return Server(sender, pkg);
		}

		public void Initialise(NetworkManager instance)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			//IL_0042: Expected O, but got Unknown
			string name = GetType().Name;
			Logger.LogInfo((object)("Initialising RPC for " + name));
			_rpc = NetworkManager.Instance.AddRPC(name, new CoroutineHandler(Server), new CoroutineHandler(Client));
		}

		protected static IEnumerator Noop(long sender, ZPackage? pkg)
		{
			yield break;
		}

		private static ZPackage CreatePackage(string? message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(message);
			return val;
		}

		public abstract IEnumerator Client(long sender, ZPackage? pkg);

		public abstract IEnumerator Server(long sender, ZPackage? pkg);
	}
	public static class ZNetHelper
	{
		public static bool IsLocalOrServer(this ZNet znet)
		{
			if (!znet.IsLocal())
			{
				return ZNetExtension.IsServerInstance(znet);
			}
			return true;
		}

		public static bool IsLocalOrClient(this ZNet znet)
		{
			if (!znet.IsLocal())
			{
				return ZNetExtension.IsClientInstance(znet);
			}
			return true;
		}

		public static bool IsLocal(this ZNet znet)
		{
			return ZNetExtension.IsLocalInstance(znet);
		}
	}
}
namespace jcdcdev.Valheim.Core.Extensions
{
	public static class PlayerExtensions
	{
		private const string CustomDataKeyPrefix = "jcdcdev-";

		public static T GetCustomData<T>(this Player player, string key, T defaultValue = default(T))
		{
			key = "jcdcdev-" + key;
			try
			{
				if (player.m_customData.TryGetValue(key, out var value))
				{
					Logger.LogDebug((object)("Getting custom data for player: " + player.GetPlayerName() + " with key: " + key + " and value: " + value));
					T val = (T)Convert.ChangeType(value, typeof(T));
					return (T)((val != null) ? ((object)val) : ((object)defaultValue));
				}
				return defaultValue;
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
				return defaultValue;
			}
		}

		public static void SetCustomData(this Player player, string key, string value)
		{
			Logger.LogDebug((object)("Setting custom data for player: " + player.GetPlayerName() + " with key: " + key + " and value: " + value));
			key = "jcdcdev-" + key;
			player.m_customData[key] = value;
		}
	}
	public static class SkillExtensions
	{
		public static string? ToEmoji(this SkillType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected I4, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected I4, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Invalid comparison between Unknown and I4
			switch ((int)type)
			{
			default:
				switch (type - 100)
				{
				default:
					if ((int)type != 999)
					{
						break;
					}
					return string.Empty;
				case 0:
					return "\ud83e\udd98";
				case 1:
					return "\ud83e\udda5";
				case 2:
					return "\ud83c\udfc3";
				case 3:
					return "\ud83c\udfca";
				case 4:
					return "\ud83c\udfa3";
				case 10:
					return "\ud83c\udfc7";
				case 5:
				case 6:
				case 7:
				case 8:
				case 9:
					break;
				}
				return string.Empty;
			case 0:
				return null;
			case 1:
				return "⚔\ufe0f";
			case 2:
				return "\ud83d\udd2a";
			case 3:
				return "\ud83c\udfcf";
			case 4:
				return "\ud83d\udd31";
			case 5:
				return "\ud83d\udde1\ufe0f";
			case 6:
				return "\ud83d\udee1\ufe0f";
			case 7:
				return "\ud83e\ude93";
			case 8:
				return "\ud83c\udff9";
			case 9:
				return "\ud83d\udd25";
			case 10:
				return "\ud83e\ude78";
			case 11:
				return "\ud83e\udd4a";
			case 12:
				return "⛏\ufe0f";
			case 13:
				return "\ud83e\ude93";
			case 14:
				return "\ud83c\udff9";
			}
		}
	}
	public static class StringExtensions
	{
		public static bool InvariantEquals(this string value, string compare)
		{
			return value.Equals(compare, StringComparison.InvariantCultureIgnoreCase);
		}

		public static bool StartsWithInvariant(this string value, string compare)
		{
			return value.StartsWith(compare, StringComparison.InvariantCultureIgnoreCase);
		}
	}
	public static class TimeExtensions
	{
		private static float SmoothDayFraction => EnvMan.instance.m_smoothDayFraction;

		public static int CurrentDay => EnvMan.instance.GetCurrentDay();

		public static string GetTimeFormat(string? originalText)
		{
			if (originalText == null || string.IsNullOrWhiteSpace(originalText))
			{
				return "HH:mm";
			}
			List<string> source = originalText.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).ToList();
			bool flag = source.Any((string x) => x == "12");
			return source.LastOrDefault((string x) => x != "12") switch
			{
				"s" => flag ? "h:mm:ss tt" : "HH:mm:ss", 
				"f" => "f", 
				"F" => "F", 
				"g" => "g", 
				"G" => "G", 
				"r" => "r", 
				"R" => "R", 
				"U" => "U", 
				_ => flag ? "h:mm tt" : "HH:mm", 
			};
		}

		public static string GetFuzzyTime()
		{
			string[] array = new string[24]
			{
				"Midnight", "Early Morning", "Early Morning", "Before Dawn", "Before Dawn", "Dawn", "Dawn", "Morning", "Morning", "Late Morning",
				"Late Morning", "Midday", "Midday", "Early Afternoon", "Early Afternoon", "Afternoon", "Afternoon", "Evening", "Evening", "Night",
				"Night", "Late Night", "Late Night", "Midnight"
			};
			int num = Math.Min((int)((float)array.Length * SmoothDayFraction), array.Length - 1);
			return array[num] ?? "";
		}

		public static DateTime ServerTimeNow()
		{
			int num = (int)(SmoothDayFraction * 24f);
			int num2 = (int)((SmoothDayFraction * 24f - (float)num) * 60f);
			int second = (int)(((SmoothDayFraction * 24f - (float)num) * 60f - (float)num2) * 60f);
			DateTime now = DateTime.Now;
			return new DateTime(now.Year, now.Month, now.Day, num, num2, second);
		}

		public static DateTime LocalNow(TimeZoneInfo? timeZone)
		{
			if (timeZone == null)
			{
				timeZone = TimeZoneInfo.Local;
			}
			return TimeZoneInfo.ConvertTime(DateTime.UtcNow, timeZone);
		}

		public static string ToEmojiClock(this DateTime? time)
		{
			int num = time?.Hour ?? 0;
			int num2 = time?.Minute ?? 0;
			if (num2 >= 0 && num2 < 30)
			{
				return num switch
				{
					0 => "\ud83d\udd5b", 
					1 => "\ud83d\udd50", 
					2 => "\ud83d\udd51", 
					3 => "\ud83d\udd52", 
					4 => "\ud83d\udd53", 
					5 => "\ud83d\udd54", 
					6 => "\ud83d\udd55", 
					7 => "\ud83d\udd56", 
					8 => "\ud83d\udd57", 
					9 => "\ud83d\udd58", 
					10 => "\ud83d\udd59", 
					11 => "\ud83d\udd5a", 
					12 => "\ud83d\udd5b", 
					13 => "\ud83d\udd50", 
					14 => "\ud83d\udd51", 
					15 => "\ud83d\udd52", 
					16 => "\ud83d\udd53", 
					17 => "\ud83d\udd54", 
					18 => "\ud83d\udd55", 
					19 => "\ud83d\udd56", 
					20 => "\ud83d\udd57", 
					21 => "\ud83d\udd58", 
					22 => "\ud83d\udd59", 
					23 => "\ud83d\udd5a", 
					_ => "\ud83d\udd5b", 
				};
			}
			return num switch
			{
				0 => "\ud83d\udd67", 
				1 => "\ud83d\udd5c", 
				2 => "\ud83d\udd5d", 
				3 => "\ud83d\udd5e", 
				4 => "\ud83d\udd5f", 
				5 => "\ud83d\udd60", 
				6 => "\ud83d\udd61", 
				7 => "\ud83d\udd62", 
				8 => "\ud83d\udd63", 
				9 => "\ud83d\udd64", 
				10 => "\ud83d\udd65", 
				11 => "\ud83d\udd66", 
				12 => "\ud83d\udd67", 
				13 => "\ud83d\udd5c", 
				14 => "\ud83d\udd5d", 
				15 => "\ud83d\udd5e", 
				16 => "\ud83d\udd5f", 
				17 => "\ud83d\udd60", 
				18 => "\ud83d\udd61", 
				19 => "\ud83d\udd62", 
				20 => "\ud83d\udd63", 
				21 => "\ud83d\udd64", 
				22 => "\ud83d\udd65", 
				23 => "\ud83d\udd66", 
				_ => "\ud83d\udd67", 
			};
		}
	}
}
namespace jcdcdev.Valheim.Signs
{
	public static class Constants
	{
		public static class CacheKeys
		{
			public static string DeathLeaderboard => "DeathLeaderboard";
		}

		public const string PluginId = "jcdcdev.Valheim.Signs";

		public const string PluginName = "jcdcdev - Signs";

		public static readonly Regex HandlebarRegexPattern = new Regex("{{([^}}]+)}}", RegexOptions.Compiled);

		public static readonly Regex HoverTextRegexPattern = new Regex("\"([^\"]*)\"", RegexOptions.Compiled);

		public const string DefaultHoverError = "Invalid sign";

		public static string ErrorMessage(string error)
		{
			return "<color=\"red\">ERROR</color>\n" + error;
		}
	}
	[BepInPlugin("jcdcdev.Valheim.Signs", "jcdcdev - Signs", "0.5.3")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class SignsPlugin : BasePlugin<SignsPlugin>
	{
		public static readonly bool IsAzuSignsInstalled = Chainloader.PluginInfos.ContainsKey("Azumatt.AzuSigns");

		private static readonly List<IAmADynamicSign> DynamicSigns = new List<IAmADynamicSign>();

		public readonly ISimpleRPC DeathLeaderboardUpdateRequest = new DeathLeaderboardUpdateRequest();

		public readonly ISimpleRPC DeathLeaderboardUpdateResponse = new DeathLeaderboardUpdateResponse();

		public readonly ISimpleRPC DeathUpdate = new DeathUpdate();

		private string LeaderboardPath => base.ConfigBasePath + "death-leaderboard.json";

		protected override string PluginId => "jcdcdev.Valheim.Signs";

		protected override void OnAwake()
		{
			DeathUpdate.Initialise(NetworkManager.Instance);
			DeathLeaderboardUpdateRequest.Initialise(NetworkManager.Instance);
			DeathLeaderboardUpdateResponse.Initialise(NetworkManager.Instance);
			AddSigns();
			EnsureLeaderboardFileExists();
		}

		private void EnsureLeaderboardFileExists()
		{
			if (!File.Exists(LeaderboardPath))
			{
				PlayerDeathLeaderBoard model = new PlayerDeathLeaderBoard
				{
					Players = new List<PlayerDeathInfo>(),
					Updated = DateTime.MinValue
				};
				WriteJsonToFile(model, LeaderboardPath);
			}
		}

		private void AddSigns()
		{
			Type[] types = Assembly.GetExecutingAssembly().GetTypes();
			foreach (Type type in types)
			{
				if (!type.IsAbstract && !type.IsInterface && typeof(IAmADynamicSign).IsAssignableFrom(type) && Activator.CreateInstance(type) is IAmADynamicSign sign)
				{
					AddSign(sign);
				}
			}
		}

		private void AddSign(IAmADynamicSign sign)
		{
			DynamicSigns.Add(sign);
		}

		public bool TryGetSignText(Sign sign, string? input, out string? output)
		{
			Sign sign2 = sign;
			string input2 = input;
			output = null;
			if (input2 == null)
			{
				Logger.LogDebug((object)"No token found.");
				return false;
			}
			IAmADynamicSign amADynamicSign = DynamicSigns.FirstOrDefault((IAmADynamicSign x) => x.CanConvert(sign2, input2));
			if (amADynamicSign == null)
			{
				Logger.LogWarning((object)("No converter found for " + input2));
				return false;
			}
			string signText = amADynamicSign.GetSignText(sign2, input2);
			if (signText == null || Utility.IsNullOrWhiteSpace(signText))
			{
				Logger.LogWarning((object)"No result found.");
				return false;
			}
			output = signText;
			return true;
		}

		public bool Client_GetSignText(Sign sign, string signText, out string output)
		{
			output = signText;
			foreach (string item in Client_GetTokenValue(signText))
			{
				if (TryGetSignText(sign, item, out string output2) && output2 != null)
				{
					output = output.Replace("{{" + item + "}}", output2);
				}
			}
			return output != signText;
		}

		private static IEnumerable<string> Client_GetTokenValue(string originalText)
		{
			MatchCollection matchCollection = Constants.HandlebarRegexPattern.Matches(originalText);
			if (matchCollection.Count == 0)
			{
				return new List<string>();
			}
			List<string> list = new List<string>();
			foreach (Match item in matchCollection)
			{
				list.Add(item.Groups[1].Value);
			}
			return list;
		}

		public bool Client_GetSignHoverText(Sign sign, string originalText, out string output)
		{
			Logger.LogDebug((object)("Sign Hover: " + originalText));
			output = string.Empty;
			IEnumerable<string> enumerable = Client_GetTokenValue(originalText);
			List<string> list = new List<string>();
			foreach (string item in enumerable)
			{
				if (TryGetSignHoverText(sign, item, out string hover) && hover != null)
				{
					list.Add(hover);
				}
			}
			if (list.Count == 0)
			{
				return false;
			}
			string text = "[<color=\"yellow\">DYNAMIC</color>] " + string.Join(" ", list);
			output = Constants.HoverTextRegexPattern.Replace(originalText, text ?? "");
			Logger.LogDebug((object)("Sign Hover: " + output));
			return output != originalText;
		}

		private bool TryGetSignHoverText(Sign sign, string? originalValue, out string? hover)
		{
			Sign sign2 = sign;
			string originalValue2 = originalValue;
			hover = null;
			if (originalValue2 == null)
			{
				Logger.LogDebug((object)"No token found.");
				return false;
			}
			IAmADynamicSign amADynamicSign = DynamicSigns.FirstOrDefault((IAmADynamicSign x) => x.CanConvert(sign2, originalValue2));
			if (amADynamicSign == null)
			{
				Logger.LogWarning((object)"No converter found.");
				return false;
			}
			string signHoverText = amADynamicSign.GetSignHoverText(sign2, originalValue2);
			if (signHoverText == null || Utility.IsNullOrWhiteSpace(signHoverText))
			{
				Logger.LogWarning((object)"No hover text found.");
				return false;
			}
			hover = signHoverText;
			return true;
		}

		public PlayerDeathLeaderBoard? Server_GetDeathLeaderboard()
		{
			if (!ZNet.instance.IsLocalOrServer())
			{
				Logger.LogWarning((object)"GetServerDeathLeaderBoardModel called on client.");
				return null;
			}
			try
			{
				string leaderboardPath = LeaderboardPath;
				if (!File.Exists(leaderboardPath))
				{
					Logger.LogWarning((object)"Death Leaderboard file not found.");
					return null;
				}
				PlayerDeathLeaderBoard playerDeathLeaderBoard = ReadJsonFromFile<PlayerDeathLeaderBoard>(leaderboardPath);
				if (playerDeathLeaderBoard == null)
				{
					Logger.LogWarning((object)"Death Leaderboard file is empty.");
					return null;
				}
				Logger.LogDebug((object)("File Based Death Leaderboard:\n\n" + playerDeathLeaderBoard.GetSignText()));
				return playerDeathLeaderBoard;
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
				return null;
			}
		}

		public PlayerDeathLeaderBoard? Client_GetOrRequestDeathLeaderboard()
		{
			if (!ZNet.instance.IsLocalOrClient())
			{
				Logger.LogWarning((object)"Client method called on server: Client_GetOrRequestDeathLeaderboard");
				return null;
			}
			PlayerDeathLeaderBoard cacheItem = GetCacheItem<PlayerDeathLeaderBoard>(Constants.CacheKeys.DeathLeaderboard);
			if (cacheItem != null)
			{
				Logger.LogDebug((object)"Using cached death leaderboard.");
				return cacheItem;
			}
			ISimpleRPC deathLeaderboardUpdateRequest = DeathLeaderboardUpdateRequest;
			DateTime minValue = DateTime.MinValue;
			deathLeaderboardUpdateRequest.SendToServer((object?)minValue.Ticks);
			return null;
		}

		public void Server_UpdateDeath(PlayerDeathInfo data)
		{
			PlayerDeathInfo data2 = data;
			string leaderboardPath = LeaderboardPath;
			PlayerDeathLeaderBoard playerDeathLeaderBoard = ReadJsonFromFile<PlayerDeathLeaderBoard>(leaderboardPath);
			if (playerDeathLeaderBoard == null)
			{
				Logger.LogWarning((object)"Death Leaderboard file not found.");
				return;
			}
			PlayerDeathInfo playerDeathInfo = playerDeathLeaderBoard.Players.FirstOrDefault((PlayerDeathInfo x) => x.Id == data2.Id);
			if (playerDeathInfo == null)
			{
				Logger.LogWarning((object)"Player not found in leaderboard.");
				playerDeathLeaderBoard.Players.Add(data2);
			}
			else
			{
				if (playerDeathInfo.Deaths == data2.Deaths)
				{
					Logger.LogDebug((object)"Player death count is the same. No update needed.");
					return;
				}
				Logger.LogInfo((object)$"Updating Death Count - {playerDeathInfo.Name}: {playerDeathInfo.Deaths} => {data2.Deaths}");
				playerDeathInfo.Deaths = data2.Deaths;
			}
			playerDeathLeaderBoard.Players = playerDeathLeaderBoard.Players.OrderByDescending((PlayerDeathInfo x) => x.Deaths).ToList();
			playerDeathLeaderBoard.Updated = DateTime.UtcNow;
			DeathLeaderboardUpdateResponse.SendAll((object?)playerDeathLeaderBoard);
			WriteJsonToFile(playerDeathLeaderBoard, leaderboardPath);
		}

		public void Client_SendDeathUpdateRequest(Player player)
		{
			if (!ZNet.instance.IsLocalOrClient())
			{
				Logger.LogWarning((object)"InvokeDeathUpdateRequest called on server.");
			}
			else
			{
				DeathUpdate.SendToServer((object?)player.GetDeathInfo());
			}
		}
	}
	public static class VersionInfo
	{
		public const string Version = "0.5.3";
	}
}
namespace jcdcdev.Valheim.Signs.RPC
{
	public class DeathLeaderboardUpdateRequest : SimpleRPC
	{
		public override IEnumerator Client(long sender, ZPackage? pkg)
		{
			return SimpleRPC.Noop(sender, pkg);
		}

		public override IEnumerator Server(long sender, ZPackage? pkg)
		{
			if (pkg == null)
			{
				Logger.LogWarning((object)"No payload received for DeathUpdate");
				BasePlugin<SignsPlugin>.Instance.BadRequest.Send(sender, "No payload received for DeathUpdate");
				yield break;
			}
			string s = pkg.ReadString();
			DateTime dateTime = DateTime.MinValue;
			if (long.TryParse(s, out var result))
			{
				dateTime = new DateTime(result);
			}
			try
			{
				PlayerDeathLeaderBoard playerDeathLeaderBoard = BasePlugin<SignsPlugin>.Instance.Server_GetDeathLeaderboard();
				if (playerDeathLeaderBoard == null)
				{
					yield break;
				}
				if (playerDeathLeaderBoard.Updated <= dateTime)
				{
					Logger.LogDebug((object)$"DeathLeaderboard is up to date. Server Last Updated: {playerDeathLeaderBoard.Updated} Client Last Updated: {dateTime}");
					yield break;
				}
				BasePlugin<SignsPlugin>.Instance.DeathLeaderboardUpdateResponse.Send(sender, (object?)playerDeathLeaderBoard);
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
			}
			yield return null;
		}
	}
	public class DeathLeaderboardUpdateResponse : SimpleRPC
	{
		public override IEnumerator Client(long sender, ZPackage? pkg)
		{
			if (pkg == null || pkg.Size() <= 0)
			{
				Logger.LogWarning((object)"DeathLeaderboardResponse called with no payload.");
				yield break;
			}
			string text = pkg.ReadString();
			if (text == "")
			{
				Logger.LogWarning((object)"DeathLeaderboardResponse called with empty payload.");
				yield break;
			}
			try
			{
				PlayerDeathLeaderBoard playerDeathLeaderBoard = JsonHelper.FromJson<PlayerDeathLeaderBoard>(text);
				if (playerDeathLeaderBoard == null || playerDeathLeaderBoard.Updated == DateTime.MinValue)
				{
					Logger.LogWarning((object)"DeathLeaderboardResponse called with invalid payload.");
				}
				else
				{
					BasePlugin<SignsPlugin>.Instance.AddCacheItem(Constants.CacheKeys.DeathLeaderboard, playerDeathLeaderBoard);
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
			}
		}

		public override IEnumerator Server(long sender, ZPackage? pkg)
		{
			if (!ZNet.instance.IsLocal())
			{
				return SimpleRPC.Noop(sender, pkg);
			}
			return Client(sender, pkg);
		}
	}
	public class DeathUpdate : SimpleRPC
	{
		public override IEnumerator Client(long sender, ZPackage? pkg)
		{
			return SimpleRPC.Noop(sender, pkg);
		}

		public override IEnumerator Server(long sender, ZPackage? pkg)
		{
			if (pkg == null)
			{
				Logger.LogWarning((object)"No payload received for DeathUpdate");
				BasePlugin<SignsPlugin>.Instance.BadRequest.Send(sender, "Failed to update death count.");
				yield break;
			}
			Logger.LogDebug((object)"DeathUpdate called.");
			PlayerDeathInfo playerDeathInfo = JsonHelper.FromJson<PlayerDeathInfo>(pkg.ReadString());
			if (playerDeathInfo == null)
			{
				Logger.LogWarning((object)"Failed to parse payload for DeathUpdate");
				BasePlugin<SignsPlugin>.Instance.BadRequest.Send(sender, "Failed to update death count.");
			}
			else
			{
				BasePlugin<SignsPlugin>.Instance.Server_UpdateDeath(playerDeathInfo);
			}
		}
	}
}
namespace jcdcdev.Valheim.Signs.Patches
{
	[HarmonyPatch(typeof(Player), "OnDeath")]
	public static class PlayerOnDeath
	{
		public static void Postfix(Player __instance)
		{
			Logger.LogDebug((object)"PlayerOnDeath.Postfix");
			__instance.IncrementDeathCount();
			BasePlugin<SignsPlugin>.Instance.Client_SendDeathUpdateRequest(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	public class PlayerOnSpawned
	{
		public static void Postfix(Player __instance)
		{
			Logger.LogDebug((object)"PlayerOnSpawned.Postfix");
			BasePlugin<SignsPlugin>.Instance.Client_SendDeathUpdateRequest(__instance);
		}
	}
	[HarmonyPatch(typeof(Sign), "GetHoverText")]
	public static class SignGetHoverText
	{
		public static void Postfix(Sign __instance, ref string __result)
		{
			Logger.LogDebug((object)"SignGetHoverText.Postfix");
			try
			{
				string originalText = __result;
				if (!BasePlugin<SignsPlugin>.Instance.Client_GetSignHoverText(__instance, originalText, out string output))
				{
					Logger.LogDebug((object)"SignGetHoverText.Postfix: No output");
				}
				else
				{
					__result = output;
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch(typeof(Sign), "UpdateText")]
	[HarmonyPriority(0)]
	public static class SignUpdateText
	{
		public static void Postfix(Sign __instance)
		{
			Logger.LogDebug((object)"SignUpdateText.Postfix");
			ZNetView nview = __instance.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				Logger.LogWarning((object)"SignUpdateText.Postfix: view is null or invalid");
				return;
			}
			try
			{
				nview.ClaimOwnership();
				ZDO zDO = nview.GetZDO();
				string signText = GetSignText(__instance, zDO);
				if (!BasePlugin<SignsPlugin>.Instance.Client_GetSignText(__instance, signText, out string output))
				{
					Logger.LogDebug((object)"SignUpdateText.Postfix: No output");
				}
				else
				{
					SetSignText(__instance, zDO, output);
				}
			}
			catch (Exception ex)
			{
				Logger.LogError((object)ex);
			}
		}

		private static void SetSignText(Sign __instance, ZDO zdo, string output)
		{
			if (SignsPlugin.IsAzuSignsInstalled)
			{
				zdo.Set("newText", output);
			}
			((TMP_Text)__instance.m_textWidget).text = output;
		}

		private static string GetSignText(Sign __instance, ZDO zdo)
		{
			string @string = zdo.GetString("newText", "");
			string string2 = zdo.GetString("text", "");
			string defaultText = __instance.m_defaultText;
			string text = ((!SignsPlugin.IsAzuSignsInstalled) ? (Utility.IsNullOrWhiteSpace(string2) ? defaultText : string2) : (Utility.IsNullOrWhiteSpace(@string) ? string2 : @string));
			Logger.LogDebug((object)("SignUpdateText.Postfix: NEW TEXT: " + @string));
			Logger.LogDebug((object)("SignUpdateText.Postfix: TEXT: " + string2));
			Logger.LogDebug((object)("SignUpdateText.Postfix: DEFAULT: " + defaultText));
			Logger.LogDebug((object)("SignUpdateText.Postfix: RESULT OUTPUT: " + text));
			return text;
		}
	}
}
namespace jcdcdev.Valheim.Signs.Models
{
	public class PlayerDeathInfo
	{
		public int Deaths { get; set; }

		public long Id { get; set; }

		public string Name { get; set; }
	}
	public class PlayerDeathLeaderBoard
	{
		public List<PlayerDeathInfo> Players { get; set; } = new List<PlayerDeathInfo>();


		public DateTime Updated { get; set; }

		public string GetSignText(int take = int.MaxValue)
		{
			List<PlayerDeathInfo> list = Players.OrderByDescending((PlayerDeathInfo x) => x.Deaths).Take(take).ToList();
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < list.Count; i++)
			{
				PlayerDeathInfo playerDeathInfo = list[i];
				stringBuilder.AppendLine($"{i + 1}. {playerDeathInfo.Name}: {playerDeathInfo.Deaths}");
			}
			return stringBuilder.ToString();
		}
	}
}
namespace jcdcdev.Valheim.Signs.Extensions
{
	public static class PlayerExtensions
	{
		public static PlayerDeathInfo GetDeathInfo(this Player player)
		{
			return new PlayerDeathInfo
			{
				Deaths = player.GetDeathCount(),
				Id = player.GetPlayerID(),
				Name = player.GetPlayerName()
			};
		}

		public static int GetDeathCount(this Player player)
		{
			string key = DeathsWorldKey(ZNet.m_world.m_uid);
			return player.GetCustomData(key, 0);
		}

		public static int IncrementDeathCount(this Player player)
		{
			Logger.LogDebug((object)"Incrementing death count.");
			string key = DeathsWorldKey(ZNet.m_world.m_uid);
			int customData = player.GetCustomData(key, 0);
			customData++;
			player.SetCustomData(key, $"{customData}");
			Logger.LogDebug((object)$"Deaths: {customData}");
			return customData;
		}

		private static string DeathsWorldKey(long worldUid)
		{
			return $"deaths-world-{worldUid}";
		}
	}
}
namespace jcdcdev.Valheim.Signs.Converters
{
	public class ActualTimeSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.StartsWithInvariant("actualTime");
		}

		public string? GetSignText(Sign sign, string input)
		{
			DateTime value = TimeExtensions.LocalNow(TimeZoneInfo.Local);
			if (input.Contains("emoji"))
			{
				return TimeExtensions.ToEmojiClock(value);
			}
			string timeFormat = TimeExtensions.GetTimeFormat(input);
			return value.ToString(timeFormat);
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Actual Time";
		}
	}
	public class ComfortSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				return input.InvariantEquals("comfort");
			}
			return false;
		}

		public string? GetSignText(Sign sign, string input)
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer == null)
			{
				return null;
			}
			return localPlayer.GetComfortLevel().ToString();
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Comfort";
		}
	}
	public class CurrentDaySign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.StartsWith("currentDay", StringComparison.InvariantCultureIgnoreCase);
		}

		public string? GetSignText(Sign sign, string input)
		{
			return TimeExtensions.CurrentDay.ToString();
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Current Day";
		}
	}
	public class DeathCountSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.StartsWithInvariant("deathCount");
		}

		public string? GetSignText(Sign sign, string input)
		{
			string playerId = input.ToLowerInvariant().Replace("deathcount", string.Empty).Trim();
			if (Utility.IsNullOrWhiteSpace(playerId))
			{
				return Player.m_localPlayer?.GetDeathCount().ToString();
			}
			PlayerDeathLeaderBoard playerDeathLeaderBoard = BasePlugin<SignsPlugin>.Instance.Client_GetOrRequestDeathLeaderboard();
			if (playerDeathLeaderBoard == null)
			{
				return Constants.ErrorMessage("Leaderboard not available");
			}
			long id;
			PlayerDeathInfo playerDeathInfo = ((!long.TryParse(playerId, out id)) ? playerDeathLeaderBoard.Players.FirstOrDefault((PlayerDeathInfo x) => x.Name.InvariantEquals(playerId)) : playerDeathLeaderBoard.Players.FirstOrDefault((PlayerDeathInfo x) => x.Id == id));
			if (playerDeathInfo != null)
			{
				return playerDeathInfo.Deaths.ToString();
			}
			return Constants.ErrorMessage("Player not found");
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Deaths Count";
		}
	}
	public class DeathLeaderboardSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.StartsWithInvariant("deathBoard");
		}

		public string? GetSignText(Sign sign, string input)
		{
			int take = 3;
			string[] array = input.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				if (int.TryParse(array[i], out var result))
				{
					take = result;
					break;
				}
			}
			return BasePlugin<SignsPlugin>.Instance.Client_GetOrRequestDeathLeaderboard()?.GetSignText(take);
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Deaths Leaderboard";
		}
	}
	public class FuzzyTime : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.StartsWithInvariant("fuzzyTime");
		}

		public string? GetSignText(Sign sign, string input)
		{
			return TimeExtensions.GetFuzzyTime();
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Fuzzy Time";
		}
	}
	public class GameTimeSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.StartsWith("gameTime", StringComparison.InvariantCultureIgnoreCase);
		}

		public string? GetSignText(Sign sign, string input)
		{
			DateTime value = TimeExtensions.ServerTimeNow();
			if (input.Contains("emoji"))
			{
				return TimeExtensions.ToEmojiClock(value);
			}
			string timeFormat = TimeExtensions.GetTimeFormat(input);
			return value.ToString(timeFormat);
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Game Time";
		}
	}
	public interface IAmADynamicSign
	{
		bool CanConvert(Sign sign, string input);

		string? GetSignText(Sign sign, string input);

		string? GetSignHoverText(Sign sign, string input);
	}
	public class OnlineCountSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.Equals("onlineCount", StringComparison.InvariantCultureIgnoreCase);
		}

		public string? GetSignText(Sign sign, string input)
		{
			return $"{ZNet.instance.GetPlayerList().Count}";
		}

		public string GetSignHoverText(Sign sign, string input)
		{
			return "Online Players";
		}
	}
	public class PlayerHealthSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.InvariantEquals("health");
		}

		public string? GetSignText(Sign sign, string input)
		{
			float health = ((Character)Player.m_localPlayer).GetHealth();
			float maxHealth = ((Character)Player.m_localPlayer).GetMaxHealth();
			return $"{health:F0}/{maxHealth:F0}";
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Health";
		}
	}
	public class PlayerStaminaSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.InvariantEquals("stamina");
		}

		public string? GetSignText(Sign sign, string input)
		{
			float stamina = Player.m_localPlayer.m_stamina;
			float maxStamina = Player.m_localPlayer.m_maxStamina;
			return $"{stamina:F0}/{maxStamina:F0}";
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			return "Stamina";
		}
	}
	public class SkillSign : IAmADynamicSign
	{
		public bool CanConvert(Sign sign, string input)
		{
			return input.StartsWithInvariant("skill");
		}

		public string? GetSignText(Sign sign, string input)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return Constants.ErrorMessage("Player not found");
			}
			bool flag = input.Contains("emoji");
			bool flag2 = input.Contains("label");
			SkillType skill = GetSkill(input);
			if ((int)skill == 0)
			{
				return Constants.ErrorMessage("Invalid skill");
			}
			float skillLevel = ((Character)localPlayer).GetSkillLevel(skill);
			string text = string.Empty;
			if (flag)
			{
				text = skill.ToEmoji() + " ";
			}
			if (flag2)
			{
				text += $"{skill} ";
			}
			return text + $"{skillLevel:F0}";
		}

		private static SkillType GetSkill(string input)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (!Enum.TryParse<SkillType>(input.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Skip(1).FirstOrDefault(), ignoreCase: true, out SkillType result))
			{
				return (SkillType)0;
			}
			return result;
		}

		public string? GetSignHoverText(Sign sign, string input)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			SkillType skill = GetSkill(input);
			if ((int)skill != 0)
			{
				return ((object)(SkillType)(ref skill)).ToString();
			}
			return "Invalid sign";
		}
	}
}