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<bool> <>9__16_0;
public static Action <>9__16_1;
internal void <CreateMenu>b__16_0(bool value)
{
_enabled.Value = value;
MelonPreferences.Save();
if (!value)
{
ClearLocalText();
}
}
internal void <CreateMenu>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<byte, Bubble> Bubbles = new Dictionary<byte, Bubble>();
private static MelonPreferences_Entry<bool> _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<bool>("Enabled", true, "Enabled", "When enabled, physical keyboard typing is shown above your head in Fusion.", false, false, (ValueValidator)null, (string)null);
ModuleManager.RegisterModule<HeadChatModule>();
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<byte> val = null;
Enumerator<byte, Bubble> enumerator = Bubbles.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
KeyValuePair<byte, Bubble> current = enumerator.Current;
if (Time.unscaledTime - current.Value.LastUpdate > 12f)
{
if (val == null)
{
val = new List<byte>();
}
val.Add(current.Key);
}
}
}
finally
{
((System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
}
if (val == null)
{
return;
}
Enumerator<byte> 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<bool>)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<HeadChatMessage, HeadChatData>(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<byte, Bubble> 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<HeadChatMessage>();
}
}
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<HeadChatData>();
RealTimeHeadChatMod.Receive(((ReceivedMessage)(ref received)).Sender.Value, headChatData.Text);
}
}
}
}