using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using BoneLib; using BoneLib.BoneMenu; using LabFusion.Entities; using LabFusion.Network; using LabFusion.Network.Serialization; using LabFusion.Player; using LabFusion.SDK.Modules; using LabFusion.UI; using LabFusion.Utilities; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using RealTimeHeadChat; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(/*Could not decode attribute arguments.*/)] [assembly: MelonInfo(typeof(RealTimeHeadChatMod), "Real-Time Head Chat", "1.0.0", "Local Utility", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : System.Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(/*Could not decode attribute arguments.*/)] internal sealed class RefSafetyRulesAttribute : System.Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RealTimeHeadChat { public sealed class RealTimeHeadChatMod : MelonMod { private sealed class Bubble { public NetworkPlayer Player; public RigNameTag Element; public float LastUpdate; } [CompilerGenerated] private static class <>O { public static Action <0>__CreateMenu; public static ServerEvent <1>__ClearAll; public static PlayerUpdate <2>__OnPlayerLeft; public static Action <3>__ClearLocalText; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Action <>9__16_0; public static Action <>9__16_1; internal void b__16_0(bool value) { _enabled.Value = value; MelonPreferences.Save(); if (!value) { ClearLocalText(); } } internal void b__16_1() { Menu.DisplayDialog("REAL-TIME HEAD CHAT", "It is enabled by default. Just type on your physical keyboard during Fusion play. Backspace edits the text; Enter clears it. It also clears after five seconds without typing. Every player who should see it needs this mod.", (Texture2D)null, (Action)null, (Action)null); } } internal const int MaxCharacters = 160; internal const int MaxUtf8Bytes = 640; private const float SendInterval = 0.1f; private const float RemoteTimeout = 12f; private const float LocalIdleTimeout = 5f; private static readonly Dictionary Bubbles = new Dictionary(); private static MelonPreferences_Entry _enabled; private static string _pendingText = string.Empty; private static string _lastSentText = string.Empty; private static float _nextSendTime; private static float _lastKeyTime; private static bool _menuCreated; public override void OnInitializeMelon() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown MelonPreferences_Category val = MelonPreferences.CreateCategory("RealTimeHeadChat"); _enabled = val.CreateEntry("Enabled", true, "Enabled", "When enabled, physical keyboard typing is shown above your head in Fusion.", false, false, (ValueValidator)null, (string)null); ModuleManager.RegisterModule(); object obj = <>O.<0>__CreateMenu; if (obj == null) { Action val2 = CreateMenu; <>O.<0>__CreateMenu = val2; obj = (object)val2; } Hooking.OnUIRigCreated += (Action)obj; object obj2 = <>O.<1>__ClearAll; if (obj2 == null) { ServerEvent val3 = ClearAll; <>O.<1>__ClearAll = val3; obj2 = (object)val3; } MultiplayerHooking.OnDisconnected += (ServerEvent)obj2; object obj3 = <>O.<2>__OnPlayerLeft; if (obj3 == null) { PlayerUpdate val4 = OnPlayerLeft; <>O.<2>__OnPlayerLeft = val4; obj3 = (object)val4; } MultiplayerHooking.OnPlayerLeft += (PlayerUpdate)obj3; } public override void OnDeinitializeMelon() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown object obj = <>O.<0>__CreateMenu; if (obj == null) { Action val = CreateMenu; <>O.<0>__CreateMenu = val; obj = (object)val; } Hooking.OnUIRigCreated -= (Action)obj; object obj2 = <>O.<1>__ClearAll; if (obj2 == null) { ServerEvent val2 = ClearAll; <>O.<1>__ClearAll = val2; obj2 = (object)val2; } MultiplayerHooking.OnDisconnected -= (ServerEvent)obj2; object obj3 = <>O.<2>__OnPlayerLeft; if (obj3 == null) { PlayerUpdate val3 = OnPlayerLeft; <>O.<2>__OnPlayerLeft = val3; obj3 = (object)val3; } MultiplayerHooking.OnPlayerLeft -= (PlayerUpdate)obj3; ClearAll(); } public override void OnUpdate() { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) CaptureKeyboardInput(); if (_pendingText.Length != 0 && Time.unscaledTime - _lastKeyTime >= 5f) { ClearLocalText(); } if (Time.unscaledTime >= _nextSendTime && _pendingText != _lastSentText) { Publish(_pendingText); _nextSendTime = Time.unscaledTime + 0.1f; } if (Bubbles.Count == 0) { return; } List val = null; Enumerator enumerator = Bubbles.GetEnumerator(); try { while (enumerator.MoveNext()) { KeyValuePair current = enumerator.Current; if (Time.unscaledTime - current.Value.LastUpdate > 12f) { if (val == null) { val = new List(); } val.Add(current.Key); } } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } if (val == null) { return; } Enumerator enumerator2 = val.GetEnumerator(); try { while (enumerator2.MoveNext()) { byte current2 = enumerator2.Current; RemoveBubble(current2); } } finally { ((System.IDisposable)enumerator2/*cast due to .constrained prefix*/).Dispose(); } } private static void CreateMenu() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown if (_menuCreated) { return; } Page val = Page.Root.CreatePage("Real-Time Head Chat", Color.cyan, 0, true); val.CreateBool("Enabled", Color.white, _enabled.Value, (Action)delegate(bool value) { _enabled.Value = value; MelonPreferences.Save(); if (!value) { ClearLocalText(); } }); Color red = Color.red; object obj = <>O.<3>__ClearLocalText; if (obj == null) { Action val2 = ClearLocalText; <>O.<3>__ClearLocalText = val2; obj = (object)val2; } val.CreateFunction("Clear my text", red, (Action)obj); Color gray = Color.gray; object obj2 = <>c.<>9__16_1; if (obj2 == null) { Action val3 = delegate { Menu.DisplayDialog("REAL-TIME HEAD CHAT", "It is enabled by default. Just type on your physical keyboard during Fusion play. Backspace edits the text; Enter clears it. It also clears after five seconds without typing. Every player who should see it needs this mod.", (Texture2D)null, (Action)null, (Action)null); }; <>c.<>9__16_1 = val3; obj2 = (object)val3; } val.CreateFunction("How to use", gray, (Action)obj2); _menuCreated = true; } private static void CaptureKeyboardInput() { if (_enabled == null || !_enabled.Value || !NetworkInfo.HasServer) { return; } string inputString = Input.inputString; if (string.IsNullOrEmpty(inputString)) { return; } bool flag = false; string text = inputString; for (int i = 0; i < text.Length; i++) { char c = text[i]; switch (c) { case '\b': if (_pendingText.Length > 0) { _pendingText = _pendingText.Substring(0, _pendingText.Length - 1); flag = true; } break; default: if (c != '\r') { if (!char.IsControl(c) && _pendingText.Length < 160) { _pendingText += c; flag = true; } break; } goto case '\n'; case '\n': ClearLocalText(); return; } } if (flag) { _lastKeyTime = Time.unscaledTime; } } private static void ClearLocalText() { _pendingText = string.Empty; if (_lastSentText.Length != 0) { Publish(string.Empty); } } private static void Publish(string text) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) text = Normalize(text); _lastSentText = text; if (NetworkInfo.HasServer) { MessageRelay.RelayModule(new HeadChatData { Text = text }, CommonMessageRoutes.UnreliableToOtherClients); } } private static string Normalize(string text) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown if (string.IsNullOrEmpty(text)) { return string.Empty; } text = text.Replace('\r', ' ').Replace('\n', ' ').Replace('\t', ' '); if (text.Length > 160) { text = text.Substring(0, 160); } StringBuilder val = new StringBuilder(text.Length); string text2 = text; foreach (char c in text2) { if (!char.IsControl(c)) { val.Append(c); } } return ((object)val).ToString(); } internal static void Receive(byte sender, string text) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown text = Normalize(text); NetworkPlayer val = default(NetworkPlayer); if (text.Length == 0) { RemoveBubble(sender); } else if (NetworkPlayerManager.TryGetPlayer(sender, ref val) && !val.NetworkEntity.IsOwner) { Bubble bubble = default(Bubble); if (!Bubbles.TryGetValue(sender, ref bubble) || bubble.Player != val) { RemoveBubble(sender); RigNameTag val2 = new RigNameTag { CrownVisible = false, Color = new Color(0.88f, 0.97f, 1f, 1f), Visible = true, Username = text }; val.HeadUI.RegisterElement((IPopupLayoutElement)(object)val2); bubble = new Bubble { Player = val, Element = val2 }; Bubbles[sender] = bubble; } else { bubble.Element.Username = text; } bubble.LastUpdate = Time.unscaledTime; } } private static void OnPlayerLeft(PlayerID player) { if (player != null) { RemoveBubble(player.SmallID); } } private static void RemoveBubble(byte sender) { Bubble bubble = default(Bubble); if (Bubbles.TryGetValue(sender, ref bubble)) { if (bubble.Player != null && bubble.Player.HeadUI != null) { bubble.Player.HeadUI.UnregisterElement((IPopupLayoutElement)(object)bubble.Element); } Bubbles.Remove(sender); } } private static void ClearAll() { //IL_000c: 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) Enumerator enumerator = Bubbles.Values.GetEnumerator(); try { while (enumerator.MoveNext()) { Bubble current = enumerator.Current; if (current.Player != null && current.Player.HeadUI != null) { current.Player.HeadUI.UnregisterElement((IPopupLayoutElement)(object)current.Element); } } } finally { ((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose(); } Bubbles.Clear(); _pendingText = string.Empty; _lastSentText = string.Empty; } } public sealed class HeadChatModule : Module { public override string Name => "Real-Time Head Chat"; public override string Author => "Local Utility"; public override Version Version => new Version(1, 0, 0); public override ConsoleColor Color => (ConsoleColor)11; protected override void OnModuleRegistered() { ModuleMessageManager.RegisterHandler(); } } public sealed class HeadChatData : INetSerializable { public string Text = string.Empty; public void Serialize(INetSerializer serializer) { serializer.SerializeValue(ref Text); } } public sealed class HeadChatMessage : ModuleMessageHandler { protected override bool OnPreRelayMessage(ReceivedMessage received) { return ((ReceivedMessage)(ref received)).Sender.HasValue && ((ReceivedMessage)(ref received)).Bytes != null && ((ReceivedMessage)(ref received)).Bytes.Length >= 4 && ((ReceivedMessage)(ref received)).Bytes.Length <= 644; } protected override void OnHandleMessage(ReceivedMessage received) { if (((ReceivedMessage)(ref received)).Sender.HasValue && ((ReceivedMessage)(ref received)).Bytes != null && ((ReceivedMessage)(ref received)).Bytes.Length >= 4 && ((ReceivedMessage)(ref received)).Bytes.Length <= 644) { HeadChatData headChatData = ((ReceivedMessage)(ref received)).ReadData(); RealTimeHeadChatMod.Receive(((ReceivedMessage)(ref received)).Sender.Value, headChatData.Text); } } } }