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 HornetCloakColor v1.15.0
plugins/HornetCloakColor.dll
Decompiled 2 months ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HornetCloakColor.Client; using HornetCloakColor.Interop; using HornetCloakColor.Shared; using Microsoft.CodeAnalysis; using Silksong.ModMenu.Elements; using Silksong.ModMenu.Models; using Silksong.ModMenu.Plugin; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; 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("HornetCloakColor")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.15.0.0")] [assembly: AssemblyInformationalVersion("1.15.0+0eba9b2e5df530332fab65160f0c51fed55f93df")] [assembly: AssemblyProduct("HornetCloakColor")] [assembly: AssemblyTitle("HornetCloakColor")] [assembly: InternalsVisibleTo("HornetCloakColor.SSMP")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.15.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace HornetCloakColor { [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("hornet.cloak.color", "HornetCloakColor", "1.15.0")] public class HornetCloakColorPlugin : BaseUnityPlugin { [CompilerGenerated] private sealed class <AllowlistMissingMaskSweepRoutine>d__18 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public float[] delaysSec; private float[] <>7__wrap1; private int <>7__wrap2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <AllowlistMissingMaskSweepRoutine>d__18(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_005e; } <>1__state = -1; <>7__wrap1 = delaysSec; <>7__wrap2 = 0; goto IL_0071; IL_005e: CloakMaskManager.SweepAllowlistedLoadedTexturesForMissingMaskDumps(); <>7__wrap2++; goto IL_0071; IL_0071: if (<>7__wrap2 < <>7__wrap1.Length) { float num2 = <>7__wrap1[<>7__wrap2]; if (num2 > 0f) { <>2__current = (object)new WaitForSecondsRealtime(num2); <>1__state = 1; return true; } goto IL_005e; } <>7__wrap1 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <PostConnectSsmpColorResendRoutine>d__22 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PostConnectSsmpColorResendRoutine>d__22(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = null; <>1__state = 2; return true; case 2: <>1__state = -1; SSMPBridge.ResendStoredLocalColorsToServer(); return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class <SsmpIntegrationRetryRoutine>d__19 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public HornetCloakColorPlugin <>4__this; private int <i>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SsmpIntegrationRetryRoutine>d__19(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; HornetCloakColorPlugin hornetCloakColorPlugin = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <i>5__2 = 0; break; case 1: <>1__state = -1; <i>5__2++; break; } if (<i>5__2 < 180) { if (SSMPBridge.IsAvailable) { UsernameColorHarmonyPatcher.Apply(); } if (SSMPBridge.IsAvailable && !SSMPBridge.IsRegistered) { hornetCloakColorPlugin.TryRegisterSsmpSatelliteAndLog(); } hornetCloakColorPlugin.RefreshLocalUsernameVisual(); if (SSMPBridge.IsRegistered && UsernameNetworkDelegates.TryResolveUsernameTransform != null) { hornetCloakColorPlugin.PushLocalUsernameToNetwork(); hornetCloakColorPlugin.PushLocalCloakColorToNetwork(); return false; } <>2__current = null; <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string ModVersion = "1.15.0"; public const string Id = "hornet.cloak.color"; internal static HornetCloakColorPlugin? Instance { get; private set; } internal static ManualLogSource? LogSource { get; private set; } internal CloakColorConfig ColorConfig { get; private set; } internal UsernameColorConfig UsernameColorConfig { get; private set; } public static string Name => "HornetCloakColor"; public static string Version => "1.15.0"; private void Awake() { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown Instance = this; LogSource = ((BaseUnityPlugin)this).Logger; CloakPaletteConfig.Load(); float[] missingMaskDumpAllowlistSweepDelaysSec = CloakPaletteConfig.MissingMaskDumpAllowlistSweepDelaysSec; if (missingMaskDumpAllowlistSweepDelaysSec != null && missingMaskDumpAllowlistSweepDelaysSec.Length != 0) { ((MonoBehaviour)this).StartCoroutine(AllowlistMissingMaskSweepRoutine(missingMaskDumpAllowlistSweepDelaysSec)); } CloakSceneScanner.EnsureCreated(); ColorConfig = new CloakColorConfig(((BaseUnityPlugin)this).Config); UsernameColorConfig = new UsernameColorConfig(((BaseUnityPlugin)this).Config); CloakColorApplier.SetLocalSceneColor(ColorConfig.EffectiveColor); MapMaskHarmonyPatcher.Apply(); CloakSpawnHookHarmonyPatcher.Apply(); CloakTk2dHarmonyPatcher.Apply(); CloakMeshRendererEnableHarmonyPatcher.Apply(); CloakHeroDamageHarmonyPatcher.Apply(); UsernameColorHarmonyPatcher.Apply(); TryRegisterSsmpSatelliteAndLog(); ((MonoBehaviour)this).StartCoroutine(SsmpIntegrationRetryRoutine()); ColorConfig.ColorChanged += OnConfigColorChanged; UsernameColorConfig.UsernameTintChanged += OnUsernameTintConfigChanged; HeroController.OnHeroInstanceSet += new HeroSetDelegate(OnHeroInstanceSet); ((BaseUnityPlugin)this).Logger.LogInfo((object)(Name + " v1.15.0 loaded.")); } [IteratorStateMachine(typeof(<AllowlistMissingMaskSweepRoutine>d__18))] private IEnumerator AllowlistMissingMaskSweepRoutine(float[] delaysSec) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <AllowlistMissingMaskSweepRoutine>d__18(0) { delaysSec = delaysSec }; } [IteratorStateMachine(typeof(<SsmpIntegrationRetryRoutine>d__19))] private IEnumerator SsmpIntegrationRetryRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SsmpIntegrationRetryRoutine>d__19(0) { <>4__this = this }; } private void TryRegisterSsmpSatelliteAndLog() { if (SSMPBridge.IsAvailable && SSMPBridge.TryRegister()) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"SSMP detected — multiplayer cloak + username sync enabled."); PushLocalCloakColorToNetwork(); PushLocalUsernameToNetwork(); } } internal static void SchedulePostConnectColorResend() { if (!((Object)(object)Instance == (Object)null)) { ((MonoBehaviour)Instance).StartCoroutine(Instance.PostConnectSsmpColorResendRoutine()); } } [IteratorStateMachine(typeof(<PostConnectSsmpColorResendRoutine>d__22))] private IEnumerator PostConnectSsmpColorResendRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <PostConnectSsmpColorResendRoutine>d__22(0); } private void PushLocalCloakColorToNetwork() { if (SSMPBridge.IsRegistered) { SSMPBridge.NotifyLocalCloakAppearanceChanged(BuildLocalCloakNetAppearance()); } } private CloakNetAppearance BuildLocalCloakNetAppearance() { return new CloakNetAppearance(ColorConfig.EffectiveColor, CloakNetAppearance.CentiFromMultiplier(ColorConfig.TextureSaturationBoost.Value)); } private void OnHeroInstanceSet(HeroController hero) { CloakColor effectiveColor = ColorConfig.EffectiveColor; CloakColorApplier.Apply(((Component)hero).gameObject, effectiveColor); CloakColorApplier.SetLocalSceneColor(effectiveColor); PushLocalCloakColorToNetwork(); PushLocalUsernameToNetworkAndVisual(); } private void OnConfigColorChanged(CloakColor color) { if (CloakPaletteConfig.DebugLogging) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Cloak color changed to {color}"); } if ((Object)(object)HeroController.SilentInstance != (Object)null) { CloakColorApplier.Apply(((Component)HeroController.SilentInstance).gameObject, color); } CloakColorApplier.SetLocalSceneColor(color); PushLocalCloakColorToNetwork(); LocalMapMaskTint.Refresh(GameManager.instance?.gameMap, color); MapMaskTint.BroadcastLocalColor(color); PushLocalUsernameToNetworkAndVisual(); } private void OnUsernameTintConfigChanged() { PushLocalUsernameToNetworkAndVisual(); } private void PushLocalUsernameToNetwork() { if (SSMPBridge.IsRegistered) { if (UsernameColorConfig.IsDisabled) { SSMPBridge.ClearLocalUsernameColorOnNetwork(); } else { SSMPBridge.NotifyLocalUsernameColorChanged(UsernameColorConfig.ResolveRgb(ColorConfig)); } } } private void RefreshLocalUsernameVisual() { UsernameTintCoordinator.ForceRefreshLocalHeroUsername(); } private void PushLocalUsernameToNetworkAndVisual() { PushLocalUsernameToNetwork(); RefreshLocalUsernameVisual(); } } internal static class SSMPBridge { public const string SSMPGuid = "ssmp"; private static bool _registered; private static Action<CloakNetAppearance>? _notifyCloakAppearanceChanged; private static Action<CloakColor>? _notifyUsernameColorChanged; private static Action? _clearLocalUsernameColorOnNetwork; private static Action? _resendStoredLocalColorsToServer; private static Func<ushort, CloakColor>? _getRemoteMapColor; public static bool IsAvailable => Chainloader.PluginInfos.ContainsKey("ssmp"); public static bool IsRegistered => _registered; public static bool TryRegister() { if (_registered) { return true; } if (!IsAvailable) { return false; } try { if (!TryBindSatellite()) { return false; } _registered = true; return true; } catch (Exception ex) { Log.Warn("SSMP detected but addon registration failed: " + ex.Message + ". Multiplayer cloak sync disabled; local recolor still works."); return false; } } public static void NotifyLocalCloakAppearanceChanged(CloakNetAppearance appearance) { if (_registered) { _notifyCloakAppearanceChanged?.Invoke(appearance); } } public static void NotifyLocalUsernameColorChanged(CloakColor color) { if (_registered) { _notifyUsernameColorChanged?.Invoke(color); } } public static void ClearLocalUsernameColorOnNetwork() { if (_registered) { _clearLocalUsernameColorOnNetwork?.Invoke(); } } public static CloakColor GetRemoteMapColorOrDefault(ushort playerId) { return _getRemoteMapColor?.Invoke(playerId) ?? CloakColor.Default; } public static void ResendStoredLocalColorsToServer() { if (_registered) { _resendStoredLocalColorsToServer?.Invoke(); } } internal static void SchedulePostConnectColorResend() { if (_registered) { HornetCloakColorPlugin.SchedulePostConnectColorResend(); } } private static bool TryBindSatellite() { string directoryName = Path.GetDirectoryName(typeof(HornetCloakColorPlugin).Assembly.Location); if (string.IsNullOrEmpty(directoryName)) { Log.Warn("Could not resolve plugin directory; cannot load HornetCloakColor.SSMP.dll."); return false; } string text = Path.Combine(directoryName, "HornetCloakColor.SSMP.dll"); if (!File.Exists(text)) { Log.Warn("SSMP is loaded but HornetCloakColor.SSMP.dll was not found next to HornetCloakColor.dll. Reinstall the mod so both DLLs are in the same folder."); return false; } Type type = Assembly.LoadFrom(text).GetType("HornetCloakColor.SSMPIntegration.SatelliteEntry"); if (type == null) { Log.Warn("HornetCloakColor.SSMP.dll is missing the integration entry type."); return false; } type.GetMethod("Register", BindingFlags.Static | BindingFlags.Public)?.Invoke(null, null); MethodInfo method = type.GetMethod("NotifyLocalCloakAppearanceChanged", BindingFlags.Static | BindingFlags.Public); if (method != null) { _notifyCloakAppearanceChanged = (Action<CloakNetAppearance>)Delegate.CreateDelegate(typeof(Action<CloakNetAppearance>), method); } MethodInfo method2 = type.GetMethod("GetRemoteMapColorOrDefault", BindingFlags.Static | BindingFlags.Public); if (method2 != null) { _getRemoteMapColor = (Func<ushort, CloakColor>)Delegate.CreateDelegate(typeof(Func<ushort, CloakColor>), method2); } MethodInfo method3 = type.GetMethod("NotifyLocalUsernameColorChanged", BindingFlags.Static | BindingFlags.Public); if (method3 != null) { _notifyUsernameColorChanged = (Action<CloakColor>)Delegate.CreateDelegate(typeof(Action<CloakColor>), method3); } MethodInfo method4 = type.GetMethod("ClearLocalUsernameColorOnNetwork", BindingFlags.Static | BindingFlags.Public); if (method4 != null) { _clearLocalUsernameColorOnNetwork = (Action)Delegate.CreateDelegate(typeof(Action), method4); } MethodInfo method5 = type.GetMethod("ResendStoredLocalColorsToServer", BindingFlags.Static | BindingFlags.Public); if (method5 != null) { _resendStoredLocalColorsToServer = (Action)Delegate.CreateDelegate(typeof(Action), method5); } return true; } } internal readonly struct UsernameResolveResult { internal bool Found { get; } internal bool IsLocal { get; } internal ushort PlayerId { get; } internal UsernameResolveResult(bool found, bool isLocal, ushort playerId) { Found = found; IsLocal = isLocal; PlayerId = playerId; } } internal static class UsernameNetworkDelegates { internal static Func<Transform, UsernameResolveResult>? TryResolveUsernameTransform; internal static Func<ushort, CloakColor?>? GetRemoteUsernameTintOrNull; internal static Func<bool>? GetTeamsEnabled; internal static Func<bool>? GetServerCustomUsernameOverridesTeam; internal static Func<int>? GetLocalPlayerTeamOrdinal; internal static void Clear() { TryResolveUsernameTransform = null; GetRemoteUsernameTintOrNull = null; GetTeamsEnabled = null; GetServerCustomUsernameOverridesTeam = null; GetLocalPlayerTeamOrdinal = null; } } public static class PluginInfo { public const string PLUGIN_GUID = "hornet.cloak.color"; public const string PLUGIN_NAME = "HornetCloakColor"; public const string PLUGIN_VERSION = "1.15.0"; } } namespace HornetCloakColor.Shared { public readonly struct CloakColor : IEquatable<CloakColor> { public byte R { get; } public byte G { get; } public byte B { get; } public static CloakColor Default => new CloakColor(byte.MaxValue, byte.MaxValue, byte.MaxValue); public CloakColor(byte r, byte g, byte b) { R = r; G = g; B = b; } public Color ToUnityColor() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) return new Color((float)(int)R / 255f, (float)(int)G / 255f, (float)(int)B / 255f, 1f); } public void ToHSV(out float h, out float s, out float v) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) Color.RGBToHSV(ToUnityColor(), ref h, ref s, ref v); } public static CloakColor FromUnityColor(Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) return new CloakColor((byte)Mathf.Clamp(Mathf.RoundToInt(color.r * 255f), 0, 255), (byte)Mathf.Clamp(Mathf.RoundToInt(color.g * 255f), 0, 255), (byte)Mathf.Clamp(Mathf.RoundToInt(color.b * 255f), 0, 255)); } public static bool TryParse(string value, out CloakColor color) { color = Default; if (string.IsNullOrWhiteSpace(value)) { return false; } string text = value.Trim(); if (text.Contains(",")) { string[] array = text.Split(','); if (array.Length != 3) { return false; } if (!byte.TryParse(array[0].Trim(), out var result)) { return false; } if (!byte.TryParse(array[1].Trim(), out var result2)) { return false; } if (!byte.TryParse(array[2].Trim(), out var result3)) { return false; } color = new CloakColor(result, result2, result3); return true; } string text2 = (text.StartsWith("#") ? text.Substring(1) : text); if (text2.Length != 6) { return false; } try { byte r = Convert.ToByte(text2.Substring(0, 2), 16); byte g = Convert.ToByte(text2.Substring(2, 2), 16); byte b = Convert.ToByte(text2.Substring(4, 2), 16); color = new CloakColor(r, g, b); return true; } catch { return false; } } public override string ToString() { return $"#{R:X2}{G:X2}{B:X2}"; } public bool Equals(CloakColor other) { if (R == other.R && G == other.G) { return B == other.B; } return false; } public override bool Equals(object obj) { if (obj is CloakColor other) { return Equals(other); } return false; } public override int GetHashCode() { return (R << 16) | (G << 8) | B; } public static bool operator ==(CloakColor a, CloakColor b) { return a.Equals(b); } public static bool operator !=(CloakColor a, CloakColor b) { return !a.Equals(b); } } public readonly struct CloakNetAppearance : IEquatable<CloakNetAppearance> { public CloakColor Color { get; } public byte TextureSaturationCenti { get; } public static CloakNetAppearance Default => new CloakNetAppearance(CloakColor.Default, 100); public float TextureSaturationMultiplier => (float)(int)TextureSaturationCenti / 100f; public CloakNetAppearance(CloakColor color, byte textureSaturationCenti) { Color = color; TextureSaturationCenti = textureSaturationCenti; } public static byte CentiFromMultiplier(float multiplier) { return (byte)Mathf.Clamp(Mathf.RoundToInt(multiplier * 100f), 0, 200); } public bool Equals(CloakNetAppearance other) { if (Color.Equals(other.Color)) { return TextureSaturationCenti == other.TextureSaturationCenti; } return false; } public override bool Equals(object? obj) { if (obj is CloakNetAppearance other) { return Equals(other); } return false; } public override int GetHashCode() { return (Color.GetHashCode() * 397) ^ TextureSaturationCenti; } public static bool operator ==(CloakNetAppearance a, CloakNetAppearance b) { return a.Equals(b); } public static bool operator !=(CloakNetAppearance a, CloakNetAppearance b) { return !a.Equals(b); } } internal static class Log { private static ManualLogSource? _fallback; private static ManualLogSource Source => HornetCloakColorPlugin.LogSource ?? _fallback ?? (_fallback = Logger.CreateLogSource("HornetCloakColor")); public static void Info(string msg) { Source.LogInfo((object)msg); } public static void Warn(string msg) { Source.LogWarning((object)msg); } public static void Error(string msg) { Source.LogError((object)msg); } public static void Debug(string msg) { Source.LogDebug((object)msg); } } } namespace HornetCloakColor.Interop { public static class HornetCloakColorInterop { public static event Func<CloakColor, CloakColor>? ModifyLocalCloakColorBeforeApply; public static event Func<UsernameTintContext, Color?>? ResolveLocalUsernameTint; public static event Func<UsernameTintContext, Color?>? ResolveRemoteUsernameTint; public static CloakColor ApplyCloakColorModifiers(CloakColor color) { if (HornetCloakColorInterop.ModifyLocalCloakColorBeforeApply == null) { return color; } CloakColor cloakColor = color; Delegate[] invocationList = HornetCloakColorInterop.ModifyLocalCloakColorBeforeApply.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { cloakColor = ((Func<CloakColor, CloakColor>)invocationList[i])(cloakColor); } return cloakColor; } internal static Color? InvokeLocalUsernameInterop(in UsernameTintContext ctx) { return InvokeUsernameChain(HornetCloakColorInterop.ResolveLocalUsernameTint, in ctx); } internal static Color? InvokeRemoteUsernameInterop(in UsernameTintContext ctx) { return InvokeUsernameChain(HornetCloakColorInterop.ResolveRemoteUsernameTint, in ctx); } private static Color? InvokeUsernameChain(Func<UsernameTintContext, Color?>? chain, in UsernameTintContext ctx) { if (chain == null) { return null; } Delegate[] invocationList = chain.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Color? result = ((Func<UsernameTintContext, Color?>)invocationList[i])(ctx); if (result.HasValue) { return result; } } return null; } public static CloakColor GetLocalCloakColorAfterModifiers() { return HornetCloakColorPlugin.Instance?.ColorConfig.EffectiveColor ?? CloakColor.Default; } public static CloakColor GetSyncedRemoteUsernameRgbOrDefault(ushort networkPlayerId) { return UsernameNetworkDelegates.GetRemoteUsernameTintOrNull?.Invoke(networkPlayerId) ?? CloakColor.Default; } } public readonly struct UsernameTintContext { public bool IsLocalPlayer { get; } public ushort NetworkPlayerId { get; } public int TeamOrdinal { get; } public bool TeamsEnabledOnServer { get; } public bool ServerAllowsCustomUsernameOverTeam { get; } public Color ProposedColor { get; } public UsernameTintContext(bool isLocalPlayer, ushort networkPlayerId, int teamOrdinal, bool teamsEnabledOnServer, bool serverAllowsCustomUsernameOverTeam, Color proposedColor) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) IsLocalPlayer = isLocalPlayer; NetworkPlayerId = networkPlayerId; TeamOrdinal = teamOrdinal; TeamsEnabledOnServer = teamsEnabledOnServer; ServerAllowsCustomUsernameOverTeam = serverAllowsCustomUsernameOverTeam; ProposedColor = proposedColor; } } } namespace HornetCloakColor.Client { internal static class CloakColorApplier { public static void Apply(GameObject? playerObject, CloakColor color, float? textureSaturationBoostOverride = null) { if (!((Object)(object)playerObject == (Object)null)) { bool useCloakShader = (Object)(object)CloakShaderManager.Shader != (Object)null; CloakRecolor.AttachOrUpdate(playerObject, color, useCloakShader, textureSaturationBoostOverride); } } public static void SetLocalSceneColor(CloakColor color) { CloakSceneScanner.SetColor(color); CloakSpriteRendererTint.SetColor(color); } } internal class CloakColorConfig { public enum Preset { Custom, Default, Crimson, Scarlet, Amber, Gold, Emerald, Teal, Azure, Royal, Violet, Magenta, Obsidian, Ivory } [CompilerGenerated] private static class <>O { public static MenuElementGenerator <0>__TryCreateElement; } private static readonly CloakColor DefaultCustom = new CloakColor(200, 60, 60); public ConfigEntry<Preset> PresetChoice { get; } public ConfigEntry<string> CustomHex { get; } public ConfigEntry<float> TextureSaturationBoost { get; } public CloakColor CurrentColor => Resolve(PresetChoice.Value, CustomHex.Value); public CloakColor EffectiveColor => HornetCloakColorInterop.ApplyCloakColorModifiers(CurrentColor); public event Action<CloakColor>? ColorChanged; public CloakColorConfig(ConfigFile config) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown PresetChoice = config.Bind<Preset>("Appearance", "Cloak Color Preset", Preset.Default, "Choose 'Custom' for your own hex."); CustomHex = config.Bind<string>("Appearance", "Custom Cloak Color", DefaultCustom.ToString(), "Custom cloak color used when preset is set to 'Custom'. Accepts #RRGGBB, RRGGBB, or 'r,g,b' (0-255 each)."); AcceptableValueRange<float> obj = new AcceptableValueRange<float>(0f, 2f); object[] array = new object[1]; object obj2 = <>O.<0>__TryCreateElement; if (obj2 == null) { MenuElementGenerator val = CloakTextureSaturationModMenuElement.TryCreateElement; <>O.<0>__TryCreateElement = val; obj2 = (object)val; } array[0] = obj2; TextureSaturationBoost = config.Bind<float>("Appearance", "Cloak Texture Saturation Boost", 1f, new ConfigDescription("Multiplies each masked pixel's saturation after the hue shift (1 = unchanged). Values above 1 punch up washed-out cloak art. This is not the same as your hex's HSV saturation (e.g. #00FF00 is already fully saturated as a color pick).", (AcceptableValueBase)(object)obj, array)); PresetChoice.SettingChanged += delegate { this.ColorChanged?.Invoke(EffectiveColor); }; CustomHex.SettingChanged += delegate { this.ColorChanged?.Invoke(EffectiveColor); }; TextureSaturationBoost.SettingChanged += delegate { this.ColorChanged?.Invoke(EffectiveColor); }; } internal static CloakColor ResolvePresetColor(Preset preset, string customHex) { return Resolve(preset, customHex); } private static CloakColor Resolve(Preset preset, string customHex) { CloakColor color; return preset switch { Preset.Default => CloakColor.Default, Preset.Crimson => new CloakColor(156, 36, 48), Preset.Scarlet => new CloakColor(220, 56, 72), Preset.Amber => new CloakColor(230, 140, 40), Preset.Gold => new CloakColor(232, 190, 64), Preset.Emerald => new CloakColor(56, 170, 90), Preset.Teal => new CloakColor(60, 180, 180), Preset.Azure => new CloakColor(64, 148, 230), Preset.Royal => new CloakColor(72, 92, 210), Preset.Violet => new CloakColor(140, 80, 210), Preset.Magenta => new CloakColor(220, 80, 180), Preset.Obsidian => new CloakColor(40, 40, 55), Preset.Ivory => new CloakColor(240, 230, 205), Preset.Custom => CloakColor.TryParse(customHex, out color) ? color : CloakColor.Default, _ => CloakColor.Default, }; } } internal static class CloakDiskNames { public const string NoCollectionFolder = "_NoCollection"; public static string CollectionFolder(string? tk2dCollectionName) { if (!string.IsNullOrWhiteSpace(tk2dCollectionName)) { return SanitizeFileStem(tk2dCollectionName); } return "_NoCollection"; } public static string? MaskCollectionNameForLookup(string? tk2dCollectionName) { if (string.Equals(tk2dCollectionName, "Player Prefab", StringComparison.OrdinalIgnoreCase)) { return "Knight"; } return tk2dCollectionName; } public static string SanitizeFileStem(string? name) { if (string.IsNullOrEmpty(name)) { return "tex"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); StringBuilder stringBuilder = new StringBuilder(Math.Max(name.Length, 4)); foreach (char c in name) { if (Array.IndexOf(invalidFileNameChars, c) >= 0 || c < ' ') { stringBuilder.Append('_'); } else { stringBuilder.Append(c); } } string text = stringBuilder.ToString(); if (text.Length == 0 || text == "." || text == "..") { return "tex"; } return text; } } internal static class CloakHeroDamageHarmonyPatcher { private const string HarmonyId = "hornet.cloak.color.hero-damage"; private static bool _applied; internal static void Apply() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown if (_applied) { return; } Harmony val = new Harmony("hornet.cloak.color.hero-damage"); bool flag = false; MethodInfo[] methods = typeof(HealthManager).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "TakeDamage") && !methodInfo.IsAbstract) { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(CloakHeroDamageHarmonyPatcher), "AfterHeroHealthEvent", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.Info("HornetCloakColor: patched HealthManager." + methodInfo.Name + " for post-damage cloak refresh."); flag = true; break; } } MethodInfo methodInfo2 = AccessTools.Method(typeof(HeroController), "DoSpecialDamage", new Type[1] { typeof(bool) }, (Type[])null); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(CloakHeroDamageHarmonyPatcher), "AfterHeroHealthEvent", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.Info("HornetCloakColor: patched HeroController.DoSpecialDamage for post-damage cloak refresh."); flag = true; } if (!flag) { Log.Warn("HornetCloakColor: no damage hooks patched (API mismatch). Cloak may briefly lose tint after hazards."); } _applied = true; } private static void AfterHeroHealthEvent() { try { HeroController instance = HeroController.instance; if (!((Object)(object)instance == (Object)null)) { CloakRecolor.NotifyHeroPossibleSpriteRebuild(instance); } } catch (Exception ex) { Log.Warn("CloakHeroDamageHarmonyPatcher: refresh threw: " + ex.Message); } } } internal static class CloakMaskManager { [CompilerGenerated] private sealed class <EnumerateCandidateMaskPaths>d__19 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IEnumerator, IDisposable { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private string primaryPath; public string <>3__primaryPath; private string aliasPath; public string <>3__aliasPath; string IEnumerator<string>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnumerateCandidateMaskPaths>d__19(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = primaryPath; <>1__state = 1; return true; case 1: <>1__state = -1; if (!string.Equals(primaryPath, aliasPath, StringComparison.OrdinalIgnoreCase)) { <>2__current = aliasPath; <>1__state = 2; return true; } break; case 2: <>1__state = -1; break; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<string> IEnumerable<string>.GetEnumerator() { <EnumerateCandidateMaskPaths>d__19 <EnumerateCandidateMaskPaths>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <EnumerateCandidateMaskPaths>d__ = this; } else { <EnumerateCandidateMaskPaths>d__ = new <EnumerateCandidateMaskPaths>d__19(0); } <EnumerateCandidateMaskPaths>d__.primaryPath = <>3__primaryPath; <EnumerateCandidateMaskPaths>d__.aliasPath = <>3__aliasPath; return <EnumerateCandidateMaskPaths>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<string>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <EnumerateCandidateMaskPathsWithBranch>d__20 : IEnumerable<(string path, string branch)>, IEnumerable, IEnumerator<(string path, string branch)>, IEnumerator, IDisposable { private int <>1__state; private (string path, string branch) <>2__current; private int <>l__initialThreadId; private string primaryPath; public string <>3__primaryPath; private string aliasPath; public string <>3__aliasPath; (string, string) IEnumerator<(string, string)>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnumerateCandidateMaskPathsWithBranch>d__20(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (primaryPath, "primary(raw collection folder)"); <>1__state = 1; return true; case 1: <>1__state = -1; if (!string.Equals(primaryPath, aliasPath, StringComparison.OrdinalIgnoreCase)) { <>2__current = (aliasPath, "aliasCompat(e.g. Knight for Player Prefab)"); <>1__state = 2; return true; } break; case 2: <>1__state = -1; break; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<(string path, string branch)> IEnumerable<(string, string)>.GetEnumerator() { <EnumerateCandidateMaskPathsWithBranch>d__20 <EnumerateCandidateMaskPathsWithBranch>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <EnumerateCandidateMaskPathsWithBranch>d__ = this; } else { <EnumerateCandidateMaskPathsWithBranch>d__ = new <EnumerateCandidateMaskPathsWithBranch>d__20(0); } <EnumerateCandidateMaskPathsWithBranch>d__.primaryPath = <>3__primaryPath; <EnumerateCandidateMaskPathsWithBranch>d__.aliasPath = <>3__aliasPath; return <EnumerateCandidateMaskPathsWithBranch>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<(string, string)>)this).GetEnumerator(); } } private static readonly Dictionary<string, Texture2D> ByMaskFilePath = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, Texture2D?> ByTextureCollectionKey = new Dictionary<string, Texture2D>(StringComparer.Ordinal); private static readonly Dictionary<string, Texture2D?> ByTexture2DMaskName = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private static string? _pluginDir; private static Texture2D? _blackWeight1x1; private static readonly HashSet<string> DumpedOriginalPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> DumpedTemplatePaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> MaskDiagLoggedCompositeKeys = new HashSet<string>(StringComparer.Ordinal); public static Texture2D BlackWeightMask { get { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0044: Expected O, but got Unknown if ((Object)(object)_blackWeight1x1 != (Object)null) { return _blackWeight1x1; } Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false, true); val.SetPixel(0, 0, Color.clear); val.Apply(false, true); ((Object)val).name = "CloakMask:BlackFallback"; _blackWeight1x1 = val; return val; } } private static string PluginDir { get { if (_pluginDir != null) { return _pluginDir; } string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); _pluginDir = (string.IsNullOrEmpty(directoryName) ? string.Empty : directoryName); return _pluginDir; } } private static string TextureCollectionCacheKey(int textureInstanceId, string collectionStem) { return textureInstanceId + "\u001f" + collectionStem; } internal static bool IsMaskConfirmedAbsentForBinding(Texture mainTex, string? tk2dCollectionName) { if ((Object)(object)mainTex == (Object)null || mainTex.width <= 0) { return false; } string collectionStem = CloakDiskNames.CollectionFolder(tk2dCollectionName); string key = TextureCollectionCacheKey(((Object)mainTex).GetInstanceID(), collectionStem); if (ByTextureCollectionKey.TryGetValue(key, out Texture2D value)) { return (Object)(object)value == (Object)null; } return false; } public static void OnPaletteReloaded() { foreach (Texture2D value in ByMaskFilePath.Values) { if ((Object)(object)value != (Object)null && (Object)(object)value != (Object)(object)_blackWeight1x1) { Object.Destroy((Object)(object)value); } } ByMaskFilePath.Clear(); ByTextureCollectionKey.Clear(); ByTexture2DMaskName.Clear(); DumpedOriginalPaths.Clear(); DumpedTemplatePaths.Clear(); MaskDiagLoggedCompositeKeys.Clear(); CloakMaterialApplier.InvalidateAll(); } public static bool TryGetTexture2DMask(Texture? texture, string? spriteName, out Texture2D mask) { mask = BlackWeightMask; if (string.IsNullOrEmpty(PluginDir)) { return false; } string text = (((Object)(object)texture != (Object)null) ? ((Object)texture).name : null); if (TryGetTexture2DMaskByName(text, texture, out mask)) { return true; } if (!string.Equals(spriteName, text, StringComparison.Ordinal) && TryGetTexture2DMaskByName(spriteName, texture, out mask)) { return true; } return false; } private static bool TryGetTexture2DMaskByName(string? name, Texture? referenceTexture, out Texture2D mask) { mask = BlackWeightMask; if (string.IsNullOrWhiteSpace(name)) { return false; } string text = CloakDiskNames.SanitizeFileStem(name); string text2 = Path.Combine(PluginDir, "CloakMasks", "Texture2D", text + ".png"); if (ByTexture2DMaskName.TryGetValue(text2, out Texture2D value)) { if ((Object)(object)value == (Object)null) { return false; } mask = value; return true; } if (!File.Exists(text2)) { if ((Object)(object)referenceTexture != (Object)null && referenceTexture.width > 0 && referenceTexture.height > 0 && CloakPaletteConfig.ShouldAutoDumpMissingMask(name)) { MaybeDumpDiscoveredTextureFiles(referenceTexture, text2, createEmptyMask: true, missingMaskAutoDump: true); } ByTexture2DMaskName[text2] = null; return false; } Texture2D val = LoadMaskFromDisk(text2); if ((Object)(object)val == (Object)null) { ByTexture2DMaskName[text2] = null; return false; } if ((Object)(object)referenceTexture != (Object)null && (((Texture)val).width != referenceTexture.width || ((Texture)val).height != referenceTexture.height)) { Log.Warn($"[CloakMasks] '{text2}' size {((Texture)val).width}x{((Texture)val).height} does not match texture " + $"'{((Object)referenceTexture).name}' ({referenceTexture.width}x{referenceTexture.height}). Ignoring this SpriteRenderer mask."); Object.Destroy((Object)(object)val); ByTexture2DMaskName[text2] = null; return false; } ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; ((Object)val).name = "CloakMask:Texture2D/" + text; ByTexture2DMaskName[text2] = val; if ((Object)(object)referenceTexture != (Object)null && CloakPaletteConfig.ShouldAutoDumpMissingMask(name)) { MaybeDumpDiscoveredTextureFiles(referenceTexture, text2, createEmptyMask: false); } mask = val; return true; } public static void SweepAllowlistedLoadedTexturesForMissingMaskDumps() { HashSet<string> missingMaskDumpAllowlist = CloakPaletteConfig.MissingMaskDumpAllowlist; if (missingMaskDumpAllowlist == null || missingMaskDumpAllowlist.Count == 0 || string.IsNullOrEmpty(PluginDir) || !CloakPaletteConfig.DumpDiscoveredTextures) { return; } Texture2D[] array = Resources.FindObjectsOfTypeAll<Texture2D>(); int num = 0; int num2 = 0; int num3 = 0; Texture2D[] array2 = array; foreach (Texture2D val in array2) { if ((Object)(object)val == (Object)null || ((Texture)val).width <= 0 || ((Texture)val).height <= 0) { continue; } string text = CloakDiskNames.SanitizeFileStem(((Object)val).name); if (text.Length != 0 && missingMaskDumpAllowlist.Contains(text)) { num2++; string text2 = Path.Combine(PluginDir, "CloakMasks", "Texture2D", text + ".png"); if (File.Exists(text2)) { num3++; } else if (CloakPaletteConfig.ShouldAutoDumpMissingMask(((Object)val).name)) { MaybeDumpDiscoveredTextureFiles((Texture)(object)val, text2, createEmptyMask: true, missingMaskAutoDump: true); num++; } } } string text3 = $"[CloakMasks] allowlistSweep: scanned {array.Length} Texture2D asset(s); " + $"allowlisted stem(s) seen in memory: {num2}; " + $"skipped {num3} (CloakMasks/Texture2D/*.png already exists); " + $"wrote {num} missing-mask dump(s)."; if (num2 == 0) { text3 += " No loaded Texture2D.name matched your allowlist — runtime names often differ from dumped PNG filenames, or those atlases only appear under tk2d (masks live in CloakMasks/<collection>/<stem>.png, not Texture2D/). Trigger the effect in-game and watch [CloakMasks] missingMaskDump lines, or grep logs for the real tex name."; } Log.Info(text3); } public static bool TryGetMaskForMainTexture(Texture? mainTex, string? tk2dCollectionName, out Texture2D mask) { mask = BlackWeightMask; if ((Object)(object)mainTex == (Object)null || string.IsNullOrEmpty(PluginDir)) { return false; } if (mainTex.width <= 0 || mainTex.height <= 0) { return false; } string text = CloakDiskNames.CollectionFolder(tk2dCollectionName); string text2 = CloakDiskNames.CollectionFolder(CloakDiskNames.MaskCollectionNameForLookup(tk2dCollectionName)); int instanceID = ((Object)mainTex).GetInstanceID(); string compositeKey = TextureCollectionCacheKey(instanceID, text); bool logDetail = CloakPaletteConfig.LogMaskResolutionDiagnostics && !MaskDiagLoggedCompositeKeys.Contains(compositeKey); if (ByTextureCollectionKey.TryGetValue(compositeKey, out Texture2D value)) { if ((Object)(object)value == (Object)null) { if (logDetail) { Log.Info($"[CloakMasksDiag] cache hit — no mask for compositeKey={compositeKey} tex='{((Object)mainTex).name}' id={instanceID}"); FinishMaskDiag(); } return false; } mask = value; return true; } string text3 = CloakDiskNames.SanitizeFileStem(string.IsNullOrEmpty(((Object)mainTex).name) ? $"tex_{((Object)mainTex).GetInstanceID()}" : ((Object)mainTex).name); string path = Path.Combine(PluginDir, "CloakMasks"); string text4 = Path.Combine(path, text, text3 + ".png"); string text5 = Path.Combine(path, text2, text3 + ".png"); foreach (string item in EnumerateCandidateMaskPaths(text4, text5)) { if (ByMaskFilePath.TryGetValue(item, out Texture2D value2) && (Object)(object)value2 != (Object)null) { ByTextureCollectionKey[compositeKey] = value2; mask = value2; return true; } } if (logDetail) { string text6 = tk2dCollectionName ?? "(null)"; string text7 = (string.Equals(text, text2, StringComparison.Ordinal) ? "" : (" stemAlias='" + text2 + "'")); Log.Info($"[CloakMasksDiag] disk resolve start compositeKey={compositeKey} tex='{((Object)mainTex).name}' id={instanceID} size={mainTex.width}x{mainTex.height} collectionRaw='{text6}' stemPrimary='{text}'{text7} texStem='{text3}'"); Log.Info($"[CloakMasksDiag] primary exists={File.Exists(text4)} → {text4}"); if (!string.Equals(text4, text5, StringComparison.OrdinalIgnoreCase)) { Log.Info($"[CloakMasksDiag] aliasCompat exists={File.Exists(text5)} → {text5}"); } } Texture2D val = null; string text8 = null; string text9 = null; foreach (var (text10, text11) in EnumerateCandidateMaskPathsWithBranch(text4, text5)) { if (File.Exists(text10)) { text8 = text10; text9 = text11; break; } } if (logDetail) { Log.Info("[CloakMasksDiag] branch=" + (text9 ?? "(none)") + " resolvedPath=" + (text8 ?? "(null)")); } if (text8 != null) { val = LoadMaskFromDisk(text8); if ((Object)(object)val != (Object)null && (((Texture)val).width != mainTex.width || ((Texture)val).height != mainTex.height)) { Log.Warn($"[CloakMasks] '{text8}' size {((Texture)val).width}x{((Texture)val).height} does not match atlas " + $"'{((Object)mainTex).name}' ({mainTex.width}x{mainTex.height}). Using zero mask (no recolor) for this atlas."); if (logDetail) { Log.Info("[CloakMasksDiag] RESULT: rejected loaded mask — size mismatch after branch=" + text9); } Object.Destroy((Object)(object)val); val = null; } } if ((Object)(object)val == (Object)null) { MaybeDumpDiscoveredTextureFiles(mainTex, text4, createEmptyMask: true, CloakPaletteConfig.ShouldAutoDumpMissingMask(((Object)mainTex).name)); ByTextureCollectionKey[compositeKey] = null; if (logDetail) { Log.Info("[CloakMasksDiag] RESULT: no mask (texStem=" + text3 + ")"); FinishMaskDiag(); } return false; } ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; string text12 = Path.GetFileName(Path.GetDirectoryName(text8)) ?? text; ((Object)val).name = "CloakMask:" + text12 + "/" + text3; ByMaskFilePath[text4] = val; if (!string.Equals(text4, text8, StringComparison.OrdinalIgnoreCase)) { ByMaskFilePath[text8] = val; } ByTextureCollectionKey[compositeKey] = val; MaybeDumpDiscoveredTextureFiles(mainTex, text4, createEmptyMask: false); mask = val; if (logDetail) { Log.Info("[CloakMasksDiag] RESULT: OK mask loaded bytes→texture name=" + ((Object)val).name); FinishMaskDiag(); } return true; void FinishMaskDiag() { if (logDetail) { MaskDiagLoggedCompositeKeys.Add(compositeKey); } } } [IteratorStateMachine(typeof(<EnumerateCandidateMaskPaths>d__19))] private static IEnumerable<string> EnumerateCandidateMaskPaths(string primaryPath, string aliasPath) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <EnumerateCandidateMaskPaths>d__19(-2) { <>3__primaryPath = primaryPath, <>3__aliasPath = aliasPath }; } [IteratorStateMachine(typeof(<EnumerateCandidateMaskPathsWithBranch>d__20))] private static IEnumerable<(string path, string branch)> EnumerateCandidateMaskPathsWithBranch(string primaryPath, string aliasPath) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <EnumerateCandidateMaskPathsWithBranch>d__20(-2) { <>3__primaryPath = primaryPath, <>3__aliasPath = aliasPath }; } private static void MaybeDumpDiscoveredTextureFiles(Texture mainTex, string maskPath, bool createEmptyMask, bool missingMaskAutoDump = false) { if (!CloakPaletteConfig.DumpDiscoveredTextures) { return; } string logTag = (missingMaskAutoDump ? "missingMaskDump" : "dumpDiscoveredTextures"); string directoryName = Path.GetDirectoryName(maskPath); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(maskPath); if (string.IsNullOrEmpty(directoryName) || string.IsNullOrEmpty(fileNameWithoutExtension)) { return; } int width = mainTex.width; int height = mainTex.height; if (width > 0 && height > 0) { Directory.CreateDirectory(directoryName); MaybeWriteOriginalTexture(mainTex, Path.Combine(directoryName, fileNameWithoutExtension + "-original.png"), width, height, logTag); if (createEmptyMask) { MaybeWriteEmptyMask(maskPath, width, height, logTag); } } } private static void MaybeWriteOriginalTexture(Texture mainTex, string outPath, int w, int h, string logTag) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (DumpedOriginalPaths.Contains(outPath) || File.Exists(outPath)) { return; } RenderTexture temporary = RenderTexture.GetTemporary(w, h, 0, (RenderTextureFormat)0, (RenderTextureReadWrite)1); RenderTexture active = RenderTexture.active; try { Graphics.Blit(mainTex, temporary); RenderTexture.active = temporary; Texture2D val = new Texture2D(w, h, (TextureFormat)4, false, true); val.ReadPixels(new Rect(0f, 0f, (float)w, (float)h), 0, 0); val.Apply(false, false); File.WriteAllBytes(outPath, ImageConversion.EncodeToPNG(val)); DumpedOriginalPaths.Add(outPath); Log.Info("[CloakMasks] " + logTag + ": wrote '" + outPath + "'."); Object.Destroy((Object)(object)val); } catch (Exception ex) { Log.Warn("[CloakMasks] " + logTag + ": could not write '" + outPath + "': " + ex.Message); } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } private static void MaybeWriteEmptyMask(string maskPath, int w, int h, string logTag) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown if (DumpedTemplatePaths.Contains(maskPath) || File.Exists(maskPath)) { return; } try { Texture2D val = new Texture2D(w, h, (TextureFormat)4, false, true); Color32[] pixels = (Color32[])(object)new Color32[w * h]; val.SetPixels32(pixels); val.Apply(false, false); File.WriteAllBytes(maskPath, ImageConversion.EncodeToPNG(val)); DumpedTemplatePaths.Add(maskPath); Log.Info("[CloakMasks] " + logTag + ": wrote empty mask template '" + maskPath + "'."); Object.Destroy((Object)(object)val); } catch (Exception ex) { Log.Warn("[CloakMasks] " + logTag + ": could not write empty mask '" + maskPath + "': " + ex.Message); } } private static Texture2D? LoadMaskFromDisk(string path) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown try { byte[] array = File.ReadAllBytes(path); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false, true); if (!ImageConversion.LoadImage(val, array)) { Object.Destroy((Object)(object)val); return null; } return val; } catch (Exception ex) { Log.Warn("[CloakMasks] Failed to read '" + path + "': " + ex.Message); return null; } } } internal static class CloakMaterialApplier { private enum AppliedMode : byte { None, CloakApplied, NoMaskRestored, VertexTinted } private readonly struct AppliedState { public readonly int SharedMatInstanceId; public readonly CloakColor Color; public readonly AppliedMode Mode; public readonly float TextureSaturationBoost; public AppliedState(int sharedMatInstanceId, CloakColor color, AppliedMode mode, float textureSaturationBoost = 0f) { SharedMatInstanceId = sharedMatInstanceId; Color = color; Mode = mode; TextureSaturationBoost = textureSaturationBoost; } } private static readonly Dictionary<int, AppliedState> AppliedByRenderer = new Dictionary<int, AppliedState>(); public static void InvalidateAll() { AppliedByRenderer.Clear(); } public static void InvalidateRenderer(MeshRenderer? renderer) { if (!((Object)(object)renderer == (Object)null)) { AppliedByRenderer.Remove(((Object)renderer).GetInstanceID()); } } public static void InvalidateSubtree(Transform? root) { if ((Object)(object)root == (Object)null) { return; } MeshRenderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<MeshRenderer>(true); foreach (MeshRenderer val in componentsInChildren) { if ((Object)(object)val != (Object)null) { InvalidateRenderer(val); } } } internal static tk2dBaseSprite? ResolveTk2dSprite(MeshRenderer renderer) { if ((Object)(object)renderer == (Object)null) { return null; } Transform transform = ((Component)renderer).transform; tk2dBaseSprite component = ((Component)transform).GetComponent<tk2dBaseSprite>(); if ((Object)(object)component != (Object)null) { return component; } if ((Object)(object)transform.parent != (Object)null) { tk2dBaseSprite component2 = ((Component)transform.parent).GetComponent<tk2dBaseSprite>(); if ((Object)(object)component2 != (Object)null) { return component2; } for (int i = 0; i < transform.parent.childCount; i++) { Transform child = transform.parent.GetChild(i); if (!((Object)(object)child == (Object)(object)transform)) { component = ((Component)child).GetComponent<tk2dBaseSprite>(); if ((Object)(object)component != (Object)null) { return component; } } } } return ((Component)transform).GetComponentInParent<tk2dBaseSprite>(); } public static void Apply(MeshRenderer renderer, tk2dBaseSprite? sprite, CloakColor color, bool useCloakShader, Dictionary<MeshRenderer, Shader> originalShaderByRenderer, float? textureSaturationBoostOverride = null) { if ((Object)(object)renderer == (Object)null) { return; } Material sharedMaterial = ((Renderer)renderer).sharedMaterial; if ((Object)(object)sharedMaterial == (Object)null || !sharedMaterial.HasProperty(CloakShaderManager.MainTexId)) { return; } int instanceID = ((Object)renderer).GetInstanceID(); int instanceID2 = ((Object)sharedMaterial).GetInstanceID(); bool flag = useCloakShader && (Object)(object)CloakShaderManager.Shader != (Object)null; float num = textureSaturationBoostOverride ?? GetTextureSaturationBoost(); if (AppliedByRenderer.TryGetValue(instanceID, out var value) && value.SharedMatInstanceId == instanceID2) { if (flag) { if (value.Mode == AppliedMode.CloakApplied && value.Color.Equals(color) && Mathf.Approximately(value.TextureSaturationBoost, num)) { return; } if (value.Mode == AppliedMode.NoMaskRestored) { Texture mainTexture = sharedMaterial.mainTexture; tk2dBaseSprite val = sprite ?? ResolveTk2dSprite(renderer); string tk2dCollectionName = (((Object)(object)((val != null) ? val.Collection : null) != (Object)null) ? ((Object)val.Collection).name : null); if ((Object)(object)mainTexture != (Object)null && CloakMaskManager.IsMaskConfirmedAbsentForBinding(mainTexture, tk2dCollectionName)) { return; } } } else if (value.Mode == AppliedMode.VertexTinted && value.Color.Equals(color)) { return; } } if (sprite == null) { sprite = ResolveTk2dSprite(renderer); } Material material = ((Renderer)renderer).material; if ((Object)(object)material == (Object)null) { return; } if (flag && (Object)(object)material.mainTexture == (Object)null) { AppliedByRenderer.Remove(instanceID); return; } CloakColor color2 = color; AppliedMode appliedMode; if (flag) { string tk2dCollectionName2 = (((Object)(object)((sprite != null) ? sprite.Collection : null) != (Object)null) ? ((Object)sprite.Collection).name : null); if (!CloakMaskManager.TryGetMaskForMainTexture(material.mainTexture, tk2dCollectionName2, out Texture2D mask)) { RestoreOriginalShader(renderer, material, originalShaderByRenderer); appliedMode = AppliedMode.NoMaskRestored; color2 = default(CloakColor); } else { EnsureCloakShader(renderer, material, originalShaderByRenderer); ApplyShaderProperties(material, sprite, color, mask, num); appliedMode = AppliedMode.CloakApplied; } } else { RestoreOriginalShader(renderer, material, originalShaderByRenderer); ApplyVertexTint(material, sprite, color); appliedMode = AppliedMode.VertexTinted; } Material sharedMaterial2 = ((Renderer)renderer).sharedMaterial; if ((Object)(object)sharedMaterial2 != (Object)null) { float textureSaturationBoost = ((appliedMode == AppliedMode.CloakApplied) ? num : 0f); AppliedByRenderer[instanceID] = new AppliedState(((Object)sharedMaterial2).GetInstanceID(), color2, appliedMode, textureSaturationBoost); } } public static void Restore(MeshRenderer renderer, Dictionary<MeshRenderer, Shader> originalShaderByRenderer) { if ((Object)(object)renderer == (Object)null) { return; } Material sharedMaterial = ((Renderer)renderer).sharedMaterial; if ((Object)(object)sharedMaterial == (Object)null || !sharedMaterial.HasProperty(CloakShaderManager.MainTexId)) { return; } int instanceID = ((Object)renderer).GetInstanceID(); int instanceID2 = ((Object)sharedMaterial).GetInstanceID(); if (AppliedByRenderer.TryGetValue(instanceID, out var value) && value.SharedMatInstanceId == instanceID2 && value.Mode == AppliedMode.NoMaskRestored) { return; } Material material = ((Renderer)renderer).material; if (!((Object)(object)material == (Object)null)) { RestoreOriginalShader(renderer, material, originalShaderByRenderer); Material sharedMaterial2 = ((Renderer)renderer).sharedMaterial; if ((Object)(object)sharedMaterial2 != (Object)null) { AppliedByRenderer[instanceID] = new AppliedState(((Object)sharedMaterial2).GetInstanceID(), default(CloakColor), AppliedMode.NoMaskRestored); } } } private static void EnsureCloakShader(MeshRenderer renderer, Material mat, Dictionary<MeshRenderer, Shader> map) { Shader shader = CloakShaderManager.Shader; if (!((Object)(object)mat.shader == (Object)(object)shader)) { if (!map.ContainsKey(renderer)) { map[renderer] = mat.shader; } Texture mainTexture = mat.mainTexture; mat.shader = shader; if ((Object)(object)mainTexture != (Object)null) { mat.mainTexture = mainTexture; } } } private static void RestoreOriginalShader(MeshRenderer renderer, Material mat, Dictionary<MeshRenderer, Shader> map) { if (map.TryGetValue(renderer, out Shader value) && !((Object)(object)value == (Object)null) && !((Object)(object)mat.shader == (Object)(object)value)) { Texture mainTexture = mat.mainTexture; mat.shader = value; if ((Object)(object)mainTexture != (Object)null) { mat.mainTexture = mainTexture; } } } private static void ApplyShaderProperties(Material mat, tk2dBaseSprite? sprite, CloakColor color, Texture2D mask, float satBoost) { if (color.Equals(CloakColor.Default)) { mat.SetFloat(CloakShaderManager.StrengthId, 0f); mat.SetTexture(CloakShaderManager.CloakMaskTexId, (Texture)(object)mask); return; } color.ToHSV(out var h, out var s, out var v); mat.SetFloat(CloakShaderManager.TargetHueId, h); mat.SetFloat(CloakShaderManager.TargetSatId, (s <= 0.001f) ? 0f : satBoost); mat.SetFloat(CloakShaderManager.TargetValId, Mathf.Lerp(0.6f, 1.4f, v)); mat.SetFloat(CloakShaderManager.StrengthId, 1f); mat.SetTexture(CloakShaderManager.CloakMaskTexId, (Texture)(object)mask); } internal static float GetTextureSaturationBoost() { HornetCloakColorPlugin instance = HornetCloakColorPlugin.Instance; if (instance?.ColorConfig == null) { return 1f; } return Mathf.Clamp(instance.ColorConfig.TextureSaturationBoost.Value, 0f, 2f); } internal static bool Tk2dSpriteVertexColorsSafeToMutate(tk2dBaseSprite sprite) { if ((Object)(object)sprite == (Object)null) { return false; } MeshFilter component = ((Component)sprite).GetComponent<MeshFilter>(); Mesh val = (((Object)(object)component != (Object)null) ? component.sharedMesh : null); if ((Object)(object)val == (Object)null) { return false; } int vertexCount = val.vertexCount; if (vertexCount <= 0) { return false; } Color[] colors = val.colors; if (colors != null && colors.Length != 0 && colors.Length != vertexCount) { return false; } return true; } private static void ApplyVertexTint(Material mat, tk2dBaseSprite? sprite, CloakColor color) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) Color color2 = (mat.color = color.ToUnityColor()); if ((Object)(object)sprite != (Object)null && Tk2dSpriteVertexColorsSafeToMutate(sprite)) { sprite.color = color2; } } public static void PruneDestroyed(Dictionary<MeshRenderer, Shader> map) { if (map.Count == 0) { return; } List<MeshRenderer> list = null; foreach (MeshRenderer key in map.Keys) { if (!Object.op_Implicit((Object)(object)key)) { (list ?? (list = new List<MeshRenderer>())).Add(key); } } if (list == null) { return; } foreach (MeshRenderer item in list) { map.Remove(item); } } } internal static class CloakMeshRendererEnableHarmonyPatcher { private const string HarmonyId = "hornet.cloak.color.renderer-visibility"; private static bool _applied; private static int _visibilityHookDepth; private static readonly List<MeshRenderer> SetActiveMeshScratch = new List<MeshRenderer>(); internal static void Apply() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown if (_applied) { return; } _applied = true; Harmony val = new Harmony("hornet.cloak.color.renderer-visibility"); bool flag = false; MethodInfo methodInfo = typeof(Renderer).GetProperty("enabled", BindingFlags.Instance | BindingFlags.Public)?.GetSetMethod(nonPublic: true); if (methodInfo != null) { try { val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(CloakMeshRendererEnableHarmonyPatcher), "Renderer_Enabled_Setter_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.Info("HornetCloakColor: hooked Renderer.enabled setter for same-frame hero cloak tint."); flag = true; } catch (Exception ex) { Log.Warn("CloakMeshRendererEnableHarmonyPatcher: Renderer.enabled setter patch failed (" + ex.GetType().Name + ": " + ex.Message + ")"); } } else { Log.Warn("CloakMeshRendererEnableHarmonyPatcher: Renderer.enabled setter not found."); } MethodInfo methodInfo2 = AccessTools.Method(typeof(GameObject), "SetActive", new Type[1] { typeof(bool) }, (Type[])null); if (methodInfo2 != null) { try { val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(CloakMeshRendererEnableHarmonyPatcher), "GameObject_SetActive_Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.Info("HornetCloakColor: hooked GameObject.SetActive for hero cloak tint when subtrees activate."); flag = true; } catch (Exception ex2) { Log.Warn("CloakMeshRendererEnableHarmonyPatcher: GameObject.SetActive patch failed (" + ex2.GetType().Name + ": " + ex2.Message + ")"); } } if (!flag) { Log.Warn("CloakMeshRendererEnableHarmonyPatcher: no visibility hooks applied — Witch/dash cloak tint may lag until LateUpdate/pre-render."); } } private static void Renderer_Enabled_Setter_Postfix(Renderer __instance, bool value) { if (!value) { return; } MeshRenderer val = (MeshRenderer)(object)((__instance is MeshRenderer) ? __instance : null); if (val == null || _visibilityHookDepth > 0) { return; } _visibilityHookDepth++; try { TryRefreshUnderRecolor(val); } finally { _visibilityHookDepth--; } } private static void GameObject_SetActive_Postfix(GameObject __instance, bool value) { if (!value || (Object)(object)__instance == (Object)null || _visibilityHookDepth > 0) { return; } _visibilityHookDepth++; try { CloakRecolor componentInParent = __instance.GetComponentInParent<CloakRecolor>(); if ((Object)(object)componentInParent == (Object)null) { return; } SetActiveMeshScratch.Clear(); __instance.GetComponentsInChildren<MeshRenderer>(true, SetActiveMeshScratch); foreach (MeshRenderer item in SetActiveMeshScratch) { if ((Object)(object)item != (Object)null) { componentInParent.RefreshMeshRendererNow(item); } } } catch (Exception ex) { Log.Warn("CloakMeshRendererEnableHarmonyPatcher: SetActive postfix threw on '" + ((Object)__instance).name + "': " + ex.Message); } finally { _visibilityHookDepth--; } } private static void TryRefreshUnderRecolor(MeshRenderer meshRenderer) { try { CloakRecolor componentInParent = ((Component)meshRenderer).GetComponentInParent<CloakRecolor>(); if (!((Object)(object)componentInParent == (Object)null)) { componentInParent.RefreshMeshRendererNow(meshRenderer); } } catch (Exception ex) { Log.Warn("CloakMeshRendererEnableHarmonyPatcher: enabled postfix threw on '" + ((Object)meshRenderer).name + "': " + ex.Message); } } } internal static class CloakPaletteConfig { public static bool DebugLogging { get; private set; } public static bool MaskResolutionDebugLogging { get; private set; } internal static bool LogMaskResolutionDiagnostics { get { if (!DebugLogging) { return MaskResolutionDebugLogging; } return true; } } public static bool MapIconDebugLogging { get; private set; } public static bool LogMapIconDiagnostics { get { if (!DebugLogging) { return MapIconDebugLogging; } return true; } } public static bool DumpDiscoveredTextures { get; private set; } public static HashSet<string>? MissingMaskDumpAllowlist { get; private set; } public static float[]? MissingMaskDumpAllowlistSweepDelaysSec { get; private set; } public static int HeroMeshRescanIntervalFrames { get; private set; } public static void Load() { ApplyDefaults(); string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (!string.IsNullOrEmpty(directoryName)) { string text = Path.Combine(directoryName, "cloak_palette.json"); if (File.Exists(text)) { try { if (TryApplyPaletteJson(File.ReadAllText(text))) { Log.Info("Loaded cloak runtime config from " + text + "."); if (MapIconDebugLogging) { Log.Info("[MapIcon] mapIconDebugLogging is true — tracing map/compass sync; grep log for \"[MapIcon]\"."); } if (MaskResolutionDebugLogging) { Log.Info("[CloakMasksDiag] maskResolutionDebugLogging is true — tracing mask path resolution; grep \"[CloakMasksDiag]\"."); } } else { Log.Warn("cloak_palette.json was not valid; using built-in defaults from the mod DLL."); } } catch (Exception ex) { Log.Warn("Could not read cloak_palette.json: " + ex.Message + ". Using defaults."); } } } CloakMaskManager.OnPaletteReloaded(); LogMissingMaskDumpConfigSummary(); } private static void ApplyDefaults() { DebugLogging = false; MaskResolutionDebugLogging = false; MapIconDebugLogging = false; HeroMeshRescanIntervalFrames = 4; DumpDiscoveredTextures = false; MissingMaskDumpAllowlist = null; MissingMaskDumpAllowlistSweepDelaysSec = null; } private static void LogMissingMaskDumpConfigSummary() { int num = MissingMaskDumpAllowlist?.Count ?? 0; float[] missingMaskDumpAllowlistSweepDelaysSec = MissingMaskDumpAllowlistSweepDelaysSec; string arg = ((missingMaskDumpAllowlistSweepDelaysSec == null || missingMaskDumpAllowlistSweepDelaysSec.Length == 0) ? "sweep=off" : ("sweep=" + string.Join(",", missingMaskDumpAllowlistSweepDelaysSec) + "s")); Log.Info($"[CloakMasks] texture dump policy: dumpDiscoveredTextures={DumpDiscoveredTextures} " + $"(off = no PNG dumps); allowlist={num} stems, {arg} " + "(when dumps on: allowlist + stem match → missing-mask dump; else no missing-mask dump)."); } internal static bool ShouldAutoDumpMissingMask(string? textureName) { string text = NormalizeMaskDumpStem(textureName); if (text.Length == 0) { return false; } if (MissingMaskDumpAllowlist != null && MissingMaskDumpAllowlist.Count > 0) { return MissingMaskDumpAllowlist.Contains(text); } return false; } internal static string NormalizeMaskDumpStem(string? textureName) { if (string.IsNullOrWhiteSpace(textureName)) { return string.Empty; } string text = textureName.Trim(); if (text.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) { string text2 = text; text = text2.Substring(0, text2.Length - 4); } return CloakDiskNames.SanitizeFileStem(text); } private static bool TryApplyPaletteJson(string json) { if (string.IsNullOrWhiteSpace(json)) { return false; } string text = json.TrimStart(); if (!text.StartsWith("{", StringComparison.Ordinal)) { return false; } if (TryExtractBool(text, "debugLogging", out var value)) { DebugLogging = value; } if (TryExtractBool(text, "maskResolutionDebugLogging", out var value2)) { MaskResolutionDebugLogging = value2; } if (TryExtractBool(text, "mapIconDebugLogging", out var value3)) { MapIconDebugLogging = value3; } if (TryExtractInt(text, "heroMeshRescanIntervalFrames", out var value4) && value4 > 0 && value4 <= 600) { HeroMeshRescanIntervalFrames = value4; } if (TryExtractBool(text, "dumpDiscoveredTextures", out var value5)) { DumpDiscoveredTextures = value5; } if (TryExtractStringArray(text, "missingMaskDumpAllowlist", out string[] values)) { MissingMaskDumpAllowlist = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = values; for (int i = 0; i < array.Length; i++) { string text2 = NormalizeMaskDumpStem(array[i]); if (text2.Length > 0) { MissingMaskDumpAllowlist.Add(text2); } } } if (TryExtractFloatArray(text, "missingMaskDumpAllowlistSweepDelaysSec", out float[] values2)) { MissingMaskDumpAllowlistSweepDelaysSec = ((values2.Length != 0) ? values2 : null); } return true; } private static bool TryExtractInt(string json, string key, out int value) { value = 0; string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*(?<n>-?[0-9]+)"; Match match = Regex.Match(json, pattern, RegexOptions.CultureInvariant); if (!match.Success) { return false; } return int.TryParse(match.Groups["n"].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out value); } private static bool TryExtractBool(string json, string key, out bool value) { value = false; string pattern = "\"" + Regex.Escape(key) + "\"\\s*:\\s*(?<v>true|false)"; Match match = Regex.Match(json, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); if (!match.Success) { return false; } value = match.Groups["v"].Value.Equals("true", StringComparison.OrdinalIgnoreCase); return true; } private static bool TryExtractStringArray(string json, string key, out string[] values) { values = Array.Empty<string>(); if (!TryExtractJsonArraySlice(json, key, out string inner)) { return false; } List<string> list = new List<string>(); foreach (Match item in Regex.Matches(inner, "\"((?:\\\\.|[^\"\\\\])*)\"", RegexOptions.CultureInvariant)) { list.Add(UnescapeJsonString(item.Groups[1].Value)); } values = list.ToArray(); return true; } private static bool TryExtractFloatArray(string json, string key, out float[] values) { values = Array.Empty<float>(); if (!TryExtractJsonArraySlice(json, key, out string inner)) { return false; } string[] array = inner.Split(','); List<float> list = new List<float>(array.Length); string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text = array2[i].Trim(); if (text.Length != 0) { if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return false; } list.Add(result); } } values = list.ToArray(); return true; } private static bool TryExtractJsonArraySlice(string json, string key, out string inner) { inner = string.Empty; string text = "\"" + key + "\""; int num = json.IndexOf(text, StringComparison.Ordinal); if (num < 0) { return false; } int num2 = json.IndexOf('[', num + text.Length); if (num2 < 0) { return false; } int num3 = 0; int num4 = -1; for (int i = num2; i < json.Length; i++) { switch (json[i]) { case '[': num3++; continue; case ']': num3--; if (num3 != 0) { continue; } break; default: continue; } num4 = i; break; } if (num4 < 0) { return false; } inner = json.Substring(num2 + 1, num4 - num2 - 1); return true; } private static string UnescapeJsonString(string s) { if (string.IsNullOrEmpty(s) || s.IndexOf('\\') < 0) { return s; } return s.Replace("\\\"", "\"", StringComparison.Ordinal).Replace("\\\\", "\\", StringComparison.Ordinal); } } [DefaultExecutionOrder(32000)] [DisallowMultipleComponent] internal class CloakRecolor : MonoBehaviour { [CompilerGenerated] private static class <>O { public static CameraCallback <0>__OnBuiltInCameraPreRender; public static Action<ScriptableRenderContext, Camera> <1>__OnSrpBeginCameraRendering; } private static readonly List<CloakRecolor> ActiveInstances = new List<CloakRecolor>(); private static byte _preDrawHookRegistration; private static int _lastPreDrawTintFrame = -1; private float? _textureSaturationBoostOverride; private readonly Dictionary<MeshRenderer, Shader> _originalShaderByRenderer = new Dictionary<MeshRenderer, Shader>(); private readonly List<MeshRenderer> _meshCache = new List<MeshRenderer>(); private readonly HashSet<MeshRenderer> _meshCacheSet = new HashSet<MeshRenderer>(); private int _meshRescanCountdown; private bool _meshCacheInvalid = true; public CloakColor Color { get; private set; } = CloakColor.Default; public bool UseCloakShader { get; private set; } = true; internal void ApplyFromTk2dPipeline(tk2dBaseSprite sprite) { MeshRenderer component = ((Component)sprite).GetComponent<MeshRenderer>(); if (!((Object)(object)component == (Object)null)) { EnsureMeshRendererTracked(component); CloakMaterialApplier.InvalidateRenderer(component); CloakMaterialApplier.Apply(component, sprite, Color, UseCloakShader, _originalShaderByRenderer, _textureSaturationBoostOverride); } } internal static void NotifyHeroPossibleSpriteRebuild(HeroController? hero) { if (!((Object)(object)hero == (Object)null)) { ((Component)hero).GetComponent<CloakRecolor>()?.ForceHierarchyRefresh(); } } private void OnEnable() { _meshCacheInvalid = true; if (!ActiveInstances.Contains(this)) { ActiveInstances.Add(this); } EnsurePreDrawTintHooks(); } private void OnDisable() { ActiveInstances.Remove(this); TeardownPreDrawTintHooksIfIdle(); } private static void EnsurePreDrawTintHooks() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_001d: 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_0028: Expected O, but got Unknown if (_preDrawHookRegistration == 0) { CameraCallback onPreRender = Camera.onPreRender; object obj = <>O.<0>__OnBuiltInCameraPreRender; if (obj == null) { CameraCallback val = OnBuiltInCameraPreRender; <>O.<0>__OnBuiltInCameraPreRender = val; obj = (object)val; } Camera.onPreRender = (CameraCallback)Delegate.Combine((Delegate?)(object)onPreRender, (Delegate?)obj); _preDrawHookRegistration |= 1; if ((Object)(object)GraphicsSettings.defaultRenderPipeline != (Object)null) { RenderPipelineManager.beginCameraRendering += OnSrpBeginCameraRendering; _preDrawHookRegistration |= 2; } } } private static void TeardownPreDrawTintHooksIfIdle() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown if (ActiveInstances.Count > 0 || _preDrawHookRegistration == 0) { return; } if (((uint)_preDrawHookRegistration & (true ? 1u : 0u)) != 0) { CameraCallback onPreRender = Camera.onPreRender; object obj = <>O.<0>__OnBuiltInCameraPreRender; if (obj == null) { CameraCallback val = OnBuiltInCameraPreRender; <>O.<0>__OnBuiltInCameraPreRender = val; obj = (object)val; } Camera.onPreRender = (CameraCallback)Delegate.Remove((Delegate?)(object)onPreRender, (Delegate?)obj); } if ((_preDrawHookRegistration & 2u) != 0) { RenderPipelineManager.beginCameraRendering -= OnSrpBeginCameraRendering; } _preDrawHookRegistration = 0; } private static void OnBuiltInCameraPreRender(Camera cam) { if (IsGameplayCameraForPreDrawTint(cam)) { RunPreDrawTintPass(); } } private static void OnSrpBeginCameraRendering(ScriptableRenderContext _, Camera cam) { if (IsGameplayCameraForPreDrawTint(cam)) { RunPreDrawTintPass(); } } private static bool IsGameplayCameraForPreDrawTint(Camera? cam) { if ((Object)(object)cam == (Object)null || !((Behaviour)cam).enabled) { return false; } Camera main = Camera.main; if ((Object)(object)main != (Object)null) { return cam == main; } return true; } private static void RunPreDrawTintPass() { int frameCount = Time.frameCount; if (frameCount == _lastPreDrawTintFrame) { return; } _lastPreDrawTintFrame = frameCount; for (int num = ActiveInstances.Count - 1; num >= 0; num--) { CloakRecolor cloakRecolor = ActiveInstances[num]; if (!Object.op_Implicit((Object)(object)cloakRecolor)) { ActiveInstances.RemoveAt(num); } else { CloakMaterialApplier.PruneDestroyed(cloakRecolor._originalShaderByRenderer); cloakRecolor.ApplyToCachedMeshRenderersCore(); } } } internal void ForceHierarchyRefresh() { _meshCacheInvalid = true; RebuildMeshCache(); CloakMaterialApplier.InvalidateSubtree(((Component)this).transform); ApplyToCachedMeshRenderersCore(); } private void LateUpdate() { CloakMaterialApplier.PruneDestroyed(_originalShaderByRenderer); MaybeRefreshMeshCache(); ApplyToCachedMeshRenderersCore(); } public void Configure(CloakColor color, bool useCloakShader, float? textureSaturationBoostOverride = null) { Color = color; UseCloakShader = useCloakShader; _textureSaturationBoostOverride = textureSaturationBoostOverride; CloakSceneScanner.EnsureCreated(); CloakSceneScanner.ReleaseEligibleUnderTransform(((Component)this).transform); _meshCacheInvalid = true; RebuildMeshCache(); CloakMaterialApplier.InvalidateSubtree(((Component)this).transform); ApplyToCachedMeshRenderersCore(); } public void SetColor(CloakColor color) { Color = color; ApplyToCachedMeshRenderersCore(); } private void MaybeRefreshMeshCache() { if (_meshCacheInvalid) { RebuildMeshCache(); } else if (--_meshRescanCountdown <= 0) { RebuildMeshCache(); } } private void RebuildMeshCache() { _meshCacheInvalid = false; _meshRescanCountdown = Mathf.Max(1, CloakPaletteConfig.HeroMeshRescanIntervalFrames); _meshCache.Clear(); _meshCacheSet.Clear(); MeshRenderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<MeshRenderer>(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return; } MeshRenderer[] array = componentsInChildren; foreach (MeshRenderer val in array) { if (!((Object)(object)val == (Object)null) && !IsUnderSsmpUsernameObject(((Component)val).transform)) { _meshCache.Add(val); _meshCacheSet.Add(val); } } } private void EnsureMeshRendererTracked(MeshRenderer meshRenderer) { if (!((Object)(object)meshRenderer == (Object)null) && !IsUnderSsmpUsernameObject(((Component)meshRenderer).transform) && _meshCacheSet.Add(meshRenderer)) { _meshCache.Add(meshRenderer); } } internal void RefreshMeshRendererNow(MeshRenderer mr) { if (!((Object)(object)mr == (Object)null) && !IsUnderSsmpUsernameObject(((Component)mr).transform)) { EnsureMeshRendererTracked(mr); CloakMaterialApplier.InvalidateRenderer(mr); CloakMaterialApplier.Apply(mr, CloakMaterialApplier.ResolveTk2dSprite(mr), Color, UseCloakShader, _originalShaderByRenderer, _textureSaturationBoostOverride); } } private void ApplyToCachedMeshRenderersCore() { foreach (MeshRenderer item in _meshCache) { if ((Object)(object)item == (Object)null) { _meshCacheInvalid = true; } else if (!IsUnderSsmpUsernameObject(((Component)item).transform)) { CloakMaterialApplier.Apply(item, null, Color, UseCloakShader, _originalShaderByRenderer, _textureSaturationBoostOverride); } } } private static bool IsUnderSsmpUsernameObject(Transform t) { Transform val = t; while ((Object)(object)val != (Object)null) { if (((Object)val).name == "Username") { return true; } val = val.parent; } return false; } public static CloakRecolor? AttachOrUpdate(GameObject? playerObject, CloakColor color, bool useCloakShader, float? textureSaturationBoostOverride = null) { if ((Object)(object)playerObject == (Object)null) { return null; } CloakRecolor cloakRecolor = playerObject.GetComponent<CloakRecolor>(); if ((Object)(object)cloakRecolor == (Object)null) { cloakRecolor = playerObject.AddComponent<CloakRecolor>(); } cloakRecolor.Configure(color, useCloakShader, textureSaturationBoostOverride); return cloakRecolor; } } [DefaultExecutionOrder(10000)] [DisallowMultipleComponent] internal class CloakSceneScanner : MonoBehaviour { [CompilerGenerated] private sealed class <RebuildEligibleCacheCo>d__29 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public CloakSceneScanner <>4__this; private Stopwatch <sw>5__2; private SpriteRenderer[] <>7__wrap2; private int <>7__wrap3; private tk2dSprite[] <>7__wrap4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <RebuildEligibleCacheCo>d__29(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <sw>5__2 = null; <>7__wrap2 = null; <>7__wrap4 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; CloakSceneScanner cloakSceneScanner = <>4__this; switch (num) { default: return false; case 0: { <>1__state = -1; <sw>5__2 = Stopwatch.StartNew(); SpriteRenderer[] array2 = Object.FindObjectsByType<SpriteRenderer>((FindObjectsInactive)1, (FindObjectsSortMode)0); if (array2 != null) { <>7__wrap2 = array2; <>7__wrap3 = 0; goto IL_00c5; } goto IL_00dc; } case 1: <>1__state = -1; <sw>5__2.Restart(); goto IL_00b7; case 2: { <>1__state = -1; tk2dSprite[] array = Object.FindObjectsByType<tk2dSprite>((FindObjectsSortMode)0); if (array == null) { break; } <>7__wrap4 = array; <>7__wrap3 = 0; goto IL_0185; } case 3: { <>1__state = -1; <sw>5__2.Restart(); goto IL_0177; } IL_0185: if (<>7__wrap3 < <>7__wrap4.Length) { tk2dSprite val = <>7__wrap4[<>7__wrap3]; if ((Object)(object)val != (Object)null) { cloakSceneScanner.TryEnrollEligible((tk2dBaseSprite)(object)val, "Rescan"); } if (<sw>5__2.Elapsed.TotalMilliseconds > 1.5) { <>2__current = null; <>1__state = 3; return true; } goto IL_0177; } <>7__wrap4 = null; break; IL_00dc: <>2__current = null; <>1__state = 2; return true; IL_00b7: <>7__wrap3++; goto IL_00c5; IL_00c5: if (<>7__wrap3 < <>7__wrap2.Length) { SpriteRenderer val2 = <>7__wrap2[<>7__wrap3]; if ((Object)(object)val2 != (Object)null) { CloakSpriteRendererTint.Watch(val2); } if (<sw>5__2.Elapsed.TotalMilliseconds > 1.5) { <>2__current = null; <>1__state = 1; return true; } goto IL_00b7; } <>7__wrap2 = null; goto IL_00dc; IL_0177: <>7__wrap3++; goto IL_0185; } cloakSceneScanner._rebuildCo = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const float RescanIntervalSec = 2f; private const double RebuildBudgetMs = 1.5; private CloakColor _color = CloakColor.Default; private readonly Dictionary<MeshRenderer, Shader> _originalShaderByRenderer = new Dictionary<MeshRenderer, Shader>(); private readonly HashSet<int> _loggedTextureIds = new HashSet<int>(); private readonly HashSet<int> _loggedRendererIds = new HashSet<int>(); private readonly List<MeshRenderer> _eligibleCache = new List<MeshRenderer>(); private readonly Dictionary<MeshRenderer, tk2dBaseSprite> _eligibleSprite = new Dictionary<MeshRenderer, tk2dBaseSprite>(); private readonly HashSet<MeshRenderer> _eligibleSet = new HashSet<MeshRenderer>(); private bool _eligibleCacheDirty = true; private float _nextRescanTime; private Coroutine? _rebuildCo; public static CloakSceneScanner? Instance { get; private set; } public static void EnsureCreated() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("HornetCloakColorSceneScanner"); Object.DontDestroyOnLoad((Object)val); Instance = val.AddComponent<CloakSceneScanner>(); } } internal static void ReleaseEligibleUnderTransform(Transform? heroRoot) { if (!((Object)(object)Instance == (Object)null) && !((Object)(object)heroRoot == (Object)null)) { Instance.RemoveEligibleUnderRoot(heroRoot); } } public static void SetColor(CloakColor color) { EnsureCreated(); Instance._color = color; CloakMaterialApplier.InvalidateAll(); } private void OnEnable() { SceneManager.activeSceneChanged += OnActiveSceneChanged; } private void OnDisable() { SceneManager.activeSceneChanged -= OnActiveSceneChanged; } private void OnActiveSceneChanged(Scene from, Scene to) { _eligibleCacheDirty = true; CloakMaterialApplier.InvalidateAll(); if (_rebuildCo != null) { ((MonoBehaviour)this).StopCoroutine(_rebuildCo); _rebuildCo = null; } _eligibleCache.Clear(); _eligibleSet.Clear(); _eligibleSprite.Clear(); } private void LateUpdate() { CloakMaterialApplier.PruneDestroyed(_originalShaderByRenderer); float realtimeSinceStartup = Time.realtimeSinceStartup; if (_rebuildCo == null && (_eligibleCacheDirty || realtimeSinceStartup >= _nextRescanTime)) { _eligibleCacheDirty = false; _nextRescanTime = realtimeSinceStartup + 2f; _rebuildCo = ((MonoBehaviour)this).StartCoroutine(RebuildEligibleCacheCo()); } ApplyEligibleCache(); } private void RemoveEligibleUnderRoot(Transform root) { for (int num = _eligibleCache.Count - 1; num >= 0; num--) { MeshRenderer val = _eligibleCache[num]; if ((Object)(object)val == (Object)null) { _eligibleCache.RemoveAt(num); } else if (IsTransformUnderAncestor(((Component)val).transform, root)) { _eligibleCache.RemoveAt(num); _eligibleSet.Remove(val); _eligibleSprite.Remove(val); CloakMaterialApplier.InvalidateRenderer(val); } } } internal static void OnSpriteSpawned(tk2dSprite sprite) { if (!((Object)(object)Instance == (Object)null) && !((Object)(object)sprite == (Object)null)) { Instance.TryEnrollEligible((tk2dBaseSprite)(object)sprite, "Spawn"); } } internal static void OnTk2dPipelineComplete(tk2dBaseSprite sprite) { if ((Object)(object)Instance == (Object)null || (Object)(object)sprite == (Object)null || (Object)(object)((Component)sprite).GetComponentInParent<CloakRecolor>() != (Object)null || IsUnderSsmpPlayerContainer(((Component)sprite).transform)) { return; } MeshRenderer component = ((Component)sprite).GetComponent<MeshRenderer>(); if (!((Object)(object)component == (Object)null)) { CloakMaterialApplier.InvalidateRenderer(component); if (Instance._eligibleSet.Contains(component)) { Instance._eligibleSprite[component] = sprite; CloakMaterialApplier.Apply(component, sprite, Instance._color, useCloakShader: true, Instance._originalShaderByRenderer); } else { Instance.TryEnrollEligible(sprite, "Tk2dPipeline"); } } } private static bool TryEvaluateScannerEligibility(MeshRenderer renderer, tk2dBaseSprite sprite, out bool wouldLogMissingMask) { wouldLogMissingMask = false; if ((Object)(object)renderer == (Object)null || (Object)(object)sprite == (Object)null) { return false; } Material sharedMaterial = ((Renderer)renderer).sharedMaterial; if ((Object)(object)sharedMaterial == (Object)null) { return false; } if (!sharedMaterial.HasProperty(CloakShaderManager.MainTexId)) { return false; } Texture mainTexture = sharedMaterial.mainTexture; if ((Object)(object)mainTexture == (Object)null) { return false; } if (IsUnderSsmpPlayerContainer(((Component)renderer).transform)) { return false; } if (IsUnderLocalHero(((Component)renderer).transform)) { return false; } if ((Object)(object)((Component)renderer).GetComponentInParent<CloakRecolor>() != (Object)null) { return false; } if (IsCompassIcon(((Component)renderer).transform)) { return false; } string tk2dCollectionName = (((Object)(object)sprite.Collection != (Object)null) ? (((Object)sprite.Collection).name ?? string.Empty) : string.Empty); if (CloakMaskManager.TryGetMaskForMainTexture(mainTexture, tk2dCollectionName, out Texture2D _)) { return true; } wouldLogMissingMask = true; return false; } private void TryEnrollEligible(tk2dBaseSprite sprite, string source) { MeshRenderer component = ((Component)sprite).GetComponent<MeshRenderer>(); if ((Object)(object)component == (Object)null || _eligibleSet.Contains(component)) { return; } if (!TryEvaluateScannerEligibility(component, sprite, out var wouldLogMissingMask)) { if (wouldLogMissingMask) { MaybeLogIgnoredTexture(component, sprite); } return; } _eligibleSet.Add(component); _eligibleCache.Add(component); _eligibleSprite[component] = sprite; if (CloakPaletteConfig.DebugLogging && _loggedRendererIds.Add(((Object)component).GetInstanceID())) { Texture val = (((Object)(object)((Renderer)component).sharedMaterial != (Object)null) ? ((Renderer)component).sharedMaterial.mainTexture : null); string arg = (((Object)(object)val != (Object)null) ? ((Object)val).name : "(null)"); int num = (((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0); string arg2 = (((Object)(object)sprite.Collection != (Object)null) ? (((Object)sprite.Collection).name ?? string.Empty) : string.Empty); Log.Info("[Scanner/" + source + "] Tinting masked orphan renderer '" + FormatTransformPath(((Component)component).transform) + "' " + $"(tex={arg}, collection='{arg2}', id={num})"); } CloakMaterialApplier.Apply(component, sprite, _color, useCloakShader: true, _originalShaderByRenderer); } private void MaybeLogIgnoredTexture(MeshRenderer renderer, tk2dBaseSprite sprite) { if (!CloakPaletteConfig.DebugLogging) { return; } Material sharedMaterial = ((Renderer)renderer).sharedMaterial; if (!((Object)(object)sharedMaterial == (Object)null) && sharedMaterial.HasProperty(CloakShaderManager.MainTexId)) { Texture mainTexture = sharedMaterial.mainTexture; if (!((Object)(object)mainTexture == (Object)null) && _loggedTextureIds.Add(((Object)mainTexture).GetInstanceID())) { string text = (((Object)(object)sprite.Collection != (Object)null) ? (((Object)sprite.Collection).name ?? string.Empty) : string.Empty); Log.Info("[Scanner] Ignored texture (no CloakMasks PNG): " + ((Object)mainTexture).name + " " + $"(id={((Object)mainTexture).GetInstanceID()}) on '{FormatTransformPath(((Component)renderer).transform)}' " + "(collection='" + text + "')"); } } } [IteratorStateMachine(typeof(<RebuildEligibleCacheCo>d__29))] private IEnumerator RebuildEligibleCacheCo() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <RebuildEligibleCacheCo>d__29(0) { <>4__this = this }; } private void ApplyEligibleCache() { for (int num = _eligibleCache.Count - 1; num >= 0; num--) { MeshRenderer val = _eligibleCache[num]; if ((Object)(object)val == (Object)null)
plugins/HornetCloakColor.SSMP.dll
Decompiled 2 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using HornetCloakColor.Client; using HornetCloakColor.Server; using HornetCloakColor.Shared; using Microsoft.CodeAnalysis; using SSMP.Api.Client; using SSMP.Api.Client.Networking; using SSMP.Api.Server; using SSMP.Api.Server.Networking; using SSMP.Game; using SSMP.Networking.Packet; using SSMP.Networking.Packet.Data; using UnityEngine; [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("HornetCloakColor.SSMP")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+0eba9b2e5df530332fab65160f0c51fed55f93df")] [assembly: AssemblyProduct("HornetCloakColor.SSMP")] [assembly: AssemblyTitle("HornetCloakColor.SSMP")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 HornetCloakColor.SSMPIntegration { public static class SatelliteEntry { public static void Register() { ClientAddon clientAddon = new ClientAddon(); ServerAddon serverAddon = new ServerAddon(); ClientAddon.RegisterAddon((ClientAddon)(object)clientAddon); ServerAddon.RegisterAddon((ServerAddon)(object)serverAddon); } public static void NotifyLocalCloakAppearanceChanged(CloakNetAppearance appearance) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) ClientAddon.Instance?.SetLocalCloakAppearance(appearance); } public static CloakColor GetRemoteMapColorOrDefault(ushort playerId) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) return ClientAddon.Instance?.GetRemoteMapColorOrDefault(playerId) ?? CloakColor.Default; } public static void NotifyLocalUsernameColorChanged(CloakColor color) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) ClientAddon.Instance?.SetLocalUsernameTint(hasCustomUsernameTint: true, color); } public static void ClearLocalUsernameColorOnNetwork() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) ClientAddon.Instance?.SetLocalUsernameTint(hasCustomUsernameTint: false, default(CloakColor)); } public static void ResendStoredLocalColorsToServer() { ClientAddon.Instance?.ForceResendLocalColorsToServer(); } } } namespace HornetCloakColor.Shared { internal enum PacketId { CloakColorUpdate, UsernameColorUpdate, ServerUsernameColorRules } internal class CloakColorPacket : IPacketData { public ushort PlayerId; public CloakColor Color; public byte TextureSaturationCenti = 100; public bool IsReliable => true; public bool DropReliableDataIfNewerExists => true; public void WriteData(IPacket packet) { packet.Write(PlayerId); packet.Write(((CloakColor)(ref Color)).R); packet.Write(((CloakColor)(ref Color)).G); packet.Write(((CloakColor)(ref Color)).B); packet.Write(TextureSaturationCenti); } public void ReadData(IPacket packet) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) PlayerId = packet.ReadUShort(); byte b = packet.ReadByte(); byte b2 = packet.ReadByte(); byte b3 = packet.ReadByte(); Color = new CloakColor(b, b2, b3); TextureSaturationCenti = packet.ReadByte(); } } internal class UsernameColorPacket : IPacketData { public ushort PlayerId; public CloakColor Color; public bool HasCustomUsernameTint; public bool IsReliable => true; public bool DropReliableDataIfNewerExists => true; public void WriteData(IPacket packet) { packet.Write(PlayerId); packet.Write(((CloakColor)(ref Color)).R); packet.Write(((CloakColor)(ref Color)).G); packet.Write(((CloakColor)(ref Color)).B); packet.Write(HasCustomUsernameTint); } public void ReadData(IPacket packet) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) PlayerId = packet.ReadUShort(); byte b = packet.ReadByte(); byte b2 = packet.ReadByte(); byte b3 = packet.ReadByte(); Color = new CloakColor(b, b2, b3); HasCustomUsernameTint = packet.ReadBool(); } } internal class ServerUsernameColorRulesPacket : IPacketData { public bool CustomUsernameColorsOverrideTeamColors; public bool IsReliable => true; public bool DropReliableDataIfNewerExists => true; public void WriteData(IPacket packet) { packet.Write(CustomUsernameColorsOverrideTeamColors); } public void ReadData(IPacket packet) { CustomUsernameColorsOverrideTeamColors = packet.ReadBool(); } } internal static class ServerAddonReceivePacketFactory { public static IPacketData Instantiate(PacketId id) { return (IPacketData)(id switch { PacketId.CloakColorUpdate => new CloakColorPacket(), PacketId.UsernameColorUpdate => new UsernameColorPacket(), PacketId.ServerUsernameColorRules => new ServerUsernameColorRulesPacket(), _ => new CloakColorPacket(), }); } } internal static class ClientAddonReceivePacketFactory { public static IPacketData Instantiate(PacketId id) { return (IPacketData)(id switch { PacketId.CloakColorUpdate => new PacketDataCollection<CloakColorPacket>(), PacketId.UsernameColorUpdate => new PacketDataCollection<UsernameColorPacket>(), PacketId.ServerUsernameColorRules => new ServerUsernameColorRulesPacket(), _ => new PacketDataCollection<CloakColorPacket>(), }); } } } namespace HornetCloakColor.Server { internal sealed class PlayerCosmeticsTracker { private readonly Dictionary<ushort, CloakNetAppearance> _cloaks = new Dictionary<ushort, CloakNetAppearance>(); private readonly Dictionary<ushort, CloakColor> _usernameTints = new Dictionary<ushort, CloakColor>(); internal IReadOnlyDictionary<ushort, CloakNetAppearance> Cloaks => _cloaks; internal IReadOnlyDictionary<ushort, CloakColor> UsernameTints => _usernameTints; internal void SetCloak(ushort playerId, CloakNetAppearance appearance) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) _cloaks[playerId] = appearance; } internal void SetUsernameTint(ushort playerId, bool hasCustomTint, CloakColor color) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!hasCustomTint) { _usernameTints.Remove(playerId); } else { _usernameTints[playerId] = color; } } internal void RemovePlayer(ushort playerId) { _cloaks.Remove(playerId); _usernameTints.Remove(playerId); } } internal class ServerAddon : ServerAddon { private readonly PlayerCosmeticsTracker _tracker = new PlayerCosmeticsTracker(); private readonly bool _customUsernameColorsOverrideTeamColors = ServerUsernameRulesStore.LoadCustomUsernameColorsOverrideTeamColors(); private readonly HashSet<ushort> _sceneCatchUpSent = new HashSet<ushort>(); private IServerApi? _api; protected override string Name => "HornetCloakColor"; protected override string Version => "1.15.0"; public override uint ApiVersion => 1u; public override bool NeedsNetwork => true; public override void Initialize(IServerApi serverApi) { _api = serverApi; ServerCosmeticsPacketSender.Init(serverApi.NetServer.GetNetworkSender<PacketId>((ServerAddon)(object)this)); IServerAddonNetworkReceiver<PacketId> networkReceiver = serverApi.NetServer.GetNetworkReceiver<PacketId>((ServerAddon)(object)this, (Func<PacketId, IPacketData>)ServerAddonReceivePacketFactory.Instantiate); networkReceiver.RegisterPacketHandler<CloakColorPacket>(PacketId.CloakColorUpdate, (GenericServerPacketHandler<CloakColorPacket>)OnCloakColorUpdate); networkReceiver.RegisterPacketHandler<UsernameColorPacket>(PacketId.UsernameColorUpdate, (GenericServerPacketHandler<UsernameColorPacket>)OnUsernameColorUpdate); serverApi.ServerManager.PlayerConnectEvent += OnPlayerConnect; serverApi.ServerManager.PlayerEnterSceneEvent += OnPlayerEnterSceneFirstCatchUp; serverApi.ServerManager.PlayerDisconnectEvent += OnPlayerDisconnect; Log.Info(_customUsernameColorsOverrideTeamColors ? "Server addon initialized (custom username colors may override team colors)." : "Server addon initialized (team colors take precedence over custom username colors when teams are used)."); } private void OnPlayerConnect(IServerPlayer player) { PushJoinSnapshotTo(player, "PlayerConnect"); } private void OnPlayerEnterSceneFirstCatchUp(IServerPlayer player) { if (_sceneCatchUpSent.Add(player.Id)) { PushJoinSnapshotTo(player, "first scene catch-up"); } } private void PushJoinSnapshotTo(IServerPlayer player, string reason) { ServerCosmeticsPacketSender.SendUsernameRulesTo(player.Id, _customUsernameColorsOverrideTeamColors); ServerCosmeticsPacketSender.SendAllOtherPlayersCloaksTo(player.Id, _tracker); ServerCosmeticsPacketSender.SendAllOtherPlayersUsernameTintsTo(player.Id, _tracker); Log.Info($"Cosmetics snapshot ({reason}) → player {player.Id} (rules + others' cloak/username)."); } private void OnPlayerDisconnect(IServerPlayer player) { _tracker.RemovePlayer(player.Id); _sceneCatchUpSent.Remove(player.Id); } private void OnCloakColorUpdate(ushort senderId, CloakColorPacket data) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) CloakNetAppearance appearance = default(CloakNetAppearance); ((CloakNetAppearance)(ref appearance))..ctor(data.Color, data.TextureSaturationCenti); _tracker.SetCloak(senderId, appearance); if (_api != null) { ServerCosmeticsPacketSender.BroadcastCloakToOthers(senderId, appearance, _api.ServerManager.Players); } } private void OnUsernameColorUpdate(ushort senderId, UsernameColorPacket data) { //IL_000e: 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) _tracker.SetUsernameTint(senderId, data.HasCustomUsernameTint, data.Color); if (_api != null) { ServerCosmeticsPacketSender.BroadcastUsernameTintToOthers(senderId, data.Color, data.HasCustomUsernameTint, _api.ServerManager.Players); } } } internal static class ServerCosmeticsPacketSender { private static IServerAddonNetworkSender<PacketId>? _sender; internal static void Init(IServerAddonNetworkSender<PacketId> sender) { _sender = sender; } internal static void SendUsernameRulesTo(ushort recipientId, bool customUsernameColorsOverrideTeamColors) { if (_sender != null) { _sender.SendSingleData(PacketId.ServerUsernameColorRules, (IPacketData)(object)new ServerUsernameColorRulesPacket { CustomUsernameColorsOverrideTeamColors = customUsernameColorsOverrideTeamColors }, recipientId); } } internal static void SendAllOtherPlayersCloaksTo(ushort recipientId, PlayerCosmeticsTracker tracker) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (_sender == null) { return; } foreach (KeyValuePair<ushort, CloakNetAppearance> cloak in tracker.Cloaks) { if (cloak.Key != recipientId) { IServerAddonNetworkSender<PacketId>? sender = _sender; CloakColorPacket obj = new CloakColorPacket { PlayerId = cloak.Key }; CloakNetAppearance value = cloak.Value; obj.Color = ((CloakNetAppearance)(ref value)).Color; value = cloak.Value; obj.TextureSaturationCenti = ((CloakNetAppearance)(ref value)).TextureSaturationCenti; sender.SendCollectionData<CloakColorPacket>(PacketId.CloakColorUpdate, obj, recipientId); } } } internal static void SendAllOtherPlayersUsernameTintsTo(ushort recipientId, PlayerCosmeticsTracker tracker) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (_sender == null) { return; } foreach (KeyValuePair<ushort, CloakColor> usernameTint in tracker.UsernameTints) { if (usernameTint.Key != recipientId) { _sender.SendCollectionData<UsernameColorPacket>(PacketId.UsernameColorUpdate, new UsernameColorPacket { PlayerId = usernameTint.Key, Color = usernameTint.Value, HasCustomUsernameTint = true }, recipientId); } } } internal static void BroadcastCloakToOthers(ushort senderId, CloakNetAppearance appearance, IReadOnlyCollection<IServerPlayer> players) { //IL_0036: 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) if (_sender == null) { return; } foreach (IServerPlayer player in players) { if (player.Id != senderId) { _sender.SendCollectionData<CloakColorPacket>(PacketId.CloakColorUpdate, new CloakColorPacket { PlayerId = senderId, Color = ((CloakNetAppearance)(ref appearance)).Color, TextureSaturationCenti = ((CloakNetAppearance)(ref appearance)).TextureSaturationCenti }, player.Id); } } } internal static void BroadcastUsernameTintToOthers(ushort senderId, CloakColor color, bool hasCustomUsernameTint, IReadOnlyCollection<IServerPlayer> players) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (_sender == null) { return; } foreach (IServerPlayer player in players) { if (player.Id != senderId) { _sender.SendCollectionData<UsernameColorPacket>(PacketId.UsernameColorUpdate, new UsernameColorPacket { PlayerId = senderId, Color = color, HasCustomUsernameTint = hasCustomUsernameTint }, player.Id); } } } } internal static class ServerUsernameRulesStore { private const string FileName = "HornetCloakColor.server.json"; internal static bool LoadCustomUsernameColorsOverrideTeamColors() { try { string directoryName = Path.GetDirectoryName(typeof(ServerAddon).Assembly.Location); if (string.IsNullOrEmpty(directoryName)) { return false; } string path = Path.Combine(directoryName, "HornetCloakColor.server.json"); if (!File.Exists(path)) { return false; } Match match = Regex.Match(File.ReadAllText(path), "\"customUsernameColorsOverrideTeamColors\"\\s*:\\s*(true|false)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); return match.Success && string.Equals(match.Groups[1].Value, "true", StringComparison.OrdinalIgnoreCase); } catch { return false; } } } } namespace HornetCloakColor.Client { internal class ClientAddon : ClientAddon { private const string UsernameObjectName = "Username"; private readonly Dictionary<ushort, CloakNetAppearance> _playerCloakAppearances = new Dictionary<ushort, CloakNetAppearance>(); private readonly Dictionary<ushort, CloakColor> _playerUsernameColors = new Dictionary<ushort, CloakColor>(); private IClientApi? _api; private CloakNetAppearance _localCloakAppearance = CloakNetAppearance.Default; private CloakColor _localUsernameColor = CloakColor.Default; private bool _localUsernameHasCustomTint; private bool _serverCustomUsernameOverridesTeam; protected override string Name => "HornetCloakColor"; protected override string Version => "1.15.0"; public override uint ApiVersion => 1u; public override bool NeedsNetwork => true; internal static ClientAddon? Instance { get; private set; } public override void Initialize(IClientApi clientApi) { Instance = this; _api = clientApi; IClientAddonNetworkSender<PacketId> networkSender = clientApi.NetClient.GetNetworkSender<PacketId>((ClientAddon)(object)this); ClientCosmeticsPacketSender.Init(clientApi, networkSender); IClientAddonNetworkReceiver<PacketId> networkReceiver = clientApi.NetClient.GetNetworkReceiver<PacketId>((ClientAddon)(object)this, (Func<PacketId, IPacketData>)ClientAddonReceivePacketFactory.Instantiate); networkReceiver.RegisterPacketHandler<CloakColorPacket>(PacketId.CloakColorUpdate, (GenericClientPacketHandler<CloakColorPacket>)OnCloakColorUpdate); networkReceiver.RegisterPacketHandler<UsernameColorPacket>(PacketId.UsernameColorUpdate, (GenericClientPacketHandler<UsernameColorPacket>)OnUsernameColorUpdate); networkReceiver.RegisterPacketHandler<ServerUsernameColorRulesPacket>(PacketId.ServerUsernameColorRules, (GenericClientPacketHandler<ServerUsernameColorRulesPacket>)OnServerUsernameColorRules); clientApi.ClientManager.ConnectEvent += OnConnected; clientApi.ClientManager.DisconnectEvent += OnDisconnected; clientApi.ClientManager.PlayerEnterSceneEvent += OnPlayerEnterScene; clientApi.ClientManager.PlayerDisconnectEvent += OnPlayerDisconnect; PushUsernameDelegates(); Log.Info("Client addon initialized."); } private void PushUsernameDelegates() { UsernameNetworkDelegates.TryResolveUsernameTransform = TryResolveUsernameTransform; UsernameNetworkDelegates.GetRemoteUsernameTintOrNull = GetRemoteUsernameTintOrNull; UsernameNetworkDelegates.GetTeamsEnabled = () => _api.ClientManager.ServerSettings.TeamsEnabled; UsernameNetworkDelegates.GetServerCustomUsernameOverridesTeam = () => _serverCustomUsernameOverridesTeam; UsernameNetworkDelegates.GetLocalPlayerTeamOrdinal = () => (int)_api.ClientManager.PlayerManager.LocalPlayerTeam; } public void SetLocalCloakAppearance(CloakNetAppearance appearance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) _localCloakAppearance = appearance; ClientCosmeticsPacketSender.TrySendCloakUpdate(_localCloakAppearance); } public void SetLocalUsernameTint(bool hasCustomUsernameTint, CloakColor color) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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) _localUsernameHasCustomTint = hasCustomUsernameTint; _localUsernameColor = color; ClientCosmeticsPacketSender.TrySendUsernameUpdate(_localUsernameHasCustomTint, _localUsernameColor); } internal CloakColor GetRemoteMapColorOrDefault(ushort playerId) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!_playerCloakAppearances.TryGetValue(playerId, out var value)) { return CloakColor.Default; } return ((CloakNetAppearance)(ref value)).Color; } internal CloakColor? GetRemoteUsernameTintOrNull(ushort playerId) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!_playerUsernameColors.TryGetValue(playerId, out var value)) { return null; } return value; } private UsernameResolveResult TryResolveUsernameTransform(Transform t) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)t == (Object)null) { return default(UsernameResolveResult); } if ((Object)(object)HeroController.instance != (Object)null && t.IsChildOf(HeroController.instance.transform)) { if (TryGetLocalNetworkPlayerId(out var id)) { return new UsernameResolveResult(true, true, id); } return new UsernameResolveResult(true, true, (ushort)0); } if (TryResolveRemoteByPlayerContainerAncestor(t, out var playerId)) { return new UsernameResolveResult(true, false, playerId); } if (_api == null) { return default(UsernameResolveResult); } foreach (IClientPlayer player in _api.ClientManager.Players) { if (!((Object)(object)player.PlayerContainer == (Object)null) && t.IsChildOf(player.PlayerContainer.transform)) { return new UsernameResolveResult(true, false, player.Id); } } return default(UsernameResolveResult); } private static bool TryResolveRemoteByPlayerContainerAncestor(Transform t, out ushort playerId) { playerId = 0; Transform val = t; while ((Object)(object)val != (Object)null) { string name = ((Object)val).name; if (name.StartsWith("Player Container ", StringComparison.Ordinal)) { return ushort.TryParse(name.Substring("Player Container ".Length), NumberStyles.Integer, CultureInfo.InvariantCulture, out playerId); } val = val.parent; } return false; } private bool TryGetLocalNetworkPlayerId(out ushort id) { id = 0; if (_api == null || (Object)(object)HeroController.instance == (Object)null) { return false; } foreach (IClientPlayer player in _api.ClientManager.Players) { if ((Object)(object)player.PlayerObject != (Object)null && player.PlayerObject == ((Component)HeroController.instance).gameObject) { id = player.Id; return true; } } return false; } internal void ForceResendLocalColorsToServer() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) ClientCosmeticsPacketSender.TrySendStoredLocal(_localCloakAppearance, _localUsernameHasCustomTint, _localUsernameColor); } private void OnConnected() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) PushUsernameDelegates(); ClientCosmeticsPacketSender.TrySendStoredLocal(_localCloakAppearance, _localUsernameHasCustomTint, _localUsernameColor); SSMPBridge.SchedulePostConnectColorResend(); } private void OnDisconnected() { UsernameNetworkDelegates.Clear(); _playerUsernameColors.Clear(); _serverCustomUsernameOverridesTeam = false; } private void OnPlayerEnterScene(IClientPlayer player) { //IL_002b: 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) if (_playerCloakAppearances.TryGetValue(player.Id, out var value) && (Object)(object)player.PlayerObject != (Object)null) { CloakColorApplier.Apply(player.PlayerObject, ((CloakNetAppearance)(ref value)).Color, (float?)((CloakNetAppearance)(ref value)).TextureSaturationMultiplier); if (!IsLocalHeroPlayer(player)) { MpRemoteCloakReapply.Schedule(player.PlayerObject, value); } } if (!IsLocalHeroPlayer(player)) { RefreshRemoteUsernameVisual(player.Id); } } private static bool IsLocalHeroPlayer(IClientPlayer player) { HeroController instance = HeroController.instance; if ((Object)(object)instance != (Object)null && (Object)(object)player.PlayerObject != (Object)null) { return player.PlayerObject == ((Component)instance).gameObject; } return false; } private void OnPlayerDisconnect(IClientPlayer player) { _playerCloakAppearances.Remove(player.Id); _playerUsernameColors.Remove(player.Id); } private void OnCloakColorUpdate(CloakColorPacket data) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) CloakNetAppearance val = default(CloakNetAppearance); ((CloakNetAppearance)(ref val))..ctor(data.Color, data.TextureSaturationCenti); _playerCloakAppearances[data.PlayerId] = val; PlayerMapMaskTintRegistry.SetColor(data.PlayerId, ((CloakNetAppearance)(ref val)).Color); IClientApi? api = _api; IClientPlayer val2 = ((api != null) ? api.ClientManager.GetPlayer(data.PlayerId) : null); if ((Object)(object)((val2 != null) ? val2.PlayerObject : null) != (Object)null) { CloakColorApplier.Apply(val2.PlayerObject, ((CloakNetAppearance)(ref val)).Color, (float?)((CloakNetAppearance)(ref val)).TextureSaturationMultiplier); MpRemoteCloakReapply.Schedule(val2.PlayerObject, val); } } private void OnServerUsernameColorRules(ServerUsernameColorRulesPacket data) { _serverCustomUsernameOverridesTeam = data.CustomUsernameColorsOverrideTeamColors; } private void OnUsernameColorUpdate(UsernameColorPacket data) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (!data.HasCustomUsernameTint) { _playerUsernameColors.Remove(data.PlayerId); } else { _playerUsernameColors[data.PlayerId] = data.Color; } RefreshRemoteUsernameVisual(data.PlayerId); } private void RefreshRemoteUsernameVisual(ushort playerId) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected I4, but got Unknown IClientPlayer val = default(IClientPlayer); if (_api == null || !_api.ClientManager.TryGetPlayer(playerId, ref val) || val == null) { return; } GameObject playerContainer = val.PlayerContainer; if ((Object)(object)playerContainer == (Object)null) { return; } GameObject val2 = FindDeepChildByName(playerContainer, "Username"); if ((Object)(object)val2 == (Object)null) { return; } Component val3 = UsernameTmpCompat.FindOnGameObject(val2); if (!((Object)(object)val3 == (Object)null)) { SsmpUsernameVanillaColors.ApplyTeamColor(val3, val.Team); if (_playerUsernameColors.ContainsKey(playerId)) { UsernameTintCoordinator.ApplyRemoteUsernameAfterSync(val3, playerId, (int)val.Team); } } } private static GameObject? FindDeepChildByName(GameObject root, string name) { Transform[] componentsInChildren = root.GetComponentsInChildren<Transform>(true); foreach (Transform val in componentsInChildren) { if (((Object)val).name == name) { return ((Component)val).gameObject; } } return null; } } internal static class ClientCosmeticsPacketSender { private static IClientApi? _api; private static IClientAddonNetworkSender<PacketId>? _sender; internal static void Init(IClientApi api, IClientAddonNetworkSender<PacketId> sender) { _api = api; _sender = sender; } internal static void TrySendCloakUpdate(CloakNetAppearance appearance) { //IL_0036: 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) if (_api == null || _sender == null || !_api.NetClient.IsConnected) { return; } try { _sender.SendSingleData(PacketId.CloakColorUpdate, (IPacketData)(object)new CloakColorPacket { PlayerId = 0, Color = ((CloakNetAppearance)(ref appearance)).Color, TextureSaturationCenti = ((CloakNetAppearance)(ref appearance)).TextureSaturationCenti }); } catch (Exception ex) { Log.Warn("HornetCloakColor: failed to send cloak color to server (" + ex.GetType().Name + ": " + ex.Message + ")."); } } internal static void TrySendUsernameUpdate(bool hasCustomUsernameTint, CloakColor color) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (_api == null || _sender == null || !_api.NetClient.IsConnected) { return; } try { _sender.SendSingleData(PacketId.UsernameColorUpdate, (IPacketData)(object)new UsernameColorPacket { PlayerId = 0, Color = color, HasCustomUsernameTint = hasCustomUsernameTint }); } catch (Exception ex) { Log.Warn("HornetCloakColor: failed to send username tint to server (" + ex.GetType().Name + ": " + ex.Message + ")."); } } internal static void TrySendStoredLocal(CloakNetAppearance cloak, bool hasCustomUsernameTint, CloakColor usernameColor) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) TrySendCloakUpdate(cloak); TrySendUsernameUpdate(hasCustomUsernameTint, usernameColor); } } internal static class SsmpUsernameVanillaColors { internal static void ApplyTeamColor(Component? textMeshObject, Team team) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected I4, but got Unknown //IL_0081: 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_007f: Unknown result type (might be due to invalid IL or missing references) Color white = default(Color); switch (team - 1) { case 0: ((Color)(ref white))..ctor(0f, 0.5882353f, 0f); break; case 1: ((Color)(ref white))..ctor(40f / 51f, 0.5882353f, 0f); break; case 2: ((Color)(ref white))..ctor(50f / 51f, 10f / 51f, 10f / 51f); break; case 3: ((Color)(ref white))..ctor(10f / 51f, 0.5882353f, 40f / 51f); break; default: white = Color.white; break; } UsernameTmpCompat.SetColor(textMeshObject, white); } } }