Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValminEye v0.2.0
plugins/ValminEye.dll
Decompiled 8 hours agousing 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) { } } }