Decompiled source of ValminEye v0.2.0

plugins/ValminEye.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ValminEye")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+638f049a95dfe5fcf469aca4031c6aa59cdd1f64")]
[assembly: AssemblyProduct("ValminEye")]
[assembly: AssemblyTitle("ValminEye")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValminEye
{
	internal static class Bosses
	{
		internal sealed class Fighter
		{
			public string Id = "";

			public int Deaths;
		}

		[HarmonyPatch(typeof(ZRoutedRpc), "HandleRoutedRPC")]
		private static class DeathHook
		{
			private static readonly int OnDeathHash = StringExtensionMethods.GetStableHashCode("OnDeath");

			private static void Prefix(RoutedRPCData data)
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				if (data == null || data.m_methodHash != OnDeathHash)
				{
					return;
				}
				try
				{
					OnPlayerDeath(data.m_senderPeerID, data.m_targetZDO);
				}
				catch (Exception ex)
				{
					Guard.Fail("death", ex);
				}
			}
		}

		[HarmonyPatch(typeof(ZoneSystem), "RPC_SetGlobalKey")]
		private static class KeyHook
		{
			private static void Prefix(ZoneSystem __instance, long sender, string name)
			{
				try
				{
					OnKeyRequest(__instance, sender, name);
				}
				catch (Exception ex)
				{
					Guard.Fail("boss key", ex);
				}
			}
		}

		private static Dictionary<int, string> _keyByPrefab;

		private static Dictionary<string, string> _nameTokenByKey;

		private static readonly Dictionary<string, Dictionary<string, Fighter>> Fights = new Dictionary<string, Dictionary<string, Fighter>>();

		internal static bool EnsurePrefabs()
		{
			if (_keyByPrefab != null)
			{
				return true;
			}
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			Dictionary<int, string> dictionary = new Dictionary<int, string>();
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>();
			foreach (GameObject prefab in instance.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Character component = prefab.GetComponent<Character>();
				if (!((Object)(object)component == (Object)null) && component.IsBoss() && !string.IsNullOrEmpty(component.m_defeatSetGlobalKey))
				{
					string text = component.m_defeatSetGlobalKey.ToLowerInvariant();
					dictionary[StringExtensionMethods.GetStableHashCode(((Object)prefab).name)] = text;
					if (!dictionary2.ContainsKey(text))
					{
						dictionary2[text] = component.m_name;
					}
				}
			}
			_keyByPrefab = dictionary;
			_nameTokenByKey = dictionary2;
			Plugin.Log.LogInfo((object)("Bosses found in game data: " + string.Join(", ", dictionary2.Keys)));
			LogDefeated();
			return true;
		}

		private static void LogDefeated()
		{
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (string globalKey in ZoneSystem.instance.GetGlobalKeys())
			{
				string text = KeyName(globalKey);
				if (IsBossKey(text) && !list.Contains(text))
				{
					list.Add(text);
				}
			}
			list.Sort(string.CompareOrdinal);
			string text2 = ((list.Count == 0) ? "none" : string.Join(", ", list));
			Plugin.Log.LogInfo((object)("Bosses already defeated in this world: " + text2));
		}

		internal static bool TryGetKey(int prefab, out string key)
		{
			key = null;
			if (_keyByPrefab != null)
			{
				return _keyByPrefab.TryGetValue(prefab, out key);
			}
			return false;
		}

		private static bool IsBossKey(string key)
		{
			if (_nameTokenByKey != null && _nameTokenByKey.ContainsKey(key))
			{
				return true;
			}
			if (Settings.BossAnyDefeatedKey.Value && key.StartsWith("defeated_", StringComparison.Ordinal))
			{
				return true;
			}
			return Settings.ExtraBossKeys.Contains(key);
		}

		private static string KeyName(string name)
		{
			string text = name.Trim().ToLowerInvariant();
			int num = text.IndexOf(' ');
			if (num >= 0)
			{
				return text.Substring(0, num);
			}
			return text;
		}

		private static ZDO FindBoss(Vector3 near, string key, long owner, out string foundKey, out float distance)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = null;
			bool flag = false;
			foundKey = null;
			distance = float.MaxValue;
			foreach (ZDO value2 in ZDOMan.instance.m_objectsByID.Values)
			{
				if (_keyByPrefab.TryGetValue(value2.GetPrefab(), out var value) && (key == null || !(value != key)))
				{
					bool flag2 = owner != 0L && value2.GetOwner() == owner;
					float num = Utils.DistanceXZ(value2.GetPosition(), near);
					if (val == null || (flag2 && !flag) || (flag2 == flag && num < distance))
					{
						val = value2;
						flag = flag2;
						foundKey = value;
						distance = num;
					}
				}
			}
			return val;
		}

		internal static Fighter GetFighter(Dictionary<string, Fighter> fighters, string player)
		{
			if (!fighters.TryGetValue(player, out var value))
			{
				value = (fighters[player] = new Fighter());
			}
			return value;
		}

		private static void OnPlayerDeath(long sender, ZDOID character)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			if (peer == null || peer.m_characterID != character)
			{
				return;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(character);
			if (zDO == null)
			{
				return;
			}
			BossFights.OnDeath(peer, zDO.GetPosition());
			if (Settings.BossEnabled.Value && EnsurePrefabs() && _keyByPrefab.Count != 0 && FindBoss(zDO.GetPosition(), null, 0L, out var foundKey, out var distance) != null && !(distance > Settings.BossDeathRadius.Value))
			{
				if (!Fights.TryGetValue(foundKey, out var value))
				{
					value = new Dictionary<string, Fighter>();
					Fights[foundKey] = value;
				}
				Fighter fighter = GetFighter(value, peer.m_playerName);
				fighter.Id = Players.PlatformId(peer);
				fighter.Deaths++;
			}
		}

		private static void OnKeyRequest(ZoneSystem zones, long sender, string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return;
			}
			EnsurePrefabs();
			string text = KeyName(name);
			if (!IsBossKey(text))
			{
				return;
			}
			bool num = !zones.GetGlobalKey(text);
			int wipes;
			BossFights.Fight fight = BossFights.OnDefeat(text, sender, out wipes);
			if (!Fights.TryGetValue(text, out var value))
			{
				value = new Dictionary<string, Fighter>();
			}
			Fights.Remove(text);
			if (!num || !Settings.BossEnabled.Value)
			{
				return;
			}
			Json json = Events.New("boss_first_kill").Str("key", text);
			if (Settings.BossIncludeName.Value)
			{
				string text2 = BossName(text);
				if (text2 != null)
				{
					json.Str("boss", text2);
				}
			}
			if (Settings.BossIncludeDay.Value && (Object)(object)EnvMan.instance != (Object)null)
			{
				json.Int("day", EnvMan.instance.GetDay());
			}
			if (Settings.BossIncludeDuration.Value && fight != null)
			{
				json.Int("duration", fight.Duration);
			}
			if (Settings.BossIncludeWipes.Value)
			{
				json.Int("wipes", wipes);
			}
			if (Settings.BossIncludePlayers.Value)
			{
				AddAttackers(value, text, sender);
				json.Array("players", PlayerItems(value, Settings.BossIncludePlayerIds.Value, Settings.BossIncludeDeaths.Value));
			}
			Events.Emit(json);
		}

		private static void AddAttackers(Dictionary<string, Fighter> fighters, string key, long sender)
		{
			ZDO val = FindKilledBoss(key, sender);
			if (val == null)
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(fighters.Keys);
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (!string.IsNullOrEmpty(peer.m_playerName))
				{
					hashSet.Add(peer.m_playerName);
				}
			}
			foreach (string item in hashSet)
			{
				if (val.GetBool(ZDOVars.s_attackers + item, false))
				{
					Fighter fighter = GetFighter(fighters, item);
					if (fighter.Id.Length == 0)
					{
						fighter.Id = Players.PlatformId(ZNet.instance.GetPeerByPlayerName(item));
					}
				}
			}
		}

		internal static ZDO FindKilledBoss(string key, long sender)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (_keyByPrefab == null || _keyByPrefab.Count == 0)
			{
				return null;
			}
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			Vector3 near = Vector3.zero;
			if (peer != null)
			{
				ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID);
				if (zDO != null)
				{
					near = zDO.GetPosition();
				}
			}
			string foundKey;
			float distance;
			return FindBoss(near, key, sender, out foundKey, out distance);
		}

		internal static List<Json> PlayerItems(Dictionary<string, Fighter> fighters, bool includeIds, bool includeDeaths)
		{
			List<string> list = new List<string>(fighters.Keys);
			list.Sort(delegate(string a, string b)
			{
				int num = fighters[b].Deaths.CompareTo(fighters[a].Deaths);
				return (num == 0) ? string.CompareOrdinal(a, b) : num;
			});
			List<Json> list2 = new List<Json>(list.Count);
			foreach (string item in list)
			{
				Json json = new Json().Str("player", item);
				if (includeIds)
				{
					json.Str("id", fighters[item].Id);
				}
				if (includeDeaths)
				{
					json.Int("deaths", fighters[item].Deaths);
				}
				list2.Add(json);
			}
			return list2;
		}

		internal static string BossName(string key)
		{
			if (_nameTokenByKey == null || !_nameTokenByKey.TryGetValue(key, out var value))
			{
				return null;
			}
			return GameText.English(value);
		}
	}
	internal static class BossFights
	{
		internal sealed class Fight
		{
			public string Key;

			public bool First;

			public float Start;

			public readonly Dictionary<string, Bosses.Fighter> Players = new Dictionary<string, Bosses.Fighter>();

			public int Duration => Mathf.RoundToInt(Time.realtimeSinceStartup - Start);
		}

		private static readonly HashSet<ZDOID> Near = new HashSet<ZDOID>();

		private static readonly Dictionary<ZDOID, Fight> Active = new Dictionary<ZDOID, Fight>();

		private static readonly HashSet<ZDOID> Defeated = new HashSet<ZDOID>();

		private static readonly Dictionary<string, int> Wipes = new Dictionary<string, int>();

		private static bool LinesWanted
		{
			get
			{
				if (!Settings.FightStartEnabled.Value)
				{
					return Settings.FightEndEnabled.Value;
				}
				return true;
			}
		}

		private static bool Wanted
		{
			get
			{
				if (!LinesWanted)
				{
					if (Settings.BossEnabled.Value)
					{
						if (!Settings.BossIncludeDuration.Value)
						{
							return Settings.BossIncludeWipes.Value;
						}
						return true;
					}
					return false;
				}
				return true;
			}
		}

		public static void Seen(List<ZDO> objects)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!Wanted || !Bosses.EnsurePrefabs())
			{
				return;
			}
			foreach (ZDO @object in objects)
			{
				if (Bosses.TryGetKey(@object.GetPrefab(), out var _))
				{
					Near.Add(@object.m_uid);
				}
			}
		}

		public static void Tick()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			if (!Wanted)
			{
				Near.Clear();
				return;
			}
			ZDOMan zdos = ZDOMan.instance;
			List<ZDOID> list = null;
			foreach (KeyValuePair<ZDOID, Fight> item in Active)
			{
				ZDO zDO = zdos.GetZDO(item.Key);
				string text = null;
				if (zDO == null)
				{
					text = "gone";
				}
				else if (!zDO.GetBool(ZDOVars.s_alert, false) || !zDO.HasOwner())
				{
					text = "wipe";
				}
				else
				{
					AddNearby(item.Value, zDO.GetPosition());
				}
				if (text != null)
				{
					if (list == null)
					{
						list = new List<ZDOID>();
					}
					list.Add(item.Key);
					if (text == "wipe")
					{
						Wipes[item.Value.Key] = ((!Wipes.TryGetValue(item.Value.Key, out var value)) ? 1 : (value + 1));
					}
					EmitEnd(item.Value, text);
				}
			}
			if (list != null)
			{
				foreach (ZDOID item2 in list)
				{
					Active.Remove(item2);
				}
			}
			foreach (ZDOID item3 in Near)
			{
				if (Active.ContainsKey(item3) || Defeated.Contains(item3))
				{
					continue;
				}
				ZDO zDO2 = zdos.GetZDO(item3);
				if (zDO2 == null || !zDO2.GetBool(ZDOVars.s_alert, false) || !zDO2.HasOwner() || !Bosses.TryGetKey(zDO2.GetPrefab(), out var key))
				{
					continue;
				}
				bool flag = !ZoneSystem.instance.GetGlobalKey(key);
				if (!flag && (Settings.FightFirstOnly.Value || !LinesWanted))
				{
					continue;
				}
				Fight fight = new Fight
				{
					Key = key,
					First = flag,
					Start = Time.realtimeSinceStartup
				};
				AddNearby(fight, zDO2.GetPosition());
				if (fight.Players.Count != 0)
				{
					Active[item3] = fight;
					if (Settings.FightStartEnabled.Value)
					{
						EmitStart(fight);
					}
				}
			}
			Near.Clear();
			Defeated.RemoveWhere((ZDOID id) => zdos.GetZDO(id) == null);
		}

		public static Fight OnDefeat(string key, long sender, out int wipes)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			Wipes.TryGetValue(key, out wipes);
			Wipes.Remove(key);
			ZDOID val = ZDOID.None;
			Fight fight = null;
			bool flag = false;
			foreach (KeyValuePair<ZDOID, Fight> item in Active)
			{
				if (!(item.Value.Key != key))
				{
					ZDO zDO = ZDOMan.instance.GetZDO(item.Key);
					bool flag2 = zDO != null && zDO.GetOwner() == sender;
					if (fight == null || (flag2 && !flag))
					{
						val = item.Key;
						fight = item.Value;
						flag = flag2;
					}
				}
			}
			if (fight == null)
			{
				if (Wanted)
				{
					ZDO val2 = Bosses.FindKilledBoss(key, sender);
					if (val2 != null)
					{
						Defeated.Add(val2.m_uid);
					}
				}
				return null;
			}
			Active.Remove(val);
			Defeated.Add(val);
			EmitEnd(fight, "kill");
			return fight;
		}

		public static void OnDeath(ZNetPeer peer, Vector3 position)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Fight fight = null;
			float num = Settings.BossDeathRadius.Value;
			foreach (KeyValuePair<ZDOID, Fight> item in Active)
			{
				ZDO zDO = ZDOMan.instance.GetZDO(item.Key);
				if (zDO != null)
				{
					float num2 = Utils.DistanceXZ(zDO.GetPosition(), position);
					if (num2 <= num)
					{
						fight = item.Value;
						num = num2;
					}
				}
			}
			if (fight != null)
			{
				Bosses.Fighter fighter = Bosses.GetFighter(fight.Players, peer.m_playerName);
				if (fighter.Id.Length == 0)
				{
					fighter.Id = Players.PlatformId(peer);
				}
				fighter.Deaths++;
			}
		}

		private static void AddNearby(Fight fight, Vector3 boss)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			float value = Settings.BossDeathRadius.Value;
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (((ZDOID)(ref peer.m_characterID)).IsNone() || string.IsNullOrEmpty(peer.m_playerName))
				{
					continue;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID);
				if (zDO != null && !(Utils.DistanceXZ(zDO.GetPosition(), boss) > value))
				{
					Bosses.Fighter fighter = Bosses.GetFighter(fight.Players, peer.m_playerName);
					if (fighter.Id.Length == 0)
					{
						fighter.Id = Players.PlatformId(peer);
					}
				}
			}
		}

		private static Json NewLine(string name, Fight fight)
		{
			Json json = Events.New(name).Str("key", fight.Key);
			if (Settings.FightIncludeBossName.Value)
			{
				string text = Bosses.BossName(fight.Key);
				if (text != null)
				{
					json.Str("boss", text);
				}
			}
			if (Settings.FightIncludeFirst.Value)
			{
				json.Bool("first", fight.First);
			}
			return json;
		}

		private static void AddDay(Json line)
		{
			if (Settings.FightIncludeDay.Value && (Object)(object)EnvMan.instance != (Object)null)
			{
				line.Int("day", EnvMan.instance.GetDay());
			}
		}

		private static void EmitStart(Fight fight)
		{
			Json json = NewLine("boss_fight_start", fight);
			AddDay(json);
			if (Settings.FightIncludePlayers.Value)
			{
				json.Array("players", Bosses.PlayerItems(fight.Players, Settings.FightIncludePlayerIds.Value, includeDeaths: false));
			}
			Events.Emit(json);
		}

		private static void EmitEnd(Fight fight, string outcome)
		{
			if (Settings.FightEndEnabled.Value)
			{
				Json json = NewLine("boss_fight_end", fight).Str("outcome", outcome);
				AddDay(json);
				if (Settings.FightIncludeDuration.Value)
				{
					json.Int("duration", fight.Duration);
				}
				if (Settings.FightIncludePlayers.Value)
				{
					json.Array("players", Bosses.PlayerItems(fight.Players, Settings.FightIncludePlayerIds.Value, Settings.FightIncludeDeaths.Value));
				}
				Events.Emit(json);
			}
		}
	}
	internal sealed class Json
	{
		private readonly StringBuilder _sb = new StringBuilder(128);

		private bool _empty = true;

		public Json()
		{
			_sb.Append('{');
		}

		public Json Str(string name, string value)
		{
			Key(name);
			Quote(_sb, value ?? "");
			return this;
		}

		public Json Int(string name, long value)
		{
			Key(name);
			_sb.Append(value.ToString(CultureInfo.InvariantCulture));
			return this;
		}

		public Json Bool(string name, bool value)
		{
			Key(name);
			_sb.Append(value ? "true" : "false");
			return this;
		}

		public Json Num(string name, float value)
		{
			Key(name);
			_sb.Append(Math.Round(value, 1).ToString("0.0", CultureInfo.InvariantCulture));
			return this;
		}

		public Json Array(string name, IEnumerable<Json> items)
		{
			Key(name);
			_sb.Append('[');
			bool flag = true;
			foreach (Json item in items)
			{
				if (!flag)
				{
					_sb.Append(',');
				}
				flag = false;
				_sb.Append(item);
			}
			_sb.Append(']');
			return this;
		}

		public override string ToString()
		{
			return _sb?.ToString() + "}";
		}

		private void Key(string name)
		{
			if (!_empty)
			{
				_sb.Append(',');
			}
			_empty = false;
			Quote(_sb, name);
			_sb.Append(':');
		}

		private static void Quote(StringBuilder sb, string s)
		{
			sb.Append('"');
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					sb.Append("\\\"");
					continue;
				case '\\':
					sb.Append("\\\\");
					continue;
				case '\n':
					sb.Append("\\n");
					continue;
				case '\r':
					sb.Append("\\r");
					continue;
				case '\t':
					sb.Append("\\t");
					continue;
				}
				if (c < ' ')
				{
					StringBuilder stringBuilder = sb.Append("\\u");
					int num = c;
					stringBuilder.Append(num.ToString("x4"));
				}
				else
				{
					sb.Append(c);
				}
			}
			sb.Append('"');
		}
	}
	internal static class Events
	{
		public const int FormatVersion = 1;

		public static Json New(string name)
		{
			return new Json().Int("v", 1L).Str("event", name);
		}

		public static void Emit(Json line)
		{
			Plugin.Log.LogInfo((object)line.ToString());
		}
	}
	internal static class GameText
	{
		public static string English(string token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return null;
			}
			try
			{
				string text = Localization.instance.Localize(token);
				if (string.IsNullOrEmpty(text) || text[0] == '[' || text[0] == '$')
				{
					return null;
				}
				return text;
			}
			catch (Exception ex)
			{
				Guard.Fail("game text", ex);
				return null;
			}
		}
	}
	internal static class Guard
	{
		private static readonly HashSet<string> Failed = new HashSet<string>();

		public static void Fail(string hook, Exception ex)
		{
			if (Failed.Add(hook))
			{
				Plugin.Log.LogError((object)$"Hook {hook} failed and keeps running without reporting further errors: {ex}");
			}
		}
	}
	internal static class Players
	{
		[HarmonyPatch(typeof(ZNet), "RPC_CharacterID")]
		private static class JoinHook
		{
			private static void Postfix(ZNet __instance, ZRpc rpc, ZDOID characterID)
			{
				try
				{
					if (((ZDOID)(ref characterID)).IsNone())
					{
						return;
					}
					ZNetPeer peer = __instance.GetPeer(rpc);
					if (peer != null && Announced.Add(peer.m_uid) && Settings.JoinEnabled.Value)
					{
						Json json = Events.New("join").Str("player", peer.m_playerName);
						if (Settings.JoinIncludeId.Value)
						{
							json.Str("id", PlatformId(peer));
						}
						Events.Emit(json);
					}
				}
				catch (Exception ex)
				{
					Guard.Fail("join", ex);
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Disconnect")]
		private static class LeaveHook
		{
			private static void Prefix(ZNetPeer peer)
			{
				try
				{
					if (peer != null && Announced.Remove(peer.m_uid) && Settings.LeaveEnabled.Value)
					{
						Json json = Events.New("leave").Str("player", peer.m_playerName);
						if (Settings.LeaveIncludeId.Value)
						{
							json.Str("id", PlatformId(peer));
						}
						Events.Emit(json);
					}
				}
				catch (Exception ex)
				{
					Guard.Fail("leave", ex);
				}
			}
		}

		private static readonly HashSet<long> Announced = new HashSet<long>();

		public static string PlatformId(ZNetPeer peer)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Invalid comparison between Unknown and I4
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			object obj;
			if (peer == null)
			{
				obj = null;
			}
			else
			{
				ISocket socket = peer.m_socket;
				obj = ((socket != null) ? socket.GetHostName() : null);
			}
			string text = (string)obj;
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			OnlineBackendType onlineBackend = ZNet.m_onlineBackend;
			if ((int)onlineBackend != 0)
			{
				if ((int)onlineBackend == 1)
				{
					return ((object)new PlatformUserID(text)/*cast due to .constrained prefix*/).ToString();
				}
				return "";
			}
			return ((object)new PlatformUserID(ZNet.instance.m_steamPlatform, text)/*cast due to .constrained prefix*/).ToString();
		}
	}
	[BepInPlugin("io.valmin.eye", "ValminEye", "0.2.0")]
	[BepInProcess("valheim_server")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "io.valmin.eye";

		public const string Name = "ValminEye";

		public const string Version = "0.2.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Settings.Bind(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("io.valmin.eye");
			Type[] types = typeof(Plugin).Assembly.GetTypes();
			foreach (Type type in types)
			{
				if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0)
				{
					try
					{
						_harmony.CreateClassProcessor(type).Patch();
					}
					catch (Exception ex)
					{
						Log.LogError((object)("Hook " + type.Name + " could not be installed: " + ex.Message));
					}
				}
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class Raids
	{
		[HarmonyPatch(typeof(ZNet), "ServerLoadWorld")]
		private static class LoadHook
		{
			private static void Prefix()
			{
				_loading = true;
			}

			private static void Finalizer()
			{
				_loading = false;
			}
		}

		[HarmonyPatch(typeof(RandEventSystem), "SetRandomEvent")]
		private static class SetHook
		{
			private static void Prefix(RandEventSystem __instance, out RandomEvent __state)
			{
				__state = __instance.m_randomEvent;
			}

			private static void Postfix(RandEventSystem __instance, RandomEvent __state)
			{
				try
				{
					if (!_loading)
					{
						RandomEvent randomEvent = __instance.m_randomEvent;
						if (__state != null && __state != randomEvent && Settings.RaidEndEnabled.Value)
						{
							Emit(__instance, "raid_end", __state);
						}
						if (randomEvent != null && randomEvent != __state && Settings.RaidStartEnabled.Value)
						{
							Emit(__instance, "raid_start", randomEvent);
						}
					}
				}
				catch (Exception ex)
				{
					Guard.Fail("raid", ex);
				}
			}
		}

		private static bool _loading;

		private static void Emit(RandEventSystem system, string name, RandomEvent raid)
		{
			Json json = Events.New(name).Str("raid", raid.m_name);
			if (Settings.RaidIncludeName.Value)
			{
				string text = GameText.English(raid.m_startMessage);
				if (text != null)
				{
					json.Str("name", text);
				}
			}
			if (Settings.RaidIncludePlayers.Value)
			{
				json.Array("players", Nearby(system, raid));
			}
			Events.Emit(json);
		}

		private static List<Json> Nearby(RandEventSystem system, RandomEvent raid)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			List<ZNetPeer> list = new List<ZNetPeer>();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.IsReady() && !((ZDOID)(ref peer.m_characterID)).IsNone())
				{
					ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID);
					if (zDO != null && system.IsInsideRandomEventArea(raid, zDO.GetPosition()))
					{
						list.Add(peer);
					}
				}
			}
			list.Sort((ZNetPeer a, ZNetPeer b) => string.CompareOrdinal(a.m_playerName, b.m_playerName));
			List<Json> list2 = new List<Json>(list.Count);
			foreach (ZNetPeer item in list)
			{
				Json json = new Json().Str("player", item.m_playerName);
				if (Settings.RaidIncludePlayerIds.Value)
				{
					json.Str("id", Players.PlatformId(item));
				}
				list2.Add(json);
			}
			return list2;
		}
	}
	internal static class Settings
	{
		public static ConfigEntry<bool> JoinEnabled;

		public static ConfigEntry<bool> JoinIncludeId;

		public static ConfigEntry<bool> LeaveEnabled;

		public static ConfigEntry<bool> LeaveIncludeId;

		public static ConfigEntry<bool> BossEnabled;

		public static ConfigEntry<bool> BossIncludeName;

		public static ConfigEntry<bool> BossIncludeDay;

		public static ConfigEntry<bool> BossIncludePlayers;

		public static ConfigEntry<bool> BossIncludePlayerIds;

		public static ConfigEntry<bool> BossIncludeDeaths;

		public static ConfigEntry<bool> BossIncludeDuration;

		public static ConfigEntry<bool> BossIncludeWipes;

		public static ConfigEntry<bool> BossAnyDefeatedKey;

		public static ConfigEntry<string> BossKeys;

		public static ConfigEntry<float> BossDeathRadius;

		public static ConfigEntry<bool> FightStartEnabled;

		public static ConfigEntry<bool> FightEndEnabled;

		public static ConfigEntry<bool> FightFirstOnly;

		public static ConfigEntry<bool> FightIncludeBossName;

		public static ConfigEntry<bool> FightIncludeFirst;

		public static ConfigEntry<bool> FightIncludeDay;

		public static ConfigEntry<bool> FightIncludeDuration;

		public static ConfigEntry<bool> FightIncludePlayers;

		public static ConfigEntry<bool> FightIncludePlayerIds;

		public static ConfigEntry<bool> FightIncludeDeaths;

		public static ConfigEntry<bool> RaidStartEnabled;

		public static ConfigEntry<bool> RaidEndEnabled;

		public static ConfigEntry<bool> RaidIncludeName;

		public static ConfigEntry<bool> RaidIncludePlayers;

		public static ConfigEntry<bool> RaidIncludePlayerIds;

		public static ConfigEntry<bool> WorldStartEnabled;

		public static ConfigEntry<bool> WorldIncludeDay;

		public static HashSet<string> ExtraBossKeys = new HashSet<string>();

		public static void Bind(ConfigFile config)
		{
			JoinEnabled = config.Bind<bool>("Join", "Enabled", true, "Write a line when a player's character enters the world.");
			JoinIncludeId = config.Bind<bool>("Join", "IncludeId", true, "Add the platform id (id), for example Steam_76561198000000000.");
			LeaveEnabled = config.Bind<bool>("Leave", "Enabled", true, "Write a line when a player leaves: quit, lost connection, kick or ban.");
			LeaveIncludeId = config.Bind<bool>("Leave", "IncludeId", true, "Add the platform id (id).");
			BossEnabled = config.Bind<bool>("BossFirstKill", "Enabled", true, "Write a line the first time each boss is defeated in this world.");
			BossIncludeName = config.Bind<bool>("BossFirstKill", "IncludeBossName", true, "Add the boss name from the game's English text (boss). Left out when the game has no name for the key.");
			BossIncludeDay = config.Bind<bool>("BossFirstKill", "IncludeDay", true, "Add the in-game day of the kill (day).");
			BossIncludePlayers = config.Bind<bool>("BossFirstKill", "IncludePlayers", true, "Add the players in the fight (players): everyone who hit the boss and everyone who died near it.");
			BossIncludePlayerIds = config.Bind<bool>("BossFirstKill", "IncludePlayerIds", true, "Add each player's platform id (players[].id).");
			BossIncludeDeaths = config.Bind<bool>("BossFirstKill", "IncludeDeaths", true, "Add how many times each player died near the boss (players[].deaths).");
			BossIncludeDuration = config.Bind<bool>("BossFirstKill", "IncludeDuration", true, "Add how long the winning fight lasted, in seconds (duration). Left out when no fight was seen.");
			BossIncludeWipes = config.Bind<bool>("BossFirstKill", "IncludeWipes", true, "Add how many fights against this boss ended without a kill since the server started (wipes).");
			BossAnyDefeatedKey = config.Bind<bool>("BossFirstKill", "AnyDefeatedKey", true, "Treat any world key starting with defeated_ as a boss kill. Bosses the game defines are found automatically either way.");
			BossKeys = config.Bind<string>("BossFirstKill", "BossKeys", "", "Extra world keys that count as a boss kill, separated by commas. For modded bosses whose key does not start with defeated_. Example: killed_mybossname, defeated_custom");
			BossDeathRadius = config.Bind<float>("BossFirstKill", "DeathRadius", 100f, "A player death counts for a boss, and a player counts as in a boss fight, within this many metres of the boss. The default is the distance at which the game shows a boss health bar.");
			FightStartEnabled = config.Bind<bool>("BossFight", "StartEnabled", true, "Write a line when a boss notices players and a fight starts.");
			FightEndEnabled = config.Bind<bool>("BossFight", "EndEnabled", true, "Write a line when a boss fight ends, with how it ended (outcome): kill, wipe or gone.");
			FightFirstOnly = config.Bind<bool>("BossFight", "FirstOnly", true, "Report fights only against bosses not yet defeated in this world.");
			FightIncludeBossName = config.Bind<bool>("BossFight", "IncludeBossName", true, "Add the boss name from the game's English text (boss).");
			FightIncludeFirst = config.Bind<bool>("BossFight", "IncludeFirst", true, "Add whether the boss had never been defeated in this world when the fight started (first).");
			FightIncludeDay = config.Bind<bool>("BossFight", "IncludeDay", true, "Add the in-game day (day).");
			FightIncludeDuration = config.Bind<bool>("BossFight", "IncludeDuration", true, "Add how long the fight lasted, in seconds (duration), to the end line.");
			FightIncludePlayers = config.Bind<bool>("BossFight", "IncludePlayers", true, "Add the players in the fight (players): everyone within DeathRadius of the boss during it.");
			FightIncludePlayerIds = config.Bind<bool>("BossFight", "IncludePlayerIds", true, "Add each player's platform id (players[].id).");
			FightIncludeDeaths = config.Bind<bool>("BossFight", "IncludeDeaths", true, "Add how many times each player died in the fight (players[].deaths), to the end line.");
			RaidStartEnabled = config.Bind<bool>("Raid", "StartEnabled", true, "Write a line when a raid starts, with the game's raid name, for example army_eikthyr.");
			RaidEndEnabled = config.Bind<bool>("Raid", "EndEnabled", true, "Write a line when a raid ends.");
			RaidIncludeName = config.Bind<bool>("Raid", "IncludeName", true, "Add the message the game shows when the raid starts, in English (name), for example \"The ground is shaking\".");
			RaidIncludePlayers = config.Bind<bool>("Raid", "IncludePlayers", true, "Add the players inside the raid area (players).");
			RaidIncludePlayerIds = config.Bind<bool>("Raid", "IncludePlayerIds", true, "Add each player's platform id (players[].id).");
			WorldStartEnabled = config.Bind<bool>("World", "StartEnabled", true, "Write a line when the world has loaded and the server is about to open to players.");
			WorldIncludeDay = config.Bind<bool>("World", "IncludeDay", true, "Add the in-game day (day).");
			ParseBossKeys();
			BossKeys.SettingChanged += delegate
			{
				ParseBossKeys();
			};
		}

		private static void ParseBossKeys()
		{
			HashSet<string> hashSet = new HashSet<string>();
			string[] array = BossKeys.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim().ToLowerInvariant();
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			ExtraBossKeys = hashSet;
		}
	}
	internal static class Ticks
	{
		[HarmonyPatch(typeof(ZDOMan), "ReleaseNearbyZDOS")]
		private static class NearHook
		{
			private static void Postfix(ZDOMan __instance)
			{
				_passed = true;
				try
				{
					BossFights.Seen(__instance.m_tempNearObjects);
				}
				catch (Exception ex)
				{
					Guard.Fail("boss scan", ex);
				}
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "ReleaseZDOS")]
		private static class PassHook
		{
			private static void Postfix()
			{
				if (!_passed)
				{
					return;
				}
				_passed = false;
				try
				{
					BossFights.Tick();
				}
				catch (Exception ex)
				{
					Guard.Fail("boss fight", ex);
				}
			}
		}

		private static bool _passed;
	}
	internal static class World
	{
		[HarmonyPatch(typeof(ZNet), "OnGenerationFinished")]
		private static class StartHook
		{
			private static void Postfix(ZNet __instance)
			{
				try
				{
					if (Settings.WorldStartEnabled.Value)
					{
						Json json = Events.New("world_start").Str("world", __instance.GetWorldName());
						if (Settings.WorldIncludeDay.Value && (Object)(object)EnvMan.instance != (Object)null)
						{
							json.Int("day", EnvMan.instance.GetDay());
						}
						Events.Emit(json);
					}
				}
				catch (Exception ex)
				{
					Guard.Fail("world start", ex);
				}
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}