Decompiled source of Bonegle v1.1.0
Mods/Bonegle.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers.Binary; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Loader; using System.Runtime.Versioning; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using BoneLib; using BoneLib.BoneMenu; using Bonegle; using HarmonyLib; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSLZ.Marrow; using Il2CppSLZ.Marrow.Input; using Il2CppSLZ.Marrow.Utilities; using Il2CppSLZ.VRMK; using Il2CppSystem; using Il2CppTMPro; using LabFusion.Marrow.Proxies; using LabFusion.Menu; using LabFusion.Network; using LabFusion.Player; using LabFusion.Preferences.Client; using LabFusion.SDK.Metadata; using LabFusion.UI.Popups; using LabFusion.Voice; using LabFusion.Voice.Unity; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(BonegleMod), "Bonegle", "1.1.0", "MarrowByte", null)] [assembly: MelonGame("Stress Level Zero", "BONELAB")] [assembly: MelonAdditionalDependencies(new string[] { "BoneLib", "LabFusion" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("MarrowByte")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("Bonegle")] [assembly: AssemblyTitle("Bonegle")] [assembly: AssemblyVersion("1.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 Bonegle { public sealed class BonegleMod : MelonMod { private Preferences _prefs; private CallSession _call; private CallPanel _panel; private Contacts _contacts; private SocialMenu _social; private MicrophoneMenu _micMenu; private VideoMenu _videoMenu; private readonly CallNetwork _network = new CallNetwork(); private readonly CallMicrophone _microphone = new CallMicrophone(new MicrophoneCapture(), new FusionMicrophone()); private readonly VoicePlayer _voice = new VoicePlayer(); private readonly VideoFeed _video = new VideoFeed(); private bool _menuBuilt; private bool _suspended = true; private Guid _mediaCall; private float _nextMenu; private float _nextWarning; private float _sceneCooldown; private float _lastVideo; private FunctionElement? _networkName; public override void OnInitializeMelon() { _prefs = new Preferences(); _call = new CallSession(_network); _contacts = new Contacts(_network, new ContactStore(Path.Combine(MelonEnvironment.UserDataDirectory, "CrossTalk"))); _network.Initialize(_call, _contacts, ((MelonBase)this).HarmonyInstance); _social = new SocialMenu(_network, _call, _contacts, ShowPanel); _panel = new CallPanel(_call, _prefs, _video, _network, _contacts, _microphone, MediaChanged); _micMenu = new MicrophoneMenu(_microphone, _prefs, _call, _network.Voice); _videoMenu = new VideoMenu(_video, _prefs, _network); ((MelonBase)this).LoggerInstance.Msg("Bonegle 1.1.0. Open the wrist panel or press F6 to call."); } public override void OnUpdate() { if (_call == null) { return; } try { EnsureMenu(); _network.Tick(Time.unscaledTime); string text = "Call network: " + _network.NetworkName; if (_networkName != null && ((Element)_networkName).ElementName != text) { ((Element)_networkName).ElementName = text; } _network.Voice.UseCall(_call.Call); _social.Tick(Time.unscaledTime); _micMenu.Tick(Time.unscaledTime); _videoMenu.Tick(Time.unscaledTime); if (_mediaCall != _call.Call) { StopMedia(); _mediaCall = _call.Call; } Transform head = Player.Head; if (!((Object)(object)head != (Object)null) || !((Object)(object)Player.RigManager != (Object)null) || !(Time.unscaledTime >= _sceneCooldown)) { Suspend(); return; } if (_suspended) { _suspended = false; _call.SendMediaState(); } if (Input.GetKeyDown((KeyCode)287)) { _panel.Toggle(head); } _panel.Tick(head); if (_call.State != CallState.Active) { StopMedia(); return; } if (_call.Mic) { _microphone.Tick(Time.unscaledTime, new MicSettings(_prefs.Device, _prefs.DeviceName, FusionMicrophone.DeviceName, _prefs.MicGain), delegate(byte[] bytes) { _network.SendMedia(2, _call.Call, bytes); }); } else { _microphone.Dispose(); } if (_call.Camera) { _video.Read((byte[] bytes) => _network.SendMedia(1, _call.Call, bytes), Time.unscaledTime); } else { _video.Pause(); } byte[] data; while (_network.TryAudio(out data)) { if (_call.Accepts(data)) { try { _network.Voice.Playback(_call.Call, _voice.Play(data, _prefs.Volume)); } catch (Exception ex) { _network.Voice.Playback(_call.Call, queued: false, ex.Message); _voice.Dispose(); } } } if (!_call.RemoteMic) { _voice.Stop(); } byte[] array = _network.TakeVideo(); if (array != null && _call.Accepts(array) && _video.Receive(array, Time.unscaledTime)) { _lastVideo = Time.unscaledTime; } if (!_call.RemoteCamera || Time.unscaledTime - _lastVideo > 3f) { _video.ClearRemote(); } } catch (Exception ex2) { Suspend(); if (Time.unscaledTime >= _nextWarning) { _nextWarning = Time.unscaledTime + 5f; ((MelonBase)this).LoggerInstance.Warning("Bonegle paused: " + ex2.GetType().Name + ": " + ex2.Message); _call.Status = "Call paused. Check the MelonLoader log."; } } } public override void OnLateUpdate() { if (_call == null || _suspended || _call.State != CallState.Active || !_call.Camera || (Object)(object)Player.Head == (Object)null) { return; } try { _video.Capture(Player.Head, Time.unscaledTime, _prefs.Selfie, _prefs.SelfieDistance, _prefs.Video); } catch (Exception ex) { _call.Camera = false; MediaChanged(); _call.Status = "Camera unavailable: " + ex.GetType().Name; } } private void Connect() { StopMedia(); _network.Connect(); } private void ShowPanel() { if ((Object)(object)Player.Head != (Object)null && !_panel.Visible) { _panel.Toggle(Player.Head); } } private void Disconnect() { _network.Disconnect(); StopMedia(); } private void MediaChanged() { if (!_call.Mic) { _microphone.Dispose(); } if (!_call.Camera) { _video.Pause(); } _network.ClearMedia(); _call.SendMediaState(); } private void StopMedia() { _microphone.Dispose(); _video.Pause(); _voice.Stop(); _video.ClearRemote(); _network.ClearMedia(); } private void Suspend() { if (!_suspended && _call.State == CallState.Active) { _network.Send(_call.PeerId, new Signal { Type = "media", Call = _call.Call.ToString(), Mic = false, Camera = false }); } _suspended = true; StopMedia(); _panel.Suspend(); } private void EnsureMenu() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) if (_menuBuilt || Time.unscaledTime < _nextMenu) { return; } _nextMenu = Time.unscaledTime + 2f; if (Page.Root == null) { return; } Color val = default(Color); ((Color)(ref val))..ctor(0.27f, 0.94f, 0.76f); Page val2 = Page.Root.CreatePage("Bonegle", val, 0, true); _networkName = val2.CreateFunction("Call network: Offline", Color.white, (Action)null); val2.CreateFunction("Open / Close Wrist Panel", val, (Action)delegate { if ((Object)(object)Player.Head != (Object)null) { _panel.Toggle(Player.Head); } }); _social.Build(val2); val2.CreateFunction("Refresh Friends", val, (Action)_network.RefreshFriends); val2.CreateFunction("Enable Calls", val, (Action)Connect); val2.CreateFunction("Disable Calls", Color.white, (Action)Disconnect); val2.CreateBool("Use Right Wrist", val, _prefs.RightWrist, (Action<bool>)delegate(bool value) { _prefs.RightWrist = value; }); val2.CreateFloat("Call Volume", val, _prefs.Volume, 0.1f, 0f, 1f, (Action<float>)delegate(float value) { _prefs.Volume = value; }); _micMenu.Build(val2); _videoMenu.Build(val2); val2.CreateFloat("Selfie Distance", val, _prefs.SelfieDistance, 0.1f, 0.4f, 2.5f, (Action<float>)delegate(float value) { _prefs.SelfieDistance = value; }); val2.CreateFunction("Mute / Unmute Call", val, (Action)delegate { _call.Mic = !_call.Mic; MediaChanged(); }); val2.CreateFunction("Camera On / Off", val, (Action)delegate { _call.Camera = !_call.Camera; MediaChanged(); }); val2.CreateFunction("End Call", Color.red, (Action)_call.HangUp); _menuBuilt = true; } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { if (_call != null) { _sceneCooldown = Time.unscaledTime + 0.5f; Suspend(); } } public override void OnDeinitializeMelon() { _social?.Dispose(); _network.Dispose(); _microphone.Dispose(); _voice.Dispose(); _video.Dispose(); _panel?.Dispose(); } } internal readonly record struct MicSettings(int Device, string Name, string AutomaticName, float Gain); internal interface IMicrophoneCapture : IDisposable { bool Running { get; } IReadOnlyList<MicDevice> GetDevices(); void Start(int device); void Poll(Action<byte[]> send); } internal interface ISharedMicrophone { bool Read(string device, float now, Action<byte[]> send); void Reset(); } internal sealed class CallMicrophone(IMicrophoneCapture windows, ISharedMicrophone fusion) : IDisposable { private float _retry; private float _started; private float _lastPacket; private float _lastSignal; private float _nextSourceCheck; private int _selection = int.MinValue; private string _savedName = ""; private string _fusionName = ""; private bool _shared; internal bool Running { get; private set; } internal float Level { get; private set; } internal string Source { get; private set; } = "Microphone idle"; internal string Error { get; private set; } = ""; internal int Bars => Math.Clamp((int)Math.Ceiling(Level * 20f), 0, 5); internal bool Silent(float now) { if (Running) { return now - Math.Max(_started, _lastSignal) > 8f; } return false; } internal void Tick(float now, MicSettings settings, Action<byte[]> send) { Level *= 0.88f; if (now >= _nextSourceCheck) { _nextSourceCheck = now + 1f; if (_selection != settings.Device || _savedName != settings.Name || (settings.Device < 0 && _fusionName != settings.AutomaticName)) { Dispose(); _selection = settings.Device; _savedName = settings.Name; _fusionName = settings.AutomaticName; } } if (now < _retry) { return; } try { if (_shared && ReadShared(now, Emit)) { Started(now); return; } if (!windows.Running) { MicDevice micDevice = MicDevice.Select(windows.GetDevices(), _selection, _savedName, _fusionName); try { windows.Start(micDevice.Id); } catch when (_selection < 0) { windows.Dispose(); if (ReadShared(now, Emit)) { Started(now); return; } throw; } fusion.Reset(); _shared = false; Source = "Windows: " + micDevice.Name; _lastPacket = now; } windows.Poll(Emit); if (now - _lastPacket > 3f) { throw new InvalidOperationException("Microphone stopped responding. Retrying..."); } Started(now); } catch (Exception ex) { Dispose(); Error = ex.Message; _retry = now + 3f; } void Emit(byte[] bytes) { float num = PcmAudio.ApplyGain(bytes, settings.Gain); Level = Math.Max(Level, num); _lastPacket = now; if (num >= 0.0015f) { _lastSignal = now; } send(bytes); } } private bool ReadShared(float now, Action<byte[]> send) { try { if (fusion.Read(_fusionName, now, send)) { _shared = true; Source = "Fusion: " + (string.IsNullOrEmpty(_fusionName) ? "default microphone" : _fusionName); return true; } } catch { } fusion.Reset(); _shared = false; return false; } private void Started(float now) { if (!Running) { _started = now; } Running = true; Error = ""; } internal void Restart() { Dispose(); _nextSourceCheck = 0f; } public void Dispose() { Error = ""; try { windows.Dispose(); } catch (Exception ex) { Error = ex.Message; } fusion.Reset(); _shared = false; Running = false; Level = 0f; _started = (_lastPacket = (_lastSignal = (_retry = 0f))); Source = "Microphone idle"; } } internal interface ICallTransport : IContactNetwork, ICallNetwork, IDisposable { IReadOnlyList<FriendRow> Friends { get; } IReadOnlyList<LobbyRow> Players { get; } string VideoWarning { get; } void Initialize(CallSession call, Contacts contacts, Harmony harmony); void Tick(double now); void Connect(); void Disconnect(); void RefreshFriends(); void RefreshPlayers(); bool SendMedia(byte kind, Guid call, byte[] payload); bool TryAudio(out byte[] data); byte[]? TakeVideo(); void ClearMedia(); } internal sealed class CallNetwork : IContactNetwork, ICallNetwork, IDisposable { private ICallTransport? _transport; private CallSession _call; private Contacts _contacts; private Harmony _harmony; private object? _layer; private bool _enabled = true; internal VoiceStats Voice { get; } = new VoiceStats(); public PeerId Self => _transport?.Self ?? default(PeerId); public bool Online { get { if (_enabled) { return _transport?.Online ?? false; } return false; } } public bool Busy => _call.State != CallState.Ready; internal bool HasSteamFriends => _transport is SteamNetwork; internal string NetworkName { get { if (!(_transport is EpicNetwork)) { if (!HasSteamFriends) { return "Offline"; } return "Steam"; } return "Epic Online Services"; } } internal IReadOnlyList<FriendRow> Friends => _transport?.Friends ?? Array.Empty<FriendRow>(); internal IReadOnlyList<LobbyRow> Players => _transport?.Players ?? Array.Empty<LobbyRow>(); internal string VideoWarning => _transport?.VideoWarning ?? ""; internal void Initialize(CallSession call, Contacts contacts, Harmony harmony) { _call = call; _contacts = contacts; _harmony = harmony; } internal void Tick(double now) { NetworkLayer val = (NetworkLayerManager.LoggedIn ? NetworkLayerManager.Layer : null); if (val != _layer) { _transport?.Disconnect(); _transport?.Dispose(); _transport = null; _layer = val; if (((val != null) ? val.Platform : null) == "Epic") { _transport = new EpicNetwork(Voice); } else if (val is SteamNetworkLayer) { _transport = new SteamNetwork(Voice); } if (_transport != null) { _transport.Initialize(_call, _contacts, _harmony); if (!_enabled) { _transport.Disconnect(); } } } if (_transport == null) { _call.Offline(_enabled ? "Connect to Fusion's Steam or Epic network." : "Calls are off. Enable calls in BoneMenu."); } else { _transport.Tick(now); } } internal void Connect() { _enabled = true; _transport?.Connect(); } internal void Disconnect() { _enabled = false; _transport?.Disconnect(); } public bool CanCall(PeerId peer) { if (Online) { return _transport.CanCall(peer); } return false; } public bool IsBlocked(PeerId peer) { if (Online) { return _transport.IsBlocked(peer); } return true; } public bool SameLobby(PeerId peer) { if (Online) { return _transport.SameLobby(peer); } return false; } public string Name(PeerId peer) { return _transport?.Name(peer) ?? _contacts.Get(peer)?.Name ?? "Player"; } public bool Send(PeerId peer, Signal message) { if (Online) { return _transport.Send(peer, message); } return false; } public void Close(PeerId peer) { _transport?.Close(peer); } internal void RefreshFriends() { _transport?.RefreshFriends(); } internal void RefreshPlayers() { _transport?.RefreshPlayers(); } internal bool SendMedia(byte kind, Guid call, byte[] payload) { if (Online) { return _transport.SendMedia(kind, call, payload); } return false; } internal bool TryAudio(out byte[] data) { data = null; return _transport?.TryAudio(out data) ?? false; } internal byte[]? TakeVideo() { return _transport?.TakeVideo(); } internal void ClearMedia() { _transport?.ClearMedia(); } public void Dispose() { _transport?.Dispose(); _transport = null; } } internal sealed class CallPanel : IDisposable { private sealed class Control { internal Image Image; internal Text Label; internal Rect Bounds; internal Color Color; internal Action Action; internal bool Enabled = true; } private sealed record PersonRow(string Name, string Status, bool Enabled, bool Social, Action Action, Action? Decline = null); private static readonly Color Ink = new Color(0.035f, 0.047f, 0.08f, 0.98f); private static readonly Color Card = new Color(0.075f, 0.09f, 0.145f, 1f); private static readonly Color Muted = new Color(0.55f, 0.61f, 0.72f, 1f); private static readonly Color Mint = new Color(0.27f, 0.94f, 0.76f, 1f); private static readonly Color Purple = new Color(0.49f, 0.4f, 0.98f, 1f); private static readonly Color Red = new Color(0.94f, 0.3f, 0.4f, 1f); private readonly CallSession _call; private readonly Preferences _prefs; private readonly CallNetwork _network; private readonly Contacts _contacts; private readonly Action _mediaChanged; private readonly VideoFeed _video; private readonly CallMicrophone _microphone; private readonly List<Control> _controls = new List<Control>(); private readonly PanelDrag _drag = new PanelDrag(); private static readonly Rect TitleBounds = new Rect(24f, 18f, 784f, 70f); private Image _titleBar; private Vector3 _dragAnchor; private bool _dragVr; private GameObject? _root; private GameObject? _wrist; private Font? _font; private Sprite? _round; private Texture2D? _roundTexture; private Text _status; private Text _friendCount; private Text _peopleTitle; private Text _page; private Text _overlay; private Text _peer; private Text _watchText; private Text _badge; private RawImage _remote; private RawImage _preview; private Control _search; private Control _end; private Control _mute; private Control _camera; private Control _view; private Control _next; private Control _answer; private Control _decline; private Control _previousPage; private Control _nextPage; private readonly Control[] _friendButtons = new Control[4]; private readonly Control[] _declineRequest = new Control[4]; private readonly Control[] _tabs = new Control[3]; private readonly List<PersonRow> _people = new List<PersonRow>(); private Control _notifications; private readonly bool[] _pressed = new bool[2]; private readonly LineRenderer?[] _rays = (LineRenderer?[])(object)new LineRenderer[2]; private Material? _rayMaterial; private int _friendPage; private int _peopleView; private float _rowsAt; private int _contactRevision = -1; private bool _visible; private float _openedAt; internal bool Visible => _visible; internal CallPanel(CallSession call, Preferences prefs, VideoFeed video, CallNetwork network, Contacts contacts, CallMicrophone microphone, Action mediaChanged) { _call = call; _prefs = prefs; _video = video; _network = network; _contacts = contacts; _microphone = microphone; _mediaChanged = mediaChanged; } internal void Toggle(Transform head) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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) Ensure(); _drag.Cancel(); _visible = !_visible; if (_visible) { if (_contacts.PendingCount > 0 && _call.State == CallState.Ready) { SelectView(3); } _openedAt = Time.unscaledTime; Vector3 forward = head.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.01f) { forward = Vector3.forward; } ((Vector3)(ref forward)).Normalize(); _root.transform.SetPositionAndRotation(head.position + forward * 1.05f - Vector3.up * 0.09f, Quaternion.LookRotation(forward)); } _root.SetActive(_visible); _pressed[0] = (_pressed[1] = true); } internal void Tick(Transform head) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) Ensure(); object obj; if (!_prefs.RightWrist) { Hand leftHand = Player.LeftHand; obj = ((leftHand != null) ? ((Component)leftHand).transform : null); } else { Hand rightHand = Player.RightHand; obj = ((rightHand != null) ? ((Component)rightHand).transform : null); } Transform val = (Transform)obj; _wrist.SetActive((Object)(object)val != (Object)null); if ((Object)(object)val != (Object)null) { Vector3 val2 = head.position - val.position; if (((Vector3)(ref val2)).sqrMagnitude > 0.001f) { _wrist.transform.SetPositionAndRotation(val.position + ((Vector3)(ref val2)).normalized * 0.075f, Quaternion.LookRotation(-val2, head.up)); } } _watchText.text = ((_call.State == CallState.Incoming) ? "INCOMING\nOPEN CALL" : ((_call.State == CallState.Active) ? "LIVE CALL\nBONEGLE" : ((_contacts.PendingCount > 0) ? "NEW REQUEST\nBONEGLE" : "BONEGLE\nOPEN"))); foreach (Control control in _controls) { ((Graphic)control.Image).color = (control.Enabled ? control.Color : Card); } ((Graphic)_titleBar).color = (_drag.Active ? Card : Ink); if (_visible) { Refresh(); } Pointer(0, head); Pointer(1, head); } private void Pointer(int hand, Transform head) { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_031e: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_03fe: Unknown result type (might be due to invalid IL or missing references) //IL_040a: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_0436: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Unknown result type (might be due to invalid IL or missing references) //IL_043d: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Unknown result type (might be due to invalid IL or missing references) //IL_0459: Unknown result type (might be due to invalid IL or missing references) //IL_045f: Unknown result type (might be due to invalid IL or missing references) BaseController val = ((hand == 0) ? Player.LeftController : Player.RightController); object obj; if (hand != 0) { XRApi xr = MarrowGame.xr; obj = ((xr != null) ? xr.RightController : null); } else { XRApi xr2 = MarrowGame.xr; obj = ((xr2 != null) ? xr2.LeftController : null); } XRController val2 = (XRController)obj; bool flag = (Object)(object)val != (Object)null && val2 != null && ((XRDevice)val2).IsConnected; bool flag2 = ((!flag) ? (hand == 1 && Input.GetKey((KeyCode)288)) : (val2.TriggerButton || val2.Trigger >= 0.7f)); Hand val3 = ((hand == 0) ? Player.LeftHand : Player.RightHand); bool flag3 = !flag || (Object)(object)val3 == (Object)null || !val3.HasAttachedObject(); Transform val4 = (Transform)(flag ? ((object)((Component)val).transform) : ((object)head)); bool flag4 = flag2 && !_pressed[hand] && flag3; _pressed[hand] = flag2; if (_drag.Hand == hand) { Vector3 panelPosition; Quaternion panelRotation; if (!_visible || !flag2 || !flag3 || flag != _dragVr) { _drag.Cancel(); DrawRay(hand, default(Vector3), default(Vector3), visible: false); } else if (_drag.Move(hand, Point(val4.position), Rotation(val4.rotation), out panelPosition, out panelRotation)) { _root.transform.SetPositionAndRotation(new Vector3(panelPosition.X, panelPosition.Y, panelPosition.Z), new Quaternion(panelRotation.X, panelRotation.Y, panelRotation.Z, panelRotation.W)); if (flag) { DrawRay(hand, val4.position, _root.transform.TransformPoint(_dragAnchor), visible: true); } } } else { if (!flag && hand == 0) { return; } if (_drag.Active) { DrawRay(hand, default(Vector3), default(Vector3), visible: false); return; } Ray ray = default(Ray); ((Ray)(ref ray))..ctor(val4.position, val4.forward); Control control = null; Vector3 hit = default(Vector3); bool flag5 = false; bool flag6 = false; int num = (_prefs.RightWrist ? 1 : 0); if (hand != num && _wrist.activeInHierarchy && Hit(_wrist.transform, ray, 180f, 84f, out var point, out hit)) { flag5 = point.x >= 0f && point.x <= 180f && point.y >= 0f && point.y <= 84f; } if (_visible && Hit(_root.transform, ray, 1000f, 660f, out point, out var hit2)) { foreach (Control control2 in _controls) { if (control2.Enabled && ((Component)control2.Image).gameObject.activeInHierarchy && ((Rect)(ref control2.Bounds)).Contains(point)) { control = control2; hit = hit2; break; } } int num2; if (control == null) { Rect titleBounds = TitleBounds; num2 = (((Rect)(ref titleBounds)).Contains(point) ? 1 : 0); } else { num2 = 0; } flag6 = (byte)num2 != 0; if (flag6) { hit = hit2; } } bool flag7 = flag3 && (control != null || flag6 || flag5); if (flag) { DrawRay(hand, val4.position, hit, flag7); } if (control != null) { ((Graphic)control.Image).color = Color.Lerp(control.Color, Color.white, 0.18f); } if (flag6 && flag3) { ((Graphic)_titleBar).color = Card; } if (!flag4 || !flag7) { return; } if (flag6 && Time.unscaledTime - _openedAt > 0.25f) { Transform transform = _root.transform; if (_drag.Begin(hand, Point(val4.position), Rotation(val4.rotation), Point(transform.position), Rotation(transform.rotation))) { _dragAnchor = transform.InverseTransformPoint(hit); _dragVr = flag; DrawRay(1 - hand, default(Vector3), default(Vector3), visible: false); } } else if (control != null && Time.unscaledTime - _openedAt > 0.25f) { control.Action(); } else if (flag5) { Toggle(head); } } } private static Vector3 Point(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) return new Vector3(value.x, value.y, value.z); } private static Quaternion Rotation(Quaternion value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Quaternion(value.x, value.y, value.z, value.w); } private static bool Hit(Transform panel, Ray ray, float width, float height, out Vector2 point, out Vector3 hit) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) point = default(Vector2); hit = default(Vector3); Plane val = default(Plane); ((Plane)(ref val))..ctor(panel.forward, panel.position); float num = default(float); if (!((Plane)(ref val)).Raycast(ray, ref num) || num < 0.015f || num > 2.5f) { return false; } hit = ((Ray)(ref ray)).GetPoint(num); Vector3 val2 = panel.InverseTransformPoint(hit); point = new Vector2(val2.x + width / 2f, height / 2f - val2.y); return true; } private void DrawRay(int hand, Vector3 from, Vector3 to, bool visible) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown if ((Object)(object)_rays[hand] == (Object)null && visible) { Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Color"); if (!Object.op_Implicit((Object)(object)val)) { return; } if (_rayMaterial == null) { _rayMaterial = new Material(val); } LineRenderer val2 = new GameObject("Bonegle Pointer") { layer = 5 }.AddComponent<LineRenderer>(); Object.DontDestroyOnLoad((Object)(object)((Component)val2).gameObject); ((Renderer)val2).material = _rayMaterial; val2.positionCount = 2; val2.startWidth = 0.0014f; val2.endWidth = 0.002f; val2.startColor = new Color(Mint.r, Mint.g, Mint.b, 0.15f); val2.endColor = Mint; val2.useWorldSpace = true; _rays[hand] = val2; } LineRenderer val3 = _rays[hand]; if (Object.op_Implicit((Object)(object)val3)) { ((Renderer)val3).enabled = visible; if (visible) { val3.SetPosition(0, from); val3.SetPosition(1, to); } } } private void Refresh() { //IL_0072: 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_0414: Unknown result type (might be due to invalid IL or missing references) //IL_0419: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Unknown result type (might be due to invalid IL or missing references) bool flag = _call.State == CallState.Active; bool flag2 = _call.State == CallState.Incoming; bool flag3 = _call.State == CallState.Ready; bool flag4 = _call.State == CallState.Offline; _badge.text = (flag ? "LIVE" : (flag4 ? "OFFLINE" : "STEAM ONLINE")); ((Graphic)_badge).color = ((flag || flag3) ? Mint : Muted); RefreshFriends(flag3); Text status = _status; bool flag5 = flag3; if (flag5) { int peopleView = _peopleView; bool flag6 = ((peopleView == 1 || peopleView == 3) ? true : false); flag5 = flag6; } status.text = (flag5 ? _contacts.Status : _call.Status); if (flag && _call.Mic) { if (_microphone.Error.Length > 0) { _status.text = _microphone.Error; } else if (_microphone.Silent(Time.unscaledTime)) { _status.text = "No mic signal. Check BoneMenu > Bonegle > Microphone."; } } _peer.text = (flag ? _call.Peer : "VIDEO CALL"); _remote.texture = ((flag && _call.RemoteCamera) ? _video.Remote : null); ((Behaviour)_remote).enabled = (Object)(object)_remote.texture != (Object)null; _preview.texture = ((flag && _call.Camera) ? _video.Local : null); ((Behaviour)_preview).enabled = (Object)(object)_preview.texture != (Object)null; _overlay.text = (flag2 ? (_call.Peer + " is calling") : ((!flag) ? ((_call.State == CallState.Searching) ? "Searching for someone..." : ((_call.State == CallState.Dialing) ? "Ringing..." : "Call a friend\nor meet someone new.")) : (_call.RemoteCamera ? "Waiting for video..." : "Their camera is off"))); ((Component)_overlay).gameObject.SetActive(!((Behaviour)_remote).enabled); ((Component)_answer.Image).gameObject.SetActive(flag2); _answer.Enabled = _call.CanAnswer; ((Component)_decline.Image).gameObject.SetActive(flag2); _search.Label.text = (flag4 ? "CONNECT" : ((_call.State == CallState.Searching) ? "SEARCHING..." : "OMEGLE MODE / SEARCH")); _search.Enabled = flag4 || flag3; Control end = _end; flag5 = flag || flag2; if (!flag5) { CallState state = _call.State; bool flag6 = ((state == CallState.Dialing || state == CallState.Searching) ? true : false); flag5 = flag6; } end.Enabled = flag5; _end.Label.text = ((_call.State == CallState.Searching) ? "STOP SEARCH" : "END CALL"); _mute.Label.text = ((!_call.Mic) ? "MIC MUTED" : ((_microphone.Error.Length > 0) ? "MIC ERROR" : (flag ? ("MIC [" + new string('|', _microphone.Bars) + new string('.', 5 - _microphone.Bars) + "]") : "MIC ON"))); _mute.Color = ((!_call.Mic || _microphone.Error.Length > 0) ? Red : ((_microphone.Bars > 0) ? Purple : Card)); _camera.Label.text = (_call.Camera ? "CAMERA ON" : "CAMERA OFF"); _view.Label.text = (_prefs.Selfie ? "VIEW SELFIE" : "VIEW FORWARD"); _next.Enabled = flag && _call.Random; } private void RefreshFriends(bool ready) { //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) ((Component)_tabs[2].Image).gameObject.SetActive(_network.HasSteamFriends); _tabs[2].Enabled = _network.HasSteamFriends; if (_peopleView == 2 && !_network.HasSteamFriends) { SelectView(0); } if (Time.unscaledTime >= _rowsAt || _contactRevision != _contacts.Revision) { BuildPeople(); } int num = Math.Max(1, (_people.Count + 3) / 4); _friendPage = Math.Clamp(_friendPage, 0, num - 1); _page.text = _friendPage + 1 + " / " + num; _previousPage.Enabled = _friendPage > 0; _nextPage.Enabled = _friendPage + 1 < num; Text peopleTitle = _peopleTitle; peopleTitle.text = _peopleView switch { 0 => "CONTACTS", 1 => "LOBBY PLAYERS", 2 => "STEAM FRIENDS", _ => "NOTIFICATIONS", }; peopleTitle = _friendCount; string text = ((_people.Count != 0) ? (_people.Count + " players") : (_peopleView switch { 0 => "Add someone from the Lobby tab", 1 => "No other players in this lobby", 2 => "No friends online", _ => "No pending requests", })); peopleTitle.text = text; for (int i = 0; i < _tabs.Length; i++) { _tabs[i].Color = ((_peopleView == i) ? Purple : Ink); } _notifications.Label.text = "NOTIFICATIONS" + ((_contacts.PendingCount > 0) ? (" (" + _contacts.PendingCount + ")") : ""); _notifications.Color = ((_peopleView == 3) ? Purple : Ink); for (int j = 0; j < _friendButtons.Length; j++) { Control control = _friendButtons[j]; int num2 = _friendPage * 4 + j; bool flag = num2 < _people.Count; ((Component)control.Image).gameObject.SetActive(flag); ((Component)_declineRequest[j].Image).gameObject.SetActive(flag && _people[num2].Decline != null); if (flag) { PersonRow personRow = _people[num2]; string text2 = ((personRow.Name.Length > 22) ? (personRow.Name.Substring(0, 21) + "...") : personRow.Name); control.Label.text = text2 + "\n" + personRow.Status; control.Action = personRow.Action; control.Enabled = _network.Online && personRow.Enabled && (ready || personRow.Social); control.Color = Ink; float num3 = ((personRow.Decline == null) ? 266 : 216); ((Component)control.Image).GetComponent<RectTransform>().sizeDelta = new Vector2(num3, 44f); ((Component)control.Label).GetComponent<RectTransform>().sizeDelta = new Vector2(num3 - 8f, 44f); ((Rect)(ref control.Bounds)).width = num3; _declineRequest[j].Action = personRow.Decline ?? ((Action)delegate { }); _declineRequest[j].Enabled = _network.Online; } } } private void SelectView(int view) { _peopleView = view; _friendPage = 0; _rowsAt = 0f; } private void BuildPeople() { _rowsAt = Time.unscaledTime + 1f; _contactRevision = _contacts.Revision; _people.Clear(); PeerId owner = _contacts.Owner; if (_peopleView == 0) { foreach (ContactRecord contact in from c in _contacts.Entries where c.State == ContactState.Accepted orderby c.Name select c) { _people.Add(new PersonRow(contact.Name, _contacts.Presence(contact.Id) + " / CALL", Enabled: true, Social: false, Guard(delegate { _call.Dial(contact.Id); }))); } return; } if (_peopleView == 1) { foreach (LobbyRow player in _network.Players) { ContactRecord contact2 = _contacts.Get(player.Id); ContactRecord contactRecord = contact2; if ((object)contactRecord != null && contactRecord.State == ContactState.Accepted) { _people.Add(new PersonRow(player.Name, "CALL", Enabled: true, Social: false, Guard(delegate { _call.Dial(player.Id); }))); } else { ContactRecord contactRecord2 = contact2; if ((object)contactRecord2 != null && contactRecord2.State == ContactState.Incoming) { _people.Add(new PersonRow(player.Name, "ACCEPT REQUEST", Enabled: true, Social: true, Guard(delegate { _contacts.Accept(player.Id, contact2.Request); }), Guard(delegate { _contacts.Decline(player.Id, contact2.Request); }))); } else { List<PersonRow> people = _people; string name = player.Name; ContactRecord contactRecord3 = contact2; string status = (((object)contactRecord3 != null && contactRecord3.State == ContactState.Outgoing) ? _contacts.RequestStatus(player.Id) : "ADD CONTACT"); ContactRecord contactRecord4 = contact2; people.Add(new PersonRow(name, status, (object)contactRecord4 == null || contactRecord4.State != ContactState.Outgoing, Social: true, Guard(delegate { _contacts.Request(player.Id); }))); } } } return; } if (_peopleView == 2) { foreach (FriendRow friend in _network.Friends) { _people.Add(new PersonRow(friend.Name, friend.Status, friend.Callable, Social: false, Guard(delegate { _call.Dial(friend.Id); }))); } return; } foreach (ContactRecord contact3 in _contacts.Entries.Where((ContactRecord c) => c.State == ContactState.Incoming)) { _people.Add(new PersonRow(contact3.Name, "ACCEPT REQUEST", Enabled: true, Social: true, Guard(delegate { _contacts.Accept(contact3.Id, contact3.Request); }), Guard(delegate { _contacts.Decline(contact3.Id, contact3.Request); }))); } Action Guard(Action action) { return delegate { if (_contacts.Owner == owner) { action(); } }; } } private void Ensure() { //IL_0098: Unknown result type (might be due to invalid IL or missing references) //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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03fa: Unknown result type (might be due to invalid IL or missing references) //IL_048a: Unknown result type (might be due to invalid IL or missing references) //IL_04f3: Unknown result type (might be due to invalid IL or missing references) //IL_052e: Unknown result type (might be due to invalid IL or missing references) //IL_0609: Unknown result type (might be due to invalid IL or missing references) //IL_064b: Unknown result type (might be due to invalid IL or missing references) //IL_0678: Unknown result type (might be due to invalid IL or missing references) //IL_06b8: Unknown result type (might be due to invalid IL or missing references) //IL_070a: Unknown result type (might be due to invalid IL or missing references) //IL_074a: Unknown result type (might be due to invalid IL or missing references) //IL_078f: Unknown result type (might be due to invalid IL or missing references) //IL_07cf: Unknown result type (might be due to invalid IL or missing references) //IL_080f: Unknown result type (might be due to invalid IL or missing references) //IL_084f: Unknown result type (might be due to invalid IL or missing references) //IL_0896: Unknown result type (might be due to invalid IL or missing references) //IL_08f8: Unknown result type (might be due to invalid IL or missing references) //IL_0928: Unknown result type (might be due to invalid IL or missing references) //IL_095b: Unknown result type (might be due to invalid IL or missing references) //IL_05a5: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_root) && Object.op_Implicit((Object)(object)_wrist)) { return; } if (Object.op_Implicit((Object)(object)_root) || Object.op_Implicit((Object)(object)_round)) { Dispose(); } _font = Resources.GetBuiltinResource<Font>("Arial.ttf"); MakeRound(); _root = CanvasRoot("Bonegle Panel", 1000f, 660f, 0.001f); Transform transform = _root.transform; Plate(transform, "Panel", 0f, 0f, 1000f, 660f, Ink); Rect titleBounds = TitleBounds; float x = ((Rect)(ref titleBounds)).x; titleBounds = TitleBounds; float y = ((Rect)(ref titleBounds)).y; titleBounds = TitleBounds; float width = ((Rect)(ref titleBounds)).width; titleBounds = TitleBounds; _titleBar = Plate(transform, "Title Bar", x, y, width, ((Rect)(ref titleBounds)).height, Ink); Plate(transform, "Accent", 32f, 31f, 5f, 40f, Mint); Label(transform, "BONEGLE", 50f, 22f, 360f, 39f, 30, Color.white, (FontStyle)1, (TextAnchor)3); Label(transform, "CALL ACROSS SERVERS / DRAG TO MOVE", 51f, 62f, 500f, 20f, 13, Muted, (FontStyle)0, (TextAnchor)3); _badge = Label(transform, "OFFLINE", 645f, 36f, 150f, 30f, 17, Mint, (FontStyle)1, (TextAnchor)3); Button(transform, "HIDE", 832f, 26f, 136f, 48f, Card, delegate { _drag.Cancel(); _visible = false; _root.SetActive(false); }); Plate(transform, "Screen", 32f, 108f, 600f, 338f, new Color(0.019f, 0.027f, 0.047f)); _remote = Raw(transform, "Remote", 32f, 108f, 600f, 338f); _peer = Label(transform, "VIDEO CALL", 52f, 123f, 450f, 25f, 15, Mint, (FontStyle)1, (TextAnchor)3); _overlay = Label(transform, "", 62f, 190f, 540f, 142f, 29, Color.white, (FontStyle)1, (TextAnchor)4); Plate(transform, "Preview Frame", 466f, 340f, 152f, 94f, Card); _preview = Raw(transform, "Preview", 471f, 345f, 142f, 80f); Label(transform, "YOU", 480f, 401f, 80f, 22f, 12, Color.white, (FontStyle)1, (TextAnchor)3); _answer = Button(transform, "ANSWER", 112f, 341f, 184f, 52f, Mint, _call.Accept, Ink); _decline = Button(transform, "DECLINE", 314f, 341f, 184f, 52f, Red, _call.HangUp); Plate(transform, "Friends", 656f, 108f, 312f, 414f, Card); _peopleTitle = Label(transform, "CONTACTS", 679f, 124f, 190f, 24f, 16, Mint, (FontStyle)1, (TextAnchor)3); Button(transform, "REFRESH", 869f, 124f, 76f, 28f, Ink, delegate { _network.RefreshFriends(); _network.RefreshPlayers(); _contacts.Refresh(); _rowsAt = 0f; }).Label.fontSize = 11; string[] array = new string[3] { "CONTACTS", "LOBBY", "STEAM" }; for (int num = 0; num < _tabs.Length; num++) { int view = num; _tabs[num] = Button(transform, array[num], 679 + num * 90, 157f, 86f, 28f, Ink, delegate { SelectView(view); }); _tabs[num].Label.fontSize = 11; } _friendCount = Label(transform, "", 679f, 187f, 268f, 22f, 12, Muted, (FontStyle)0, (TextAnchor)3); for (int num2 = 0; num2 < _friendButtons.Length; num2++) { Control control = Button(transform, "", 679f, 215 + num2 * 51, 266f, 44f, Ink, delegate { }); control.Label.fontSize = 14; _friendButtons[num2] = control; _declineRequest[num2] = Button(transform, "X", 903f, 215 + num2 * 51, 42f, 44f, Red, delegate { }); } _previousPage = Button(transform, "BACK", 679f, 424f, 78f, 34f, Ink, delegate { _friendPage--; }); _page = Label(transform, "", 762f, 424f, 89f, 34f, 14, Color.white, (FontStyle)0, (TextAnchor)4); _nextPage = Button(transform, "MORE", 867f, 424f, 78f, 34f, Ink, delegate { _friendPage++; }); _notifications = Button(transform, "NOTIFICATIONS", 679f, 468f, 266f, 32f, Ink, delegate { SelectView(3); }); _notifications.Label.fontSize = 13; _search = Button(transform, "OMEGLE MODE / SEARCH", 32f, 470f, 363f, 52f, Purple, delegate { if (_call.State == CallState.Offline) { _network.Connect(); } else { _call.Search(); } }); _end = Button(transform, "END CALL", 409f, 470f, 223f, 52f, Red, _call.HangUp); _mute = Button(transform, "MIC ON", 32f, 550f, 222f, 48f, Card, delegate { _call.Mic = !_call.Mic; _mediaChanged(); }); _camera = Button(transform, "CAMERA ON", 270f, 550f, 222f, 48f, Card, delegate { _call.Camera = !_call.Camera; _mediaChanged(); }); _view = Button(transform, "VIEW SELFIE", 508f, 550f, 222f, 48f, Card, delegate { _prefs.Selfie = !_prefs.Selfie; }); _next = Button(transform, "NEXT PERSON", 746f, 550f, 222f, 48f, Card, _call.Next); _status = Label(transform, "", 32f, 619f, 936f, 25f, 16, Muted, (FontStyle)0, (TextAnchor)3); _root.SetActive(false); _wrist = CanvasRoot("Bonegle Wrist", 180f, 84f, 0.00065f); Plate(_wrist.transform, "Watch", 0f, 0f, 180f, 84f, Ink); Plate(_wrist.transform, "Indicator", 9f, 16f, 4f, 52f, Mint); _watchText = Label(_wrist.transform, "BONEGLE\nOPEN", 20f, 10f, 148f, 64f, 20, Mint, (FontStyle)1, (TextAnchor)4); } private GameObject CanvasRoot(string name, float width, float height, float scale) { //IL_000e: 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_0039: Unknown result type (might be due to invalid IL or missing references) GameObject obj = Node(name); obj.GetComponent<RectTransform>().sizeDelta = new Vector2(width, height); Canvas obj2 = obj.AddComponent<Canvas>(); obj2.renderMode = (RenderMode)2; obj2.sortingOrder = 100; obj.transform.localScale = Vector3.one * scale; Object.DontDestroyOnLoad((Object)(object)obj); return obj; } private static GameObject Node(string name) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown return new GameObject(name, new Il2CppReferenceArray<Type>((Type[])(object)new Type[1] { Il2CppType.Of<RectTransform>() })) { layer = 5 }; } private static GameObject At(Transform parent, string name, float x, float y, float w, float h) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //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) //IL_003b: 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_0053: Unknown result type (might be due to invalid IL or missing references) GameObject obj = Node(name); obj.transform.SetParent(parent, false); RectTransform component = obj.GetComponent<RectTransform>(); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0f, 1f); component.pivot = val; Vector2 anchorMin = (component.anchorMax = val); component.anchorMin = anchorMin; component.sizeDelta = new Vector2(w, h); component.anchoredPosition = new Vector2(x, 0f - y); return obj; } private Image Plate(Transform parent, string name, float x, float y, float w, float h, Color color) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) Image obj = At(parent, name, x, y, w, h).AddComponent<Image>(); obj.sprite = _round; obj.type = (Type)1; ((Graphic)obj).color = color; ((Graphic)obj).raycastTarget = false; return obj; } private Text Label(Transform parent, string text, float x, float y, float w, float h, int size, Color color, FontStyle style = (FontStyle)0, TextAnchor align = (TextAnchor)3) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) Text obj = At(parent, "Label", x, y, w, h).AddComponent<Text>(); obj.font = _font; obj.text = text; obj.fontSize = size; obj.fontStyle = style; ((Graphic)obj).color = color; obj.alignment = align; obj.supportRichText = false; ((Graphic)obj).raycastTarget = false; obj.horizontalOverflow = (HorizontalWrapMode)0; obj.verticalOverflow = (VerticalWrapMode)0; return obj; } private static RawImage Raw(Transform parent, string name, float x, float y, float w, float h) { RawImage obj = At(parent, name, x, y, w, h).AddComponent<RawImage>(); ((Graphic)obj).raycastTarget = false; ((Behaviour)obj).enabled = false; return obj; } private Control Button(Transform parent, string name, float x, float y, float w, float h, Color color, Action action, Color? textColor = null) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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) Image val = Plate(parent, name, x, y, w, h, color); Text label = Label(((Component)val).transform, name, 4f, 0f, w - 8f, h, 16, (Color)(((??)textColor) ?? Color.white), (FontStyle)1, (TextAnchor)4); Control control = new Control { Image = val, Label = label, Bounds = new Rect(x, y, w, h), Color = color, Action = action }; _controls.Add(control); return control; } private void MakeRound() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) _roundTexture = new Texture2D(64, 64, (TextureFormat)4, false); Il2CppStructArray<Color32> val = new Il2CppStructArray<Color32>(4096L); for (int i = 0; i < 64; i++) { for (int j = 0; j < 64; j++) { float num = Mathf.Max(Mathf.Abs((float)j - 31.5f) - 17f, 0f); float num2 = Mathf.Max(Mathf.Abs((float)i - 31.5f) - 17f, 0f); byte b = (byte)(Mathf.Clamp01(14.5f - Mathf.Sqrt(num * num + num2 * num2)) * 255f); ((Il2CppArrayBase<Color32>)(object)val)[i * 64 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b); } } _roundTexture.SetPixels32(val); _roundTexture.Apply(false, false); _round = Sprite.Create(_roundTexture, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(16f, 16f, 16f, 16f)); } internal void Suspend() { _drag.Cancel(); if (Object.op_Implicit((Object)(object)_wrist)) { _wrist.SetActive(false); } if (Object.op_Implicit((Object)(object)_root)) { _root.SetActive(false); } _visible = false; LineRenderer[] rays = _rays; foreach (LineRenderer val in rays) { if (Object.op_Implicit((Object)(object)val)) { ((Renderer)val).enabled = false; } } _pressed[0] = (_pressed[1] = true); } public void Dispose() { Suspend(); if (Object.op_Implicit((Object)(object)_root)) { Object.Destroy((Object)(object)_root); } if (Object.op_Implicit((Object)(object)_wrist)) { Object.Destroy((Object)(object)_wrist); } LineRenderer[] rays = _rays; foreach (LineRenderer val in rays) { if (Object.op_Implicit((Object)(object)val)) { Object.Destroy((Object)(object)((Component)val).gameObject); } } if (Object.op_Implicit((Object)(object)_round)) { Object.Destroy((Object)(object)_round); } if (Object.op_Implicit((Object)(object)_roundTexture)) { Object.Destroy((Object)(object)_roundTexture); } if (Object.op_Implicit((Object)(object)_rayMaterial)) { Object.Destroy((Object)(object)_rayMaterial); } _controls.Clear(); _root = (_wrist = null); } } internal enum CallState { Offline, Ready, Dialing, Incoming, Searching, Active } internal interface ICallNetwork { PeerId Self { get; } bool CanCall(PeerId peer); bool IsBlocked(PeerId peer); string Name(PeerId peer); bool Send(PeerId peer, Signal message); void Close(PeerId peer); } internal sealed class CallSession(ICallNetwork network) { private readonly Queue<PeerId> _skipped = new Queue<PeerId>(); private readonly Queue<Guid> _ended = new Queue<Guid>(); private double _now; private double _deadline; private double _heartbeat; private double _lastHeard; private bool _outgoing; private bool _accepted; private PeerId _retryPeer; private double _retryUntil; internal CallState State { get; private set; } internal string Peer { get; private set; } = ""; internal PeerId PeerId { get; private set; } internal string Status { get; set; } = "Waiting for Fusion's network connection..."; internal Guid Call { get; private set; } internal int SearchId { get; private set; } internal bool Mic { get; set; } = true; internal bool Camera { get; set; } = true; internal bool RemoteMic { get; private set; } internal bool RemoteCamera { get; private set; } internal bool Random { get; private set; } internal bool CanAnswer { get { if (State == CallState.Incoming) { return !_accepted; } return false; } } internal bool Searching { get { if (State == CallState.Searching) { return PeerId == 0uL; } return false; } } internal void Online() { Reset(); State = CallState.Ready; Status = "Choose a contact or search for someone."; } internal void Offline(string reason) { Reset(); State = CallState.Offline; Status = reason; } internal void Dial(PeerId peer) { if (State != CallState.Ready) { return; } if (peer == 0uL || peer == network.Self || !network.CanCall(peer) || network.IsBlocked(peer)) { Status = "Choose an accepted contact or Steam friend."; return; } Begin(peer, Guid.NewGuid(), random: false, outgoing: true); State = CallState.Dialing; Status = "Calling " + Peer + "..."; _deadline = _now + 35.0; if (!network.Send(peer, Message("offer"))) { Finish("Could not reach your friend."); } } internal void Search() { if (State == CallState.Ready) { _skipped.Clear(); _retryPeer = 0uL; SearchAgain(); } } private void SearchAgain() { State = CallState.Searching; SearchId++; Status = "Searching for another Bonegle player..."; } internal bool CanMatch(PeerId peer) { if (Searching && peer != 0uL && peer != network.Self && !_skipped.Contains(peer) && (peer != _retryPeer || _now >= _retryUntil)) { return !network.IsBlocked(peer); } return false; } internal void Match(PeerId peer) { if (CanMatch(peer) && !(peer > network.Self)) { Begin(peer, Guid.NewGuid(), random: true, outgoing: true); _deadline = _now + 12.0; if (!network.Send(peer, Message("offer"))) { Retry("Searching for another Bonegle player..."); } } } internal void Accept() { if (CanAnswer) { _accepted = true; _deadline = _now + 12.0; Status = "Answering..."; if (!network.Send(PeerId, Message("accept"))) { Finish("The caller disconnected."); } } } internal void Receive(PeerId peer, Signal message) { if (peer == 0uL || peer == network.Self || State == CallState.Offline || !Guid.TryParse(message.Call, out var result) || result == Guid.Empty) { return; } if (message.Type == "offer") { if (PeerId == peer && Call == result) { return; } bool flag = message.Mode == "random"; if (message.Version == 3 && !_ended.Contains(result) && !network.IsBlocked(peer)) { bool num; if (!flag) { if (!(message.Mode == "friend") || State != CallState.Ready) { goto IL_00eb; } num = network.CanCall(peer); } else { num = CanMatch(peer); } if (num) { Begin(peer, result, flag, outgoing: false); _deadline = _now + (double)(flag ? 12 : 35); if (flag) { _accepted = true; if (!network.Send(peer, Message("accept"))) { Retry("Searching for another Bonegle player..."); } return; } State = CallState.Incoming; Status = Peer + " is calling."; if (!network.Send(peer, Message("ringing"))) { Finish("The caller disconnected."); } return; } } goto IL_00eb; } if (peer != PeerId || result != Call) { return; } if (message.Version != 3) { Finish("Both players need the same Bonegle build."); return; } _lastHeard = _now; switch (message.Type) { case "accept": { bool flag2 = _outgoing; if (flag2) { CallState state = State; bool flag3 = ((state == CallState.Dialing || state == CallState.Searching) ? true : false); flag2 = flag3; } if (flag2) { if (network.Send(peer, Message("start"))) { Activate(); } else { Disconnected(peer); } } break; } case "start": { bool flag2 = !_outgoing && _accepted; if (flag2) { CallState state = State; bool flag3 = (uint)(state - 3) <= 1u; flag2 = flag3; } if (flag2) { Activate(); } break; } case "media": if (State == CallState.Active) { RemoteMic = message.Mic; RemoteCamera = message.Camera; } break; case "ping": if (State == CallState.Active && !network.Send(peer, Message("pong"))) { Disconnected(peer); } break; case "end": Disconnected(peer); break; } return; IL_00eb: if (PeerId != peer) { network.Send(peer, new Signal { Type = "end", Call = message.Call }); network.Close(peer); } } internal void Disconnected(PeerId peer) { if (!(peer != PeerId) && !(peer == 0uL)) { if (State == CallState.Searching) { Retry("Searching for another Bonegle player..."); } else { Finish((State == CallState.Active) ? "Call ended. Choose a friend or press Search." : "Call ended or your friend is unavailable."); } } } internal void Tick(double now) { _now = now; if (PeerId == 0uL) { return; } if (State == CallState.Active) { if (now - _lastHeard > 30.0) { Finish("Connection lost."); } else if (now >= _heartbeat) { _heartbeat = now + 5.0; if (!network.Send(PeerId, Message("ping"))) { Disconnected(PeerId); } else { SendMediaState(); } } } else if (now >= _deadline) { network.Send(PeerId, Message("end")); if (State == CallState.Searching) { Retry("Searching for another Bonegle player..."); } else { Finish("No answer. Both players need the same Bonegle build and Fusion network."); } } } internal void HangUp() { CallState state = State; if ((uint)state > 1u) { if (PeerId != 0uL) { network.Send(PeerId, Message("end")); } Finish("Call ended. Choose a friend or press Search."); } } internal void Next() { if (State == CallState.Active && Random) { Skip(PeerId); network.Send(PeerId, Message("end")); Finish(""); SearchAgain(); } } internal void SearchFailed(string reason) { if (State == CallState.Searching) { Finish(reason); } } internal void SendMediaState() { if (State == CallState.Active && !network.Send(PeerId, Message("media")with { Mic = Mic, Camera = Camera })) { Disconnected(PeerId); } } internal bool Accepts(byte[] data) { if (State == CallState.Active && Protocol.ValidMedia(data, out var call) && call == Call) { if (data[0] != 2) { return RemoteCamera; } return RemoteMic; } return false; } private Signal Message(string type) { return new Signal { Type = type, Call = Call.ToString(), Mode = (Random ? "random" : "friend") }; } private void Begin(PeerId peer, Guid call, bool random, bool outgoing) { PeerId = peer; Peer = network.Name(peer); Call = call; Random = random; _outgoing = outgoing; _accepted = false; bool remoteMic = (RemoteCamera = false); RemoteMic = remoteMic; } private void Activate() { State = CallState.Active; _lastHeard = _now; _heartbeat = _now + 5.0; Status = "Connected to " + Peer; SendMediaState(); } private void Retry(string reason) { _retryPeer = PeerId; _retryUntil = _now + 10.0; Finish(reason); State = CallState.Searching; } private void Skip(PeerId peer) { if (!(peer == 0uL)) { _skipped.Enqueue(peer); while (_skipped.Count > 16) { _skipped.Dequeue(); } } } private void Finish(string reason) { PeerId peerId = PeerId; Reset(); State = CallState.Ready; Status = reason; if (peerId != 0uL) { network.Close(peerId); } } private void Reset() { if (Call != Guid.Empty) { _ended.Enqueue(Call); while (_ended.Count > 32) { _ended.Dequeue(); } } PeerId = 0uL; Peer = ""; Call = Guid.Empty; bool flag = (Random = (_accepted = (_outgoing = false))); bool remoteMic = (RemoteCamera = flag); RemoteMic = remoteMic; } } internal readonly record struct CameraPose(Vector3 Position, Quaternion Rotation) { internal static CameraPose Create(Vector3 head, Quaternion rotation, bool selfie, float scale, float distance) { scale = (float.IsFinite(scale) ? Math.Clamp(scale, 0.25f, 4f) : 1f); distance = (float.IsFinite(distance) ? Math.Clamp(distance, 0.4f, 2.5f) : 0.85f); Vector3 vector = (selfie ? new Vector3(0f, 0.03f, distance) : new Vector3(0f, 0f, 0.22f)); Quaternion quaternion = (selfie ? Quaternion.CreateFromYawPitchRoll((float)Math.PI, MathF.Atan2(0.13f, distance), 0f) : Quaternion.Identity); return new CameraPose(head + Vector3.Transform(vector * scale, rotation), Quaternion.Normalize(rotation * quaternion)); } } internal sealed class CaptureCursor(int capacity, int packetFrames, int rate, int position, float now) { private int _position = position; private float _lastPoll = now; private float _lastMovement = now; internal bool Stalled(float time) { return time - now >= 2f; } internal (int Offset, int Count) Read(int position, float now) { if (position < 0 || position >= capacity) { throw new ArgumentOutOfRangeException("position"); } int num = (position - position + capacity) % capacity; if (now - now >= (float)capacity / (float)rate || num > packetFrames * 3) { num = Math.Min(num, packetFrames * 3); position = (position - num + capacity) % capacity; } now = now; int num2 = num / packetFrames; int position2 = position; position = (position + num2 * packetFrames) % capacity; if (num2 > 0) { now = now; } return (Offset: position2, Count: num2); } } internal interface IContactNetwork : ICallNetwork { bool Online { get; } bool Busy { get; } bool SameLobby(PeerId peer); } internal sealed record ContactNotice(PeerId Owner, string Kind, PeerId Peer, Guid Request, string Name, string Text); internal sealed class Contacts(IContactNetwork network, ContactStore store) { private readonly Dictionary<PeerId, ContactRecord> _entries = new Dictionary<PeerId, ContactRecord>(); private readonly Dictionary<PeerId, (double Seen, bool Busy)> _presence = new Dictionary<PeerId, (double, bool)>(); private readonly Dictionary<PeerId, double> _nextProbe = new Dictionary<PeerId, double>(); private readonly HashSet<PeerId> _delivered = new HashSet<PeerId>(); private readonly HashSet<PeerId> _failed = new HashSet<PeerId>(); private readonly Dictionary<PeerId, double> _sentAt = new Dictionary<PeerId, double>(); private readonly Queue<ContactNotice> _notices = new Queue<ContactNotice>(); private bool _writable = true; private double _now; private double _pollAt; internal PeerId Owner { get; private set; } internal int Revision { get; private set; } internal string Status { get; private set; } = "Add people from Lobby Players."; internal IEnumerable<ContactRecord> Entries => _entries.Values; internal int PendingCount => _entries.Values.Count((ContactRecord c) => c.State == ContactState.Incoming); internal bool Knows(PeerId peer) { return _entries.ContainsKey(peer); } internal bool Accepted(PeerId peer) { ContactRecord? contactRecord = Get(peer); if ((object)contactRecord == null) { return false; } return contactRecord.State == ContactState.Accepted; } internal ContactRecord? Get(PeerId peer) { return _entries.GetValueOrDefault(peer); } internal bool TryNotice(out ContactNotice notice) { return _notices.TryDequeue(out notice); } internal static bool IsMessage(string type) { switch (type) { case "contact_decline": case "contact_pending": case "contact_request": case "contact_accept": case "contact_remove": case "contact_probe": case "contact_presence": return true; default: return false; } } internal void UseAccount(PeerId owner) { Offline(); if (Owner == owner) { return; } Owner = owner; _entries.Clear(); _notices.Clear(); _writable = true; _delivered.Clear(); _failed.Clear(); _sentAt.Clear(); try { ContactRecord[] array = store.Load(owner); foreach (ContactRecord contactRecord in array) { _entries.Add(contactRecord.Id, contactRecord); } } catch (Exception ex) when (((ex is IOException || ex is UnauthorizedAccessException || ex is JsonException) ? 1 : 0) != 0) { _writable = false; Status = "Could not load contacts. The existing file was kept."; Revision++; return; } Status = "Add people from Lobby Players."; foreach (ContactRecord item in _entries.Values.Where((ContactRecord c) => c.State == ContactState.Incoming)) { Notice("request", item, item.Name + " wants to add you."); } Revision++; } internal void Offline() { _presence.Clear(); _nextProbe.Clear(); _pollAt = 0.0; Revision++; } internal string Presence(PeerId peer) { if (!network.Online) { return "Offline"; } if (!_presence.TryGetValue(peer, out (double, bool) value) || _now - value.Item1 > 100.0) { return "Not reached"; } if (!value.Item2) { return "Available"; } return "Busy"; } internal bool Request(PeerId peer) { if (!Ready() || peer == 0uL || peer == Owner || network.IsBlocked(peer)) { return false; } ContactRecord contactRecord = Get(peer); if ((object)contactRecord != null && contactRecord.State == ContactState.Accepted) { Status = "Already in your contacts."; return false; } if ((object)contactRecord != null && contactRecord.State == ContactState.Incoming) { Status = "Open Notifications to accept their request."; return false; } if ((object)contactRecord != null && contactRecord.State == ContactState.Outgoing) { _failed.Remove(peer); _sentAt[peer] = _now; bool flag = Send(contactRecord, "contact_request"); Status = (flag ? ("Retrying the request to " + contactRecord.Name + "...") : "Could not send the request. Both players need the same Bonegle build."); Revision++; return flag; } if (!network.SameLobby(peer)) { Status = "Join their Fusion lobby to send a request."; return false; } if ((object)contactRecord != null && contactRecord.State == ContactState.Hidden && Age(contactRecord) < 60) { Status = "Wait a moment before sending another request."; return false; } if (_entries.Values.Count((ContactRecord c) => c.State != ContactState.Hidden) >= 128) { Status = "Your contact list is full. Remove a contact first."; return false; } ContactRecord contactRecord2 = new ContactRecord(peer, DisplayName.Clean(network.Name(peer)), ContactState.Outgoing, Guid.NewGuid(), Stamp()); if (!Put(contactRecord2)) { return false; } bool flag2 = Send(contactRecord2, "contact_request"); _sentAt[peer] = _now; Status = (flag2 ? ("Sending a request to " + contactRecord2.Name + "...") : "Could not send the request. Both players need the same Bonegle build."); _nextProbe[peer] = _now + 45.0; return flag2; } internal bool IsPending(PeerId peer, Guid request) { ContactRecord contactRecord = Get(peer); if ((object)contactRecord != null && contactRecord.State == ContactState.Incoming) { return contactRecord.Request == request; } return false; } internal bool Accept(PeerId peer, Guid request) { if (!Ready() || !IsPending(peer, request) || network.IsBlocked(peer)) { return false; } ContactRecord contactRecord = Get(peer)with { State = ContactState.Accepted, Updated = Stamp() }; if (!Put(contactRecord)) { return false; } Send(contactRecord, "contact_accept"); _nextProbe[peer] = _now + 5.0; Status = contactRecord.Name + " is now a contact."; Notice("added", contactRecord, Status); return true; } internal bool Decline(PeerId peer, Guid request) { if (!Ready() || !IsPending(peer, request)) { return false; } return Remove(peer); } internal bool Remove(PeerId peer) { if (Ready()) { ContactRecord contactRecord = Get(peer); if ((object)contactRecord != null && contactRecord.State != ContactState.Hidden) { string type = ((contactRecord.State == ContactState.Accepted) ? "contact_remove" : "contact_decline"); if (!Put(contactRecord with { State = ContactState.Hidden, Updated = Stamp() })) { return false; } Send(contactRecord, type); _presence.Remove(peer); _nextProbe.Remove(peer); _delivered.Remove(peer); Status = ((contactRecord.State == ContactState.Accepted) ? "Contact removed." : "Request closed."); return true; } } return false; } internal void Receive(PeerId peer, Signal message) { if (!network.Online || Owner != network.Self || peer == 0uL || peer == Owner || network.IsBlocked(peer) || message.Version != 3 || !Guid.TryParse(message.Request, out var result) || result == Guid.Empty) { return; } ContactRecord contactRecord = Get(peer); if (message.Type == "contact_request") { if ((object)contactRecord != null && contactRecord.Request == result) { ContactRecord contact = contactRecord; Send(contact, contactRecord.State switch { ContactState.Accepted => "contact_accept", ContactState.Hidden => "contact_decline", _ => "contact_pending", }); } else { if (!network.SameLobby(peer) || !_writable || ((object)contactRecord != null && contactRecord.State == ContactState.Hidden && Age(contactRecord) < 60) || _entries.Values.Count((ContactRecord c) => c.State == ContactState.Incoming) >= 16 || _entries.Values.Count((ContactRecord c) => c.State != ContactState.Hidden) >= 128 || ((object)contactRecord != null && contactRecord.State == ContactState.Incoming && Age(contactRecord) < 60)) { return; } if ((object)contactRecord != null && contactRecord.State == ContactState.Outgoing && Owner < peer) { Send(contactRecord, "contact_request"); return; } contactRecord = new ContactRecord(peer, DisplayName.Clean(network.Name(peer)), ContactState.Incoming, result, Stamp()); if (Put(contactRecord)) { Send(contactRecord, "contact_pending"); Notice("request", contactRecord, contactRecord.Name + " wants to add you."); } } } else { if (contactRecord == null || contactRecord.Request != result) { return; } switch (message.Type) { case "contact_pending": if (contactRecord.State == ContactState.Outgoing) { _failed.Remove(peer); _sentAt.Remove(peer); if (_delivered.Add(peer)) { Revision++; } Status = "Request sent to " + contactRecord.Name + ". Waiting for them to accept."; } break; case "contact_accept": if (contactRecord.State != ContactState.Outgoing) { break; } goto IL_02fe; case "contact_probe": if (contactRecord.State != ContactState.Outgoing) { if (contactRecord.State == ContactState.Accepted) { Seen(peer, message.Presence); Send(contactRecord, "contact_presence"); } else if (contactRecord.State == ContactState.Hidden) { Send(contactRecord, "contact_remove"); } break; } goto IL_02fe; case "contact_decline": if (contactRecord.State == ContactState.Hidden) { break; } goto IL_0393; case "contact_remove": if (contactRecord.State == ContactState.Hidden) { break; } goto IL_0393; case "contact_presence": { if (contactRecord.State == ContactState.Accepted) { Seen(peer, message.Presence); } break; } IL_0393: if (Put(contactRecord with { State = ContactState.Hidden, Updated = Stamp() })) { _presence.Remove(peer); Status = ((contactRecord.State == ContactState.Accepted) ? (contactRecord.Name + " removed the contact.") : ("Request closed by " + contactRecord.Name + ".")); Notice("closed", contactRecord, Status); } break; IL_02fe: contactRecord = contactRecord with { State = ContactState.Accepted, Updated = Stamp() }; if (Put(contactRecord)) { Notice("added", contactRecord, contactRecord.Name + " accepted your request."); Status = contactRecord.Name + " is now a contact."; Send(contactRecord, "contact_presence"); _nextProbe[peer] = _now + 5.0; } break; } } } internal void Tick(double now, bool canProbe) { _now = now; KeyValuePair<PeerId, double>[] array = _sentAt.Where((KeyValuePair<PeerId, double> x) => now - x.Value > 20.0).ToArray(); for (int num = 0; num < array.Length; num++) { KeyValuePair<PeerId, double> keyValuePair = array[num]; _sentAt.Remove(keyValuePair.Key); Unreachable(keyValuePair.Key); } if (!network.Online || Owner != network.Self || !canProbe || now < _pollAt) { return; } _pollAt = now + 1.0; ContactRecord contactRecord = (from c in _entries.Values where c.State == ContactState.Outgoing || (c.State == ContactState.Accepted && Owner > c.Id) where _nextProbe.GetValueOrDefault(c.Id) <= now && !network.IsBlocked(c.Id) orderby _nextProbe.GetValueOrDefault(c.Id) select c).FirstOrDefault(); if (!(contactRecord == null)) { _nextProbe[contactRecord.Id] = now + 45.0; if (contactRecord.State == ContactState.Accepted) { Send(contactRecord, "contact_probe"); } else { Send(contactRecord, "contact_request"); } } } internal void Refresh() { _nextProbe.Clear(); _pollAt = 0.0; Revision++; } internal string RequestStatus(PeerId peer) { if (!_delivered.Contains(peer)) { if (!_failed.Contains(peer)) { return "Waiting for delivery"; } return "Not reached / retry"; } return "Waiting for acceptance"; } internal void Unreachable(PeerId peer) { ContactRecord? contactRecord = Get(peer); if ((object)contactRecord != null && contactRecord.State == ContactState.Outgoing && !_delivered.Contains(peer) && _failed.Add(peer)) { Status = Get(peer).Name + " was not reached. Both players need the same Bonegle build."; Revision++; } } private void Seen(PeerId peer, string presence) { if ((presence == "ready" || presence == "busy") ? true : false) { bool num = Presence(peer) != ((presence == "busy") ? "Busy" : "Available"); _presence[peer] = (_now, presence == "busy"); if (num) { Revision++; } } } private bool Ready() { if (!network.Online || Owner == 0uL || Owner != network.Self) { Status = "Enable calls and connect to Fusion first."; return false; } if (!_writable) { Status = "Contacts are unavailable. The existing file was kept."; return false; } return true; } private bool Send(ContactRecord contact, string type) { return network.Send(contact.Id, new Signal { Type = type, Request = contact.Request.ToString(), Presence = (network.Busy ? "busy" : "ready") }); } private bool Put(ContactRecord contact) { if (!_writable) { return false; } ContactRecord[] array = (from c in _entries.Values.Where((ContactRecord c) => c.Id != contact.Id).Append(contact) orderby c.State == ContactState.Hidden, c.Updated descending select c).Take(192).ToArray(); try { store.Save(Owner, array); } catch (Exception ex) when (((ex is IOException || ex is UnauthorizedAccessException) ? 1 : 0) != 0) { Status = "Could not save contacts. Try again after checking the UserData folder."; Revision++; return false; } _entries.Clear(); ContactRecord[] array2 = array; foreach (ContactRecord contactRecord in array2) { _entries.Add(contactRecord.Id, contactRecord); } Revision++; return true; } private void Notice(string kind, ContactRecord contact, string text) { if (_notices.Count >= 32) { _notices.Dequeue(); } _notices.Enqueue(new ContactNotice(Owner, kind, contact.Id, contact.Request, contact.Name, text)); } private static long Stamp() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } private static long Age(ContactRecord contact) { return Stamp() - contact.Updated; } } internal enum ContactState { Incoming, Outgoing, Accepted, Hidden } internal sealed record ContactRecord(PeerId Id, string Name, ContactState State, Guid Request, long Updated); internal sealed class ContactStore(string directory) { private sealed record Profile(int Version, PeerId Owner, ContactRecord[] Contacts); internal ContactRecord[] Load(PeerId owner) { string text = PathFor(owner); if (!File.Exists(text)) { return Array.Empty<ContactRecord>(); } if (new FileInfo(text).Length > 262144) { throw new InvalidDataException("Contact file is too large."); } Profile profile = JsonSerializer.Deserialize<Profile>(File.ReadAllText(text)); if (profile == null || profile.Version != 1 || profile.Owner != owner || profile.Contacts == null || profile.Contacts.Length > 192) { throw new InvalidDataException("Invalid contact file."); } HashSet<PeerId> hashSet = new HashSet<PeerId>(); ContactRecord[] contacts = profile.Contacts; foreach (ContactRecord contactRecord in contacts) { if (contactRecord == null || contactRecord.Id == 0uL || contactRecord.Id == owner || contactRecord.Request == Guid.Empty || contactRecord.Id.IsEpic != owner.IsEpic || !Enum.IsDefined(typeof(ContactState), contactRecord.State) || contactRecord.Name == null || !hashSet.Add(contactRecord.Id)) { throw new InvalidDataException("Invalid contact entry."); } } return profile.Contacts.Select((ContactRecord c) => c with { Name = DisplayName.Clean(c.Name) }).ToArray(); } internal void Save(PeerId owner, IEnumerable<ContactRecord> contacts) { Profile profile = new Profile(1, owner, contacts.ToArray()); if (owner == 0uL || profile.Contacts.Length > 192) { throw new InvalidDataException("Invalid contact profile."); } Directory.CreateDirectory(directory); string text = PathFor(owner); string text2 = text + ".tmp"; File.WriteAllText(text2, JsonSerializer.Serialize(profile)); File.Move(text2, text, overwrite: true); } private string PathFor(PeerId owner) { return Path.Combine(directory, "contacts-" + (owner.IsEpic ? ("epic-" + owner.Raw) : owner.ToString()) + ".json"); } } internal static class DisplayName { internal static string Clean(string? name) { string text = new string((name ?? "").Where(delegate(char c) { bool flag = !char.IsControl(c); if (flag) { bool flag2 = ((c == '<' || c == '>') ? true : false); flag = !flag2; } return flag; }).Take(40).ToArray()); if (!string.IsNullOrWhiteSpace(text)) { return text; } return "Player"; } } internal sealed class EpicBridge : IDisposable { private static Assembly? _assembly; private readonly object _instance; private readonly Func<bool> _alive; private readonly Func<string> _error; private readonly Action<double> _tick; private readonly Action<bool, int, double> _search; private readonly Func<string, byte, byte[], int, bool, bool> _send; private readonly Action<string> _close; private readonly Action _dispose; internal string Self { get; } internal string DisplayName { get; } internal bool Alive => _alive(); internal string Error => _error(); internal EpicBridge(object layer, Func<string, bool> allow, Action<string, byte, byte[], int> receive, Action<string> closed, Action<int, string, string> matched, Action<string> searchError) { if (_assembly == null) { using Stream stream = typeof(EpicBridge).Assembly.GetManifestResourceStream("Bonegle.Epic.dll") ?? throw new InvalidOperationException("Epic support is missing from this build."); using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); memoryStream.Position = 0L; _assembly = (AssemblyLoadContext.GetLoadContext(layer.GetType().Assembly) ?? AssemblyLoadContext.Default).LoadFromStream(memoryStream); } Type type = _assembly.GetType("Bonegle.Epic.Bridge", throwOnError: true); _instance = Activator.CreateInstance(type, layer, allow, receive, closed, matched, searchError); Self = (string)type.GetProperty("Self").GetValue(_instance); DisplayName = (string)type.GetProperty("DisplayName").GetValue(_instance); _alive = Bind<Func<bool>>("get_Alive"); _error = Bind<Func<string>>("get_Error"); _tick = Bind<Action<double>>("Tick"); _search = Bind<Action<bool, int, double>>("Search"); _send = Bind<Func<string, byte, byte[], int, bool, bool>>("Send"); _close = Bind<Action<string>>("Close"); _dispose = Bind<Action>("Dispose"); T Bind<T>(string name) where T : notnull, Delegate { return type.GetMethod(name).CreateDelegate<T>(_instance); } } internal void Tick(double now) { _tick(now); } internal void Search(bool wanted, int id, double now) { _search(wanted, id, now); } internal bool Send(string peer, byte channel, byte[] data, int count, bool reliable) { return _send(peer, channel, data, count, reliable); } internal void Close(string peer) { _close(peer); } public void Dispose() { _dispose(); } } internal sealed class EpicNetwork(VoiceStats voice) : ICallTransport, IContactNetwork, ICallNetwork, IDisposable { private sealed class Peer { internal double LastSeen; internal double Window; internal int Controls; internal void Touch(double now) { LastSeen = now; } } private readonly List<LobbyRow> _players = new List<LobbyRow>(); private readonly Dictionary<PeerId, string> _names = new Dictionary<PeerId, string>(); private readonly Dictionary<PeerId, Peer> _peers = new Dictionary<PeerId, Peer>(); private readonly Dictionary<PeerId, double> _closing = new Dictionary<PeerId, double>(); private readonly Queue<byte[]> _audio = new Queue<byte[]>(); private readonly EpicFrames _frames = new EpicFrames(); private readonly VideoBudget _videoBudget = new VideoBudget(); private readonly MediaBudget _receiveBudget = new MediaBudget(); private readonly byte[] _packet = new byte[1170]; private EpicBridge? _bridge; private object? _layer; private CallSession _call; private Contacts _contacts; private byte[]? _video; private Guid _mediaCall; private uint _audioSequence; private uint _videoSequence; private uint _lastAudio; private uint _lastVideo; private bool _enabled = true; private bool _hasAudio; private bool _hasVideo; private double _now; private double _retryAt; private double _nextPlayers; private string _name = "Player"; public PeerId Self { get; private set; } public bool Online { get { if (_enabled) { EpicBridge? bridge = _bridge; if (bridge != null && bridge.Alive) { return NetworkLayerManager.Layer == _layer; } } return false; } } public bool Busy => _call.State != CallState.Ready; public IReadOnlyList<FriendRow> Friends => Array.Empty<FriendRow>(); public IReadOnlyList<LobbyRow> Players => _players; public string VideoWarning { get; private set; } = ""; public void Initialize(CallSession call, Contacts contacts, Harmony harmony) { _call = call; _contacts = contacts; } public void Connect() { _enabled = true; _retryAt = 0.0; } public void Tick(double now) { _now = now; if (!_enabled) { return; } if (NetworkLayerManager.LoggedIn) { NetworkLayer layer = NetworkLayerManager.Layer; if (!(((layer != null) ? layer.Platform : null) != "Epic")) { if (_bridge != null && !Online) { Stop(); } if (_bridge == null) { if (now < _retryAt) { return; } try { _layer = NetworkLayerManager.Layer; _bridge = new EpicBridge(_layer, Allow, Received, Closed, Matched, delegate(string error) { _call.SearchFailed(error); }); Self = PeerId.Epic(_bridge.Self); if (Self.IsEmpty) { throw new InvalidOperationException("Fusion's Epic account ID is unavailable."); } _name = DisplayName.Clean(_bridge.DisplayName); _contacts.UseAccount(Self); _call.Online(); _nextPlayers = 0.0; } catch (Exception ex) { Stop(); _retryAt = now + 10.0; _call.Offline("Epic calls unavailable: " + (ex.InnerException?.Message ?? ex.Message)); return; } } if (_mediaCall != _call.Call) { ClearMedia(); _mediaCall = _call.Call; _audioSequence = (_videoSequence = 0u); _hasAudio = (_hasVideo = false); _videoBudget.Reset(); } if (now >= _nextPlayers) { RefreshPlayers(); } _call.Tick(now); _bridge.Search(_call.Searching, _call.SearchId, now); _bridge.Tick(now); _bridge.Search(_call.Searching, _call.SearchId, now); _contacts.Tick(now, _peers.Count < 12); KeyValuePair<PeerId, double>[] array = _closing.Where((KeyValuePair<PeerId, double> pair) => now >= pair.Value).ToArray(); for (int num = 0; num < array.Length; num++) { KeyValuePair<PeerId, double> keyValuePair = array[num]; _closing.Remove(keyValuePair.Key); _bridge.Close(keyValuePair.Key.Raw); _peers.Remove(keyValuePair.Key); } KeyValuePair<PeerId, Peer>[] array2 = _peers.Where<KeyValuePair<PeerId, Peer>>((KeyValuePair<PeerId, Peer> pair) => pair.Key != _call.PeerId && now - pair.Value.LastSeen > 12.0).ToArray(); for (int num = 0; num < array2.Length; num++) { KeyValuePair<PeerId, Peer> keyValuePair2 = array2[num]; _bridge.Close(keyValuePair2.Key.Raw); _peers.Remove(keyValuePair2.Key); } return; } } Stop(); _call.Offline("Connect to Fusion's Epic Online Services network."); } private bool Allow(string raw) { PeerId peerId = PeerId.Epic(raw); if (Online && !peerId.IsEmpty && peerId != Self && !IsBlocked(peerId)) { if (!(peerId == _call.PeerId) && !_contacts.Knows(peerId) && !SameLobby(peerId)) { return _call.CanMatch(peerId);