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 PvP Kill Feed v1.1.0
PvPKillFeed.dll
Decompiled 8 hours agousing 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.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Extensions; using Jotunn.Managers; using Jotunn.Utils; using RdMods.PvpKillFeed.Patches; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PvPKillFeed")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("PvP Kill Feed")] [assembly: AssemblyTitle("PvPKillFeed")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] namespace RdMods.PvpKillFeed { public static class ChatCommands { [HarmonyPatch(typeof(Chat), "InputText")] private static class ChatInputPatch { private static bool Prefix(Chat __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)((Terminal)__instance).m_input == (Object)null) { return true; } return !TryHandleChat(((TMP_InputField)((Terminal)__instance).m_input).text); } } private class DelegateCommand : ConsoleCommand { private readonly string _name; private readonly string _help; private readonly Action<string[]> _run; public override string Name => _name; public override string Help => _help; public override bool IsCheat => false; public DelegateCommand(string name, string help, Action<string[]> run) { _name = name; _help = help; _run = run; } public override void Run(string[] args) { _run(args); } } public static void Register() { CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new DelegateCommand("disablekillfeed", "Hide the kill feed on this client for the current session.", delegate { SetFeedHidden(hidden: true); })); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new DelegateCommand("enablekillfeed", "Show the kill feed on this client. Rejoin also re-enables it.", delegate { SetFeedHidden(hidden: false); })); } public static bool TryHandleChat(string raw) { if (string.IsNullOrEmpty(raw) || raw[0] != '/') { return false; } string text = raw.Trim(); int num = text.IndexOf(' '); if (num > 0) { text = text.Substring(0, num); } if (text.Equals("/disablekillfeed", StringComparison.OrdinalIgnoreCase)) { SetFeedHidden(hidden: true); return true; } if (text.Equals("/enablekillfeed", StringComparison.OrdinalIgnoreCase)) { SetFeedHidden(hidden: false); return true; } return false; } private static void SetFeedHidden(bool hidden) { if ((Object)(object)Plugin.Instance == (Object)null || (Object)(object)Plugin.Instance.Hud == (Object)null) { Tell("Kill feed is not ready."); return; } if (Plugin.Instance.Hud.LocalHidden == hidden) { Tell(hidden ? "Kill feed is already hidden." : "Kill feed is already shown."); return; } Plugin.Instance.Hud.SetLocalHidden(hidden); Tell(hidden ? "Kill feed hidden. Other players still see your kills. /enablekillfeed to show." : "Kill feed shown."); } private static void Tell(string message) { if ((Object)(object)Chat.instance != (Object)null) { ((Terminal)Chat.instance).AddString(message); } } } public static class GuildColor { public const string GuildsPluginGuid = "org.bepinex.plugins.guilds"; public static readonly Color White = Color.white; public static readonly int WhiteRgb = PvpKillEvent.Pack(Color.white); private static bool _initialized; private static bool _available; private static MethodInfo _getPlayerGuild; private static MethodInfo _getGuilds; private static FieldInfo _generalField; private static FieldInfo _colorField; private static FieldInfo _membersField; public static bool IsAvailable { get { Init(); return _available; } } public static void Init() { if (_initialized) { return; } _initialized = true; if (!Chainloader.PluginInfos.ContainsKey("org.bepinex.plugins.guilds")) { Plugin.Log.LogInfo((object)"Guilds is not loaded. Names will render white."); return; } Type type = FindType("Guilds.API"); Type type2 = FindType("Guilds.Guild"); Type type3 = FindType("Guilds.GuildGeneral"); if (type == null || type2 == null || type3 == null) { Plugin.Log.LogWarning((object)"Guilds is present but its API types could not be resolved."); return; } _getPlayerGuild = type.GetMethod("GetPlayerGuild", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Player) }, null); _getGuilds = type.GetMethod("GetGuilds", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); _generalField = type2.GetField("General", BindingFlags.Instance | BindingFlags.Public); _membersField = type2.GetField("Members", BindingFlags.Instance | BindingFlags.Public); _colorField = type3.GetField("color", BindingFlags.Instance | BindingFlags.Public); _available = _getPlayerGuild != null && _generalField != null && _colorField != null; Plugin.Log.LogInfo((object)(_available ? "Bound to Guilds.API for live guild RGB colors." : "Failed to bind Guilds.API. Names will render white.")); } public static int RgbFor(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return ColorToRgb(ColorFor(player)); } public static int RgbForName(string playerName) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return ColorToRgb(ColorForName(playerName)); } public static Color ColorFor(Player player) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Init(); if (!_available || (Object)(object)player == (Object)null || _getPlayerGuild == null) { return White; } try { return ReadGuildColor(_getPlayerGuild.Invoke(null, new object[1] { player })); } catch { return White; } } public static Color ColorForName(string playerName) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) Init(); if (string.IsNullOrEmpty(playerName)) { return White; } Player val = FindLivePlayer(playerName); if ((Object)(object)val != (Object)null) { return ColorFor(val); } return ColorFromGuildScan(playerName); } public static int ColorToRgb(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return PvpKillEvent.Pack(color); } private static Color ColorFromGuildScan(string playerName) { //IL_000d: 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) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (_getGuilds == null) { return White; } try { if (!(_getGuilds.Invoke(null, null) is IEnumerable enumerable)) { return White; } foreach (object item in enumerable) { if (GuildContainsPlayer(item, playerName)) { return ReadGuildColor(item); } } } catch { } return White; } private static Color ReadGuildColor(object guild) { //IL_001d: 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_0032: Unknown result type (might be due to invalid IL or missing references) if (guild == null || _generalField == null || _colorField == null) { return White; } object value = _generalField.GetValue(guild); if (value == null) { return White; } return ParseHtml(_colorField.GetValue(value) as string); } private static bool GuildContainsPlayer(object guild, string playerName) { if (guild == null || _membersField == null) { return false; } if (!(_membersField.GetValue(guild) is IDictionary dictionary)) { return false; } foreach (object key in dictionary.Keys) { if (key != null) { FieldInfo field = key.GetType().GetField("name", BindingFlags.Instance | BindingFlags.Public); if (string.Equals((field != null) ? (field.GetValue(key) as string) : null, playerName, StringComparison.OrdinalIgnoreCase)) { return true; } } } return false; } private static Player FindLivePlayer(string playerName) { foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && string.Equals(NameResolver.OfPlayer(allPlayer), playerName, StringComparison.OrdinalIgnoreCase)) { return allPlayer; } } return null; } private static Type FindType(string fullName) { Type type = Type.GetType(fullName + ", Guilds"); if (type != null) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == "Guilds") { return assembly.GetType(fullName); } } return null; } private static Color ParseHtml(string html) { //IL_0008: 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_0036: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(html)) { return White; } string text = html.Trim(); if (text[0] != '#') { text = "#" + text; } Color result = default(Color); if (!ColorUtility.TryParseHtmlString(text, ref result)) { return White; } result.a = 1f; return result; } } public enum HudAnchor { TopLeft, TopCenter, TopRight, BottomLeft, BottomCenter, BottomRight } public static class KillFeedAssets { private const string SwordResource = "kill_icon_sword.png"; private static Sprite _sword; public static Sprite Sword => _sword; public static void Load() { _sword = ToSprite(LoadTexture("kill_icon_sword.png")); if ((Object)(object)_sword == (Object)null) { Plugin.Log.LogWarning((object)"Failed to load crossed-swords kill icon."); } } private static Texture2D LoadTexture(string fileName) { try { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string n) => n.EndsWith(fileName, StringComparison.OrdinalIgnoreCase)); if (text == null) { return null; } using Stream stream = executingAssembly.GetManifestResourceStream(text); if (stream == null) { return null; } using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); Texture2D val = AssetUtils.LoadImage(memoryStream.ToArray()); if ((Object)(object)val != (Object)null) { ((Object)val).name = fileName; ((Texture)val).filterMode = (FilterMode)1; ((Texture)val).wrapMode = (TextureWrapMode)1; } return val; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Kill icon load failed: " + ex.Message)); return null; } } private static Sprite ToSprite(Texture2D texture) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)texture == (Object)null) { return null; } return Sprite.Create(texture, new Rect(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height), new Vector2(0.5f, 0.5f), 100f); } } public class KillFeedConfig { public ConfigEntry<HudAnchor> Position; public ConfigEntry<int> OffsetX; public ConfigEntry<int> OffsetY; public ConfigEntry<bool> NewestOnTop; public ConfigEntry<float> EntryDurationSeconds; public ConfigEntry<float> FadeOutSeconds; public ConfigEntry<int> MaxEntries; public ConfigEntry<int> FontSize; public ConfigEntry<int> IconSize; public ConfigEntry<int> EntrySpacing; public ConfigEntry<float> BackgroundAlpha; public ConfigEntry<int> BackgroundPadX; public ConfigEntry<int> BackgroundPadY; public KillFeedConfig(ConfigFile cfg) { Position = ConfigFileExtensions.BindConfig<HudAnchor>(cfg, "HUD", "Position", HudAnchor.TopCenter, "HUD anchor.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); OffsetX = ConfigFileExtensions.BindConfig<int>(cfg, "HUD", "OffsetX", 0, "Horizontal inset (px).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); OffsetY = ConfigFileExtensions.BindConfig<int>(cfg, "HUD", "OffsetY", 36, "Vertical inset (px).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); NewestOnTop = ConfigFileExtensions.BindConfig<bool>(cfg, "HUD", "NewestOnTop", true, "New kills appear at the top.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); EntryDurationSeconds = ConfigFileExtensions.BindConfig<float>(cfg, "HUD", "EntryDurationSeconds", 7f, "Seconds fully visible.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); FadeOutSeconds = ConfigFileExtensions.BindConfig<float>(cfg, "HUD", "FadeOutSeconds", 2f, "Fade-out seconds.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); MaxEntries = ConfigFileExtensions.BindConfig<int>(cfg, "HUD", "MaxEntries", 3, "Max lines on screen.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); FontSize = ConfigFileExtensions.BindConfig<int>(cfg, "HUD", "FontSize", 15, "Name font size.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); IconSize = ConfigFileExtensions.BindConfig<int>(cfg, "HUD", "IconSize", 20, "Sword icon size (px).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); EntrySpacing = ConfigFileExtensions.BindConfig<int>(cfg, "HUD", "EntrySpacing", 4, "Gap between lines (px).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); BackgroundAlpha = ConfigFileExtensions.BindConfig<float>(cfg, "HUD", "BackgroundAlpha", 0.55f, "Bar opacity 0-1.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); BackgroundPadX = ConfigFileExtensions.BindConfig<int>(cfg, "HUD", "BackgroundPadX", 5, "Bar padding X (px).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); BackgroundPadY = ConfigFileExtensions.BindConfig<int>(cfg, "HUD", "BackgroundPadY", 1, "Bar padding Y (px).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); } } public class KillFeedHud : MonoBehaviour { private class EntryView { public GameObject Root; public RectTransform Motion; public CanvasGroup Group; } private class PendingKill { public string Killer; public string Victim; public Color KillerColor; public Color VictimColor; } private const float SlideInSeconds = 0.2f; private KillFeedConfig _cfg; private RectTransform _root; private VerticalLayoutGroup _stack; private Sprite _barSprite; private readonly List<EntryView> _entries = new List<EntryView>(); private readonly Queue<PendingKill> _queued = new Queue<PendingKill>(); private bool _guiBound; public bool LocalHidden { get; private set; } public void Configure(KillFeedConfig cfg) { _cfg = cfg; } public void BindGui() { if (!_guiBound) { _guiBound = true; GUIManager.OnCustomGUIAvailable += RebuildRoot; if ((Object)(object)GUIManager.CustomGUIFront != (Object)null) { RebuildRoot(); } } } public void UnbindGui() { if (_guiBound) { _guiBound = false; GUIManager.OnCustomGUIAvailable -= RebuildRoot; } } public void SetLocalHidden(bool hidden) { LocalHidden = hidden; ApplyVisibility(); } public void ResetSession() { LocalHidden = false; ApplyVisibility(); } public void RefreshLayout() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) ApplyAnchor(); ApplySpacing(); if ((Object)(object)_stack != (Object)null) { ((LayoutGroup)_stack).childAlignment = ChildAlignmentForAnchor(); } } public void Push(PvpKillEvent ev) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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) if (LocalHidden) { return; } string text = NameResolver.Sanitize(ev.Killer); string text2 = NameResolver.Sanitize(ev.Victim); if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2)) { PendingKill pendingKill = new PendingKill { Killer = text, Victim = text2, KillerColor = ev.KillerColor, VictimColor = ev.VictimColor }; if ((Object)(object)_stack == (Object)null) { _queued.Enqueue(pendingKill); } else { Spawn(pendingKill); } } } private void RebuildRoot() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Expected O, but got Unknown try { _entries.Clear(); _root = null; _stack = null; GameObject customGUIFront = GUIManager.CustomGUIFront; if (!((Object)(object)customGUIFront == (Object)null)) { EnsureBarSprite(); GameObject val = new GameObject("PvPKillFeed_Root", new Type[3] { typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter) }); val.layer = 5; val.transform.SetParent(customGUIFront.transform, false); _root = val.GetComponent<RectTransform>(); ApplyAnchor(); _stack = val.GetComponent<VerticalLayoutGroup>(); ((LayoutGroup)_stack).childAlignment = ChildAlignmentForAnchor(); ((HorizontalOrVerticalLayoutGroup)_stack).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)_stack).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)_stack).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)_stack).childForceExpandHeight = false; ((LayoutGroup)_stack).padding = new RectOffset(0, 0, 0, 0); ApplySpacing(); ContentSizeFitter component = val.GetComponent<ContentSizeFitter>(); component.horizontalFit = (FitMode)2; component.verticalFit = (FitMode)2; ApplyVisibility(); while (_queued.Count > 0) { Spawn(_queued.Dequeue()); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Kill feed GUI rebuild failed: " + ex.Message)); } } private void ApplyAnchor() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root == (Object)null) && _cfg != null) { HudAnchor value = _cfg.Position.Value; bool flag = IsCenter(value); bool flag2 = IsRight(value); bool flag3 = IsBottom(value); float num = (flag ? 0.5f : (flag2 ? 1f : 0f)); float num2 = (flag3 ? 0f : 1f); _root.anchorMin = new Vector2(num, num2); _root.anchorMax = new Vector2(num, num2); _root.pivot = new Vector2(num, num2); float num3 = _cfg.OffsetX.Value; float num4 = Math.Max(0, _cfg.OffsetY.Value); float num5 = (flag ? num3 : (flag2 ? (0f - Math.Max(0f, num3)) : Math.Max(0f, num3))); _root.anchoredPosition = new Vector2(num5, flag3 ? num4 : (0f - num4)); _root.sizeDelta = Vector2.zero; } } private void ApplySpacing() { if (!((Object)(object)_stack == (Object)null) && _cfg != null) { ((HorizontalOrVerticalLayoutGroup)_stack).spacing = Mathf.Max(0, _cfg.EntrySpacing.Value); } } private TextAnchor ChildAlignmentForAnchor() { if (_cfg == null) { return (TextAnchor)1; } return (TextAnchor)(_cfg.Position.Value switch { HudAnchor.TopLeft => 0, HudAnchor.TopCenter => 1, HudAnchor.BottomLeft => 6, HudAnchor.BottomCenter => 7, HudAnchor.BottomRight => 8, _ => 2, }); } private static bool IsCenter(HudAnchor anchor) { if (anchor != HudAnchor.TopCenter) { return anchor == HudAnchor.BottomCenter; } return true; } private static bool IsRight(HudAnchor anchor) { if (anchor != HudAnchor.TopRight) { return anchor == HudAnchor.BottomRight; } return true; } private static bool IsBottom(HudAnchor anchor) { if (anchor != HudAnchor.BottomLeft && anchor != HudAnchor.BottomCenter) { return anchor == HudAnchor.BottomRight; } return true; } private Vector2 InnerPivot() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (_cfg == null || IsCenter(_cfg.Position.Value)) { return new Vector2(0.5f, 0.5f); } if (!IsRight(_cfg.Position.Value)) { return new Vector2(0f, 0.5f); } return new Vector2(1f, 0.5f); } private Vector2 SlideOffset() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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_002b: Unknown result type (might be due to invalid IL or missing references) HudAnchor value = _cfg.Position.Value; if (IsCenter(value)) { if (!IsBottom(value)) { return new Vector2(0f, 18f); } return new Vector2(0f, -18f); } if (!IsRight(value)) { return new Vector2(-24f, 0f); } return new Vector2(24f, 0f); } private void Spawn(PendingKill pending) { if ((Object)(object)_stack == (Object)null || _cfg == null || LocalHidden) { return; } ApplyAnchor(); ApplySpacing(); TrimToMax(); EntryView entryView = BuildRow(pending); if (entryView != null && !((Object)(object)entryView.Root == (Object)null)) { if (_cfg.NewestOnTop.Value) { entryView.Root.transform.SetAsFirstSibling(); } _entries.Add(entryView); LayoutRebuilder.ForceRebuildLayoutImmediate(_root); ((MonoBehaviour)this).StartCoroutine(SafeRunEntry(entryView)); } } private void TrimToMax() { int num = Math.Max(1, _cfg.MaxEntries.Value); while (_entries.Count >= num) { EntryView entryView = _entries[0]; _entries.RemoveAt(0); if (entryView != null && (Object)(object)entryView.Root != (Object)null) { Object.Destroy((Object)(object)entryView.Root); } } _entries.RemoveAll((EntryView e) => e == null || (Object)(object)e.Root == (Object)null); } private EntryView BuildRow(PendingKill pending) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0130: 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_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) int num = Math.Max(8, _cfg.IconSize.Value); int num2 = Mathf.Clamp(_cfg.BackgroundPadX.Value, 0, 32); int num3 = Mathf.Clamp(_cfg.BackgroundPadY.Value, 0, 32); GameObject val = new GameObject("KillLine", new Type[2] { typeof(RectTransform), typeof(LayoutElement) }); val.layer = 5; val.transform.SetParent(((Component)_stack).transform, false); LayoutElement component = val.GetComponent<LayoutElement>(); component.flexibleWidth = 1f; GameObject val2 = new GameObject("Content", new Type[5] { typeof(RectTransform), typeof(CanvasGroup), typeof(HorizontalLayoutGroup), typeof(ContentSizeFitter), typeof(Image) }); val2.layer = 5; val2.transform.SetParent(val.transform, false); RectTransform component2 = val2.GetComponent<RectTransform>(); Vector2 pivot = (component2.anchorMax = (component2.anchorMin = InnerPivot())); component2.pivot = pivot; component2.anchoredPosition = Vector2.zero; HorizontalLayoutGroup component3 = val2.GetComponent<HorizontalLayoutGroup>(); ((LayoutGroup)component3).childAlignment = (TextAnchor)4; ((HorizontalOrVerticalLayoutGroup)component3).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component3).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandWidth = false; ((HorizontalOrVerticalLayoutGroup)component3).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component3).spacing = 8f; ((LayoutGroup)component3).padding = new RectOffset(num2, num2, num3, num3); ContentSizeFitter component4 = val2.GetComponent<ContentSizeFitter>(); component4.horizontalFit = (FitMode)2; component4.verticalFit = (FitMode)2; Image component5 = val2.GetComponent<Image>(); component5.sprite = _barSprite; component5.type = (Type)0; ((Graphic)component5).color = new Color(0f, 0f, 0f, Mathf.Clamp01(_cfg.BackgroundAlpha.Value)); ((Graphic)component5).raycastTarget = false; TextMeshProUGUI killer = CreateName(val2.transform, pending.Killer, pending.KillerColor); CreateIcon(val2.transform, num); TextMeshProUGUI victim = CreateName(val2.transform, pending.Victim, pending.VictimColor); float num4 = (component.preferredHeight = (component.minHeight = MeasureRowHeight(killer, victim, num, num3))); component.flexibleHeight = 0f; LayoutRebuilder.ForceRebuildLayoutImmediate(component2); Rect rect = component2.rect; float height = ((Rect)(ref rect)).height; if (height > num4) { component.minHeight = height; component.preferredHeight = height; } CanvasGroup component6 = val2.GetComponent<CanvasGroup>(); component6.alpha = 0f; component6.blocksRaycasts = false; component6.interactable = false; return new EntryView { Root = val, Motion = component2, Group = component6 }; } private TextMeshProUGUI CreateName(Transform parent, string name, Color color) { //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) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Name", new Type[4] { typeof(RectTransform), typeof(TextMeshProUGUI), typeof(ContentSizeFitter), typeof(Outline) }) { layer = 5 }; val.transform.SetParent(parent, false); TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>(); ((Graphic)component).raycastTarget = false; ((TMP_Text)component).text = name; ((Graphic)component).color = color; ((TMP_Text)component).fontSize = Math.Max(8, _cfg.FontSize.Value); ((TMP_Text)component).fontStyle = (FontStyles)1; ((TMP_Text)component).characterSpacing = -1.2f; ((TMP_Text)component).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)component).overflowMode = (TextOverflowModes)0; ((TMP_Text)component).alignment = (TextAlignmentOptions)4097; ((TMP_Text)component).enableAutoSizing = false; TMP_FontAsset val2 = ((GUIManager.Instance != null) ? GUIManager.Instance.TMP_AveriaSansLibre : null); if ((Object)(object)val2 != (Object)null) { ((TMP_Text)component).font = val2; } Outline component2 = val.GetComponent<Outline>(); ((Shadow)component2).effectColor = new Color(0f, 0f, 0f, 0.92f); ((Shadow)component2).effectDistance = new Vector2(1f, -1f); ((Shadow)component2).useGraphicAlpha = true; ContentSizeFitter component3 = val.GetComponent<ContentSizeFitter>(); component3.horizontalFit = (FitMode)2; component3.verticalFit = (FitMode)2; return component; } private static float MeasureRowHeight(TextMeshProUGUI killer, TextMeshProUGUI victim, int iconSize, int padY) { float num = Mathf.Max(MeasureNameHeight(killer), MeasureNameHeight(victim)); return Mathf.Max((float)iconSize, num) + (float)padY * 2f; } private static float MeasureNameHeight(TextMeshProUGUI tmp) { if ((Object)(object)tmp == (Object)null) { return 0f; } ((TMP_Text)tmp).ForceMeshUpdate(true, false); return Mathf.Max(((TMP_Text)tmp).preferredHeight, ((TMP_Text)tmp).fontSize); } private Image CreateIcon(Transform parent, int size) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("KillIcon", new Type[3] { typeof(RectTransform), typeof(LayoutElement), typeof(Image) }) { layer = 5 }; val.transform.SetParent(parent, false); LayoutElement component = val.GetComponent<LayoutElement>(); component.preferredWidth = size; component.preferredHeight = size; component.minWidth = size; component.minHeight = size; val.GetComponent<RectTransform>().sizeDelta = new Vector2((float)size, (float)size); Image component2 = val.GetComponent<Image>(); ((Graphic)component2).raycastTarget = false; component2.preserveAspect = true; ((Graphic)component2).color = Color.white; component2.sprite = KillFeedAssets.Sword; return component2; } private void EnsureBarSprite() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_barSprite != (Object)null)) { Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, Color.white); ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)0; val.Apply(); _barSprite = Sprite.Create(val, new Rect(0f, 0f, 1f, 1f), new Vector2(0.5f, 0.5f), 1f); } } private IEnumerator SafeRunEntry(EntryView view) { IEnumerator routine; try { routine = AnimateEntry(view); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Kill feed animation failed to start: " + ex.Message)); yield break; } while (true) { bool flag; try { flag = routine.MoveNext(); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Kill feed animation error: " + ex2.Message)); break; } if (flag) { yield return routine.Current; continue; } break; } } private IEnumerator AnimateEntry(EntryView view) { if (view == null || (Object)(object)view.Motion == (Object)null || (Object)(object)view.Group == (Object)null) { yield break; } Vector2 slide = SlideOffset(); view.Motion.anchoredPosition = slide; float t = 0f; while (t < 0.2f) { if ((Object)(object)view.Root == (Object)null || (Object)(object)view.Motion == (Object)null) { yield break; } t += Time.unscaledDeltaTime; float num = Mathf.Clamp01(t / 0.2f); float num2 = 1f - (1f - num) * (1f - num); view.Group.alpha = num2; view.Motion.anchoredPosition = Vector2.Lerp(slide, Vector2.zero, num2); yield return null; } if ((Object)(object)view.Root == (Object)null || (Object)(object)view.Motion == (Object)null) { yield break; } view.Group.alpha = 1f; view.Motion.anchoredPosition = Vector2.zero; float num3 = Math.Max(0.1f, _cfg.EntryDurationSeconds.Value); yield return (object)new WaitForSecondsRealtime(num3); float fade = Math.Max(0.05f, _cfg.FadeOutSeconds.Value); t = 0f; while (t < fade) { if ((Object)(object)view.Root == (Object)null || (Object)(object)view.Group == (Object)null) { yield break; } t += Time.unscaledDeltaTime; view.Group.alpha = 1f - Mathf.Clamp01(t / fade); yield return null; } _entries.Remove(view); if ((Object)(object)view.Root != (Object)null) { Object.Destroy((Object)(object)view.Root); } } private void ApplyVisibility() { if (!((Object)(object)_root == (Object)null)) { bool flag = (Object)(object)Player.m_localPlayer != (Object)null && ((Object)(object)Hud.instance == (Object)null || Hud.instance.IsVisible()); bool flag2 = !LocalHidden && flag; if (((Component)_root).gameObject.activeSelf != flag2) { ((Component)_root).gameObject.SetActive(flag2); } } } private void Update() { try { ApplyVisibility(); } catch { } } } public static class NameResolver { public static string OfPlayer(Player player) { if ((Object)(object)player == (Object)null) { return ""; } string playerName = player.GetPlayerName(); if (!string.IsNullOrEmpty(playerName) && playerName != "...") { return Sanitize(playerName); } string hoverName = ((Character)player).GetHoverName(); if (!string.IsNullOrEmpty(hoverName) && hoverName != "...") { return Sanitize(hoverName); } return ""; } public static string Sanitize(string name) { if (string.IsNullOrEmpty(name)) { return ""; } return name.Replace("<", "").Replace(">", "").Trim(); } } public static class Network { [HarmonyPatch(typeof(Game), "Start")] private static class GameStartPatch { private static void Postfix() { if ((Object)(object)Plugin.Instance != (Object)null && (Object)(object)Plugin.Instance.Hud != (Object)null) { Plugin.Instance.Hud.ResetSession(); } ResetSession(); TryRegister(); } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] private static class ZNetDestroyPatch { private static void Prefix() { _registeredRpc = null; ResetSession(); } } public const string RpcName = "PKF"; private const float DedupSeconds = 2.5f; private const int MaxCache = 8; private static ZRoutedRpc _registeredRpc; private static readonly Dictionary<long, float> _recentVictims = new Dictionary<long, float>(8); private static readonly List<long> _stale = new List<long>(8); public static void Broadcast(PvpKillEvent ev) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown if (ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); ev.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PKF", new object[1] { val }); } } public static void OnRpc(long sender, ZPackage pkg) { if (pkg != null && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Plugin.Instance == (Object)null)) { PvpKillEvent ev; try { ev = PvpKillEvent.Read(pkg); } catch { return; } if (!IsDuplicate(ev.VictimId)) { Plugin.Instance.OnPvpKillReceived(ev); } } } public static void ResetSession() { _recentVictims.Clear(); _stale.Clear(); PlayerDeathPatch.ResetDebounce(); CharacterDamagePatch.ClearCredit(); } private static bool IsDuplicate(long victimId) { float unscaledTime = Time.unscaledTime; Prune(unscaledTime); if (_recentVictims.TryGetValue(victimId, out var value) && unscaledTime - value < 2.5f) { return true; } _recentVictims[victimId] = unscaledTime; return false; } private static void Prune(float now) { if (_recentVictims.Count == 0) { return; } _stale.Clear(); foreach (KeyValuePair<long, float> recentVictim in _recentVictims) { if (now - recentVictim.Value >= 2.5f) { _stale.Add(recentVictim.Key); } } for (int i = 0; i < _stale.Count; i++) { _recentVictims.Remove(_stale[i]); } _stale.Clear(); if (_recentVictims.Count > 8) { _recentVictims.Clear(); } } private static void TryRegister() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && _registeredRpc != instance) { instance.Register<ZPackage>("PKF", (Action<long, ZPackage>)OnRpc); _registeredRpc = instance; } } } [BepInPlugin("com.rdmods.pvpkillfeed", "PvP Kill Feed", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "com.rdmods.pvpkillfeed"; public const string PluginName = "PvP Kill Feed"; public const string PluginVersion = "1.1.0"; private Harmony _harmony; private ConfigFileWatcher _configWatcher; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public KillFeedConfig FeedConfig { get; private set; } public KillFeedHud Hud { get; private set; } private void Awake() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; FeedConfig = new KillFeedConfig(((BaseUnityPlugin)this).Config); KillFeedAssets.Load(); GuildColor.Init(); Hud = ((Component)this).gameObject.AddComponent<KillFeedHud>(); Hud.Configure(FeedConfig); Hud.BindGui(); ChatCommands.Register(); _configWatcher = new ConfigFileWatcher(((BaseUnityPlugin)this).Config, 1000L); _configWatcher.OnConfigFileReloaded += OnConfigReloaded; SynchronizationManager.OnConfigurationSynchronized += OnConfigSynced; _harmony = new Harmony("com.rdmods.pvpkillfeed"); _harmony.PatchAll(); Log.LogInfo((object)"PvP Kill Feed v1.1.0 loaded."); } private void OnDestroy() { SynchronizationManager.OnConfigurationSynchronized -= OnConfigSynced; if (_configWatcher != null) { _configWatcher.OnConfigFileReloaded -= OnConfigReloaded; } if ((Object)(object)Hud != (Object)null) { Hud.UnbindGui(); } Network.ResetSession(); if (_harmony != null) { _harmony.UnpatchSelf(); } } public void OnPvpKillReceived(PvpKillEvent ev) { if (!((Object)(object)Hud == (Object)null) && !Hud.LocalHidden) { Hud.Push(ev); } } private void OnConfigReloaded() { RefreshHud(); } private static void OnConfigSynced(object sender, ConfigurationSynchronizationEventArgs args) { if ((Object)(object)Instance != (Object)null) { Instance.RefreshHud(); } } private void RefreshHud() { if ((Object)(object)Hud != (Object)null) { Hud.RefreshLayout(); } } } public struct PvpKillEvent { public const int MaxNameChars = 32; public string Killer; public string Victim; public long VictimId; public int KillerRgb; public int VictimRgb; public Color KillerColor => Unpack(KillerRgb); public Color VictimColor => Unpack(VictimRgb); public void Write(ZPackage pkg) { WriteName(pkg, Killer); WriteName(pkg, Victim); pkg.Write(VictimId); pkg.Write(KillerRgb); pkg.Write(VictimRgb); } public static PvpKillEvent Read(ZPackage pkg) { return new PvpKillEvent { Killer = pkg.ReadString(), Victim = pkg.ReadString(), VictimId = pkg.ReadLong(), KillerRgb = pkg.ReadInt(), VictimRgb = pkg.ReadInt() }; } public static int Pack(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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) int num = Mathf.Clamp(Mathf.RoundToInt(c.r * 255f), 0, 255); int num2 = Mathf.Clamp(Mathf.RoundToInt(c.g * 255f), 0, 255); int num3 = Mathf.Clamp(Mathf.RoundToInt(c.b * 255f), 0, 255); return (num << 16) | (num2 << 8) | num3; } public static Color Unpack(int rgb) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) return new Color((float)((rgb >> 16) & 0xFF) / 255f, (float)((rgb >> 8) & 0xFF) / 255f, (float)(rgb & 0xFF) / 255f, 1f); } private static void WriteName(ZPackage pkg, string name) { if (string.IsNullOrEmpty(name)) { pkg.Write(""); } else { pkg.Write((name.Length > 32) ? name.Substring(0, 32) : name); } } } } namespace RdMods.PvpKillFeed.Patches { [HarmonyPatch(typeof(Character), "Damage")] public static class CharacterDamagePatch { internal static bool HasConfirmedKiller; internal static string ConfirmedKillerName; internal static int ConfirmedKillerRgb; private static void Prefix(Character __instance, HitData hit, out bool __state) { __state = false; if (!((Object)(object)__instance == (Object)null) && hit != null && __instance.IsPlayer() && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !HasConfirmedKiller && !(__instance.GetHealth() <= 0f)) { Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)__instance)) { __state = true; } } } private static void Postfix(Character __instance, HitData hit, bool __state) { if (!__state || HasConfirmedKiller || (Object)(object)__instance == (Object)null || hit == null || __instance.GetHealth() > 0f) { return; } Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)__instance)) { string text = NameResolver.OfPlayer(val); if (!string.IsNullOrEmpty(text)) { HasConfirmedKiller = true; ConfirmedKillerName = text; ConfirmedKillerRgb = GuildColor.RgbFor(val); } } } internal static void ClearCredit() { HasConfirmedKiller = false; ConfirmedKillerName = null; ConfirmedKillerRgb = GuildColor.WhiteRgb; } } [HarmonyPatch(typeof(Player), "OnSpawned")] public static class PlayerSpawnedCreditReset { private static void Postfix(Player __instance) { if ((Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer) { CharacterDamagePatch.ClearCredit(); } } } [HarmonyPatch(typeof(Player), "OnDeath")] public static class PlayerDeathPatch { private const float BroadcastCooldown = 2f; private static float _lastBroadcastTime = -999f; private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (!CharacterDamagePatch.HasConfirmedKiller || string.IsNullOrEmpty(CharacterDamagePatch.ConfirmedKillerName)) { CharacterDamagePatch.ClearCredit(); return; } float unscaledTime = Time.unscaledTime; if (unscaledTime - _lastBroadcastTime < 2f) { CharacterDamagePatch.ClearCredit(); return; } string text = NameResolver.OfPlayer(__instance); if (string.IsNullOrEmpty(text)) { CharacterDamagePatch.ClearCredit(); return; } _lastBroadcastTime = unscaledTime; Network.Broadcast(new PvpKillEvent { Killer = CharacterDamagePatch.ConfirmedKillerName, Victim = text, VictimId = __instance.GetPlayerID(), KillerRgb = CharacterDamagePatch.ConfirmedKillerRgb, VictimRgb = GuildColor.RgbFor(__instance) }); CharacterDamagePatch.ClearCredit(); } internal static void ResetDebounce() { _lastBroadcastTime = -999f; } } }