using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using Mirror; using SmolMods.Core; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("smolMods.EyeState")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+a3231e664cf7d257a8601fb862a89ade69b41d83")] [assembly: AssemblyProduct("smolMods.EyeState")] [assembly: AssemblyTitle("smolMods.EyeState")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SmolMods.EyeState { internal static class EyeChannelPayload { private const byte Subtype = 2; private static readonly Dictionary HostMoods = new Dictionary(); internal static void Register() { ModChannel.Register((byte)2, (Action)OnServerFrame, (Action)OnClientFrame); ModChannel.PeerHello += OnPeerHello; ModChannel.PeerLeft += OnPeerLeft; ModChannel.SessionReset += OnSessionReset; } internal static void SendEyeState(int mood, ulong ourNetId, bool stillAnimated) { if (ModChannel.IsServer) { ModChannel.Broadcast((byte)2, Body(mood, ourNetId, stillAnimated), -1); } else { ModChannel.SendToHost((byte)2, Body(mood, ourNetId, stillAnimated)); } } private static bool FlagOf(byte[] body) { if (body.Length >= 10) { return (body[9] & 1) != 0; } return true; } private static void OnServerFrame(int connId, byte[] body) { if (body.Length >= 1) { int num = (sbyte)body[0]; bool flag = FlagOf(body); EyeStateSync.SetRemoteMood(ModChannel.EpicIdOf(connId), num, flag); ulong num2 = ModChannel.NetIdOf(connId); EyeStateSync.SetRemoteMoodByNetId(num2, num, flag); if (connId > 0) { HostMoods[connId] = (num2, num, flag); } ModChannel.Broadcast((byte)2, Body(num, num2, flag), connId); } } private static void OnClientFrame(byte[] body) { if (body.Length >= 9) { ulong num = 0uL; for (int i = 0; i < 8; i++) { num |= (ulong)body[1 + i] << 8 * i; } EyeStateSync.SetRemoteMoodByNetId(num, (sbyte)body[0], FlagOf(body)); } } private static void OnPeerHello(int connId) { if (connId < 0) { EyeStateFeature.MarkDirty(); return; } int localMood = EyeStateFeature.LocalMood; ulong num = NetIds.LocalNetId(); if (num != 0L) { ModChannel.SendToConnection(connId, (byte)2, Body(localMood, num, EyeStateFeature.LocalStillAnimated)); } foreach (KeyValuePair hostMood in HostMoods) { if (hostMood.Key != connId && hostMood.Value.Item1 != 0L) { ModChannel.SendToConnection(connId, (byte)2, Body(hostMood.Value.Item2, hostMood.Value.Item1, hostMood.Value.Item3)); } } } private static void OnPeerLeft(int connId) { if (HostMoods.TryGetValue(connId, out (ulong, int, bool) value)) { HostMoods.Remove(connId); if (value.Item1 != 0L) { ModChannel.Broadcast((byte)2, Body(-1, value.Item1, stillAnimated: true), -1); } } } private static void OnSessionReset() { HostMoods.Clear(); EyeStateFeature.MarkDirty(); EyeStateSync.ClearAll(); } private static byte[] Body(int mood, ulong netId, bool stillAnimated) { byte[] array = new byte[10] { (byte)(sbyte)mood, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; for (int i = 0; i < 8; i++) { array[1 + i] = (byte)(netId >> 8 * i); } array[9] = (stillAnimated ? ((byte)1) : ((byte)0)); return array; } } internal static class EyeMoods { internal const int NoOverride = -1; internal static readonly (int Value, string Label)[] Moods = new(int, string)[12] { (-1, "Default"), (0, "Normal"), (4, "Squint >.<"), (6, "Dead x_x"), (3, "Sleepy u.u"), (1, "Line -.-"), (2, "wobbly *.*"), (5, "Ring O.O"), (7, "Beady ·.·"), (8, "Square"), (9, "Thick Ring o.o"), (10, "Spiral @.@") }; internal static string LabelFor(int v) { (int, string)[] moods = Moods; for (int i = 0; i < moods.Length; i++) { (int, string) tuple = moods[i]; if (tuple.Item1 == v) { return tuple.Item2; } } return "Default"; } internal static string GlyphFor(int mood) { string result = "o"; string text; switch (mood) { case 0: text = "o"; break; case 1: text = "-"; break; case 2: text = "*"; break; case 3: text = "u"; break; case 4: text = "<"; break; case 5: text = "O"; break; case 6: text = "x"; break; case 7: text = "·"; break; case 8: text = "■"; result = "#"; break; case 9: text = "◎"; result = "O"; break; case 10: text = "@"; break; default: return ""; } try { TMP_FontAsset menuGlyphFont = ModSettingsApi.MenuGlyphFont; if ((Object)(object)menuGlyphFont != (Object)null && !menuGlyphFont.HasCharacter(text[0], false, false)) { return result; } } catch { } return text; } } [HarmonyPatch] internal static class EyeStateFeature { private static ModRowDef _row; private static ModRowDef _stillAnimatedRow; private static ModRowDef _shareRow; private static PlayerEyes _localEyes; private static float _nextSyncTick; private static int _moodMirror = -1; private static bool _stillMirror = true; private static bool _shareMirror; private static int _lastSentMood = int.MinValue; private static bool _lastSentStill = true; private static bool _loggedMoodReadFailure; internal static int LocalMood => _moodMirror; internal static bool LocalStillAnimated => _stillMirror; internal static void Register() { int[] array = new int[EyeMoods.Moods.Length]; for (int i = 0; i < EyeMoods.Moods.Length; i++) { array[i] = EyeMoods.Moods[i].Value; } _row = ModSettingsApi.AddArrayRow(200, "Eye state", "settings_mod_eyeState", -1, array, (Func)EyeMoods.LabelFor, (Action)delegate(int v) { _moodMirror = v; }, (Func)null); _stillAnimatedRow = ModSettingsApi.AddArrayRow(200, "Still animated", "settings_mod_eyesStillAnimated", 1, new int[2] { 0, 1 }, (Func)((int v) => (v != 1) ? "Off" : "On"), (Action)delegate(int v) { _stillMirror = v == 1; MarkDirty(); }, (Func)null); _shareRow = ModSettingsApi.AddArrayRow(200, "Share eye state", "settings_mod_shareEyeState", 1, new int[2] { 0, 1 }, (Func)((int v) => (v != 1) ? "Off" : "On"), (Action)delegate(int v) { _shareMirror = v == 1; ModChannel.SetEnabled(v == 1); if (v != 1) { EyeStateSync.ClearAll(); } }, (Func)null); EyeChannelPayload.Register(); ModSettingsApi.AddTicker((Action)TickSync); ModSettingsApi.RequestCharacterPreview(); ModSettingsApi.SetPreviewGlyphProvider((Func)(() => EyeMoods.GlyphFor(LocalMood))); ModSettingsApi.AddNoteLine(200, (Func)(() => "Vanilla users do not sync Eye states.")); } internal static void TickSync() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown try { if (!_shareMirror) { return; } ModChannel.Tick(); if (Time.unscaledTime < _nextSyncTick) { return; } _nextSyncTick = Time.unscaledTime + 0.5f; int localMood = LocalMood; bool localStillAnimated = LocalStillAnimated; if (localMood != _lastSentMood || localStillAnimated != _lastSentStill) { PlayerCharacter val = LocalPlayerCharacter(); if (!((Object)(object)val == (Object)null)) { _lastSentMood = localMood; _lastSentStill = localStillAnimated; EyeChannelPayload.SendEyeState(localMood, ((NetworkBehaviour)val).netId, localStillAnimated); } } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(22, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Eye sync tick failed: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); } logger.LogWarning(val2); } } } internal static void MarkDirty() { _lastSentMood = int.MinValue; _lastSentStill = true; } private static PlayerCharacter LocalPlayerCharacter() { try { List allPlayerCharacters = PlayerCharacter.allPlayerCharacters; if (allPlayerCharacters == null) { return null; } for (int i = 0; i < allPlayerCharacters.Count; i++) { if ((Object)(object)allPlayerCharacters[i] != (Object)null && ((NetworkBehaviour)allPlayerCharacters[i]).isLocalPlayer) { return allPlayerCharacters[i]; } } } catch { } return null; } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerCharacter), "Start")] private static void PlayerStart_Postfix(PlayerCharacter __instance) { try { if (((NetworkBehaviour)__instance).isLocalPlayer && __instance.playerEyes != null) { _localEyes = __instance.playerEyes; } } catch { } } private static bool GameWantsSpecialEyes(PlayerEyes eyes) { //IL_0001: 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_000f: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown try { return (int)eyes.moodLeft != 0 || (int)eyes.moodRight > 0; } catch (Exception ex) { if (!_loggedMoodReadFailure) { _loggedMoodReadFailure = true; ManualLogSource logger = Plugin.Logger; if (logger != null) { bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(64, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Reading the game's eye mood failed ("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ex.Message); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("); 'Still animated' inactive"); } logger.LogWarning(val); } } return false; } } [HarmonyPostfix] [HarmonyPatch(typeof(PlayerEyes), "Update")] private static void EyesUpdate_Postfix(PlayerEyes __instance) { try { int num; bool flag; if (_localEyes == null || !(((Il2CppObjectBase)__instance).Pointer == ((Il2CppObjectBase)_localEyes).Pointer)) { (num, flag) = EyeStateSync.StateFor(__instance); } else { num = _moodMirror; flag = _stillMirror; } if (num >= 0 && (!flag || !GameWantsSpecialEyes(__instance))) { __instance.SetEyeMood((PlayerEyeMood)num); } } catch { } } } internal static class EyeStateSync { private static readonly Dictionary RemoteMoods = new Dictionary(); private static readonly Dictionary EyesOwner = new Dictionary(); private static readonly Dictionary RemoteMoodsByNetId = new Dictionary(); internal static void SetRemoteMoodByNetId(ulong netId, int mood, bool stillAnimated) { if (netId != 0L) { if (mood < 0) { RemoteMoodsByNetId.Remove(netId); } else { RemoteMoodsByNetId[netId] = (mood, stillAnimated); } } } internal static void SetRemoteMood(string epicUserId, int mood, bool stillAnimated) { if (!string.IsNullOrEmpty(epicUserId)) { if (mood < 0) { RemoteMoods.Remove(epicUserId); } else { RemoteMoods[epicUserId] = (mood, stillAnimated); } } } internal static void ClearAll() { RemoteMoods.Clear(); RemoteMoodsByNetId.Clear(); } internal static (int mood, bool still) StateFor(PlayerEyes eyes) { if (eyes == null || (RemoteMoods.Count == 0 && RemoteMoodsByNetId.Count == 0)) { return (mood: -1, still: true); } PlayerCharacter val = OwnerOf(eyes); if ((Object)(object)val == (Object)null) { return (mood: -1, still: true); } try { if (RemoteMoodsByNetId.Count > 0 && ((NetworkBehaviour)val).netId != 0 && RemoteMoodsByNetId.TryGetValue(((NetworkBehaviour)val).netId, out (int, bool) value)) { return value; } } catch { } if ((Object)(object)val.playerNetworking == (Object)null) { return (mood: -1, still: true); } string epicUserId = val.playerNetworking.epicUserId; if (string.IsNullOrEmpty(epicUserId)) { return (mood: -1, still: true); } if (!RemoteMoods.TryGetValue(epicUserId, out (int, bool) value2)) { return (mood: -1, still: true); } return value2; } private static PlayerCharacter OwnerOf(PlayerEyes eyes) { IntPtr pointer = ((Il2CppObjectBase)eyes).Pointer; if (EyesOwner.TryGetValue(pointer, out var value)) { if ((Object)(object)value != (Object)null) { return value; } EyesOwner.Remove(pointer); } try { List allPlayerCharacters = PlayerCharacter.allPlayerCharacters; if (allPlayerCharacters == null) { return null; } for (int i = 0; i < allPlayerCharacters.Count; i++) { PlayerCharacter val = allPlayerCharacters[i]; if (!((Object)(object)val == (Object)null) && val.playerEyes != null && ((Il2CppObjectBase)val.playerEyes).Pointer == pointer) { EyesOwner[pointer] = val; return val; } } } catch { } return null; } } [BepInPlugin("com.smolmods.eyestate", "smolMods.ShareChangedEyeState", "1.0.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BasePlugin { public const string PluginGuid = "com.smolmods.eyestate"; public const string PluginName = "smolMods.ShareChangedEyeState"; public const string PluginVersion = "1.0.2"; internal static ManualLogSource Logger; public override void Load() { //IL_0015: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown Logger = ((BasePlugin)this).Log; EyeStateFeature.Register(); Harmony val = new Harmony("com.smolmods.eyestate"); val.PatchAll(typeof(Plugin).Assembly); int num = 0; foreach (MethodBase patchedMethod in val.GetPatchedMethods()) { _ = patchedMethod; num++; } ManualLogSource log = ((BasePlugin)this).Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(27, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted("smolMods.ShareChangedEyeState"); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted("1.0.2"); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" loaded, "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" methods patched."); } log.LogInfo(val2); } } }