Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SharePermissions v2.36.1
SharePermissions.dll
Decompiled 3 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using POpusCodec.Enums; using Photon.Pun; using Photon.Realtime; using Photon.Voice; using Photon.Voice.PUN; using Photon.Voice.Unity; using SharePermissions.Core; using SharePermissions.Net; using SharePermissions.Net.Commands; using SharePermissions.Patches; using SharePermissions.Players; using SharePermissions.UI; using SharePermissions.UI.Kit; using SharePermissions.Voice; using Steamworks; using Steamworks.Data; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.TextCore; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("RED")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("2.36.1.0")] [assembly: AssemblyInformationalVersion("2.36.1+7bafb24bec10113d392915440f5fbcab51e332cc")] [assembly: AssemblyProduct("SharePermissions")] [assembly: AssemblyTitle("SharePermissions")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.36.1.0")] [module: UnverifiableCode] [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 SharePermissions { public static class SharePermissionsApi { private enum Member { IsModeratorActor, LocalIsModerator, UiLanguage, IsHotkeyTaken } public const int ApiVersion = 1; private static int failuresNoted; public static bool LocalIsModerator { get { try { return LocalIsModeratorRaw(); } catch (Exception ex) { NoteFailure(Member.LocalIsModerator, ex); return false; } } } public static string UiLanguage { get { try { return UiLanguageRaw(); } catch (Exception ex) { NoteFailure(Member.UiLanguage, ex); return ""; } } } public static bool IsModeratorActor(int actor) { try { return IsModeratorActorRaw(actor); } catch (Exception ex) { NoteFailure(Member.IsModeratorActor, ex); return false; } } public static bool IsHotkeyTaken(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return IsHotkeyTakenRaw(key); } catch (Exception ex) { NoteFailure(Member.IsHotkeyTaken, ex); return false; } } [MethodImpl(MethodImplOptions.NoInlining)] private static bool IsModeratorActorRaw(int actor) { if (actor > 0 && PhotonNetwork.InRoom && SemiFunc.IsMasterClient()) { return AuthorizedActors.IsModeratorActor(actor); } return false; } [MethodImpl(MethodImplOptions.NoInlining)] private static bool LocalIsModeratorRaw() { if (PhotonNetwork.InRoom) { return ModerationActions.LocalIsModerator; } return false; } [MethodImpl(MethodImplOptions.NoInlining)] private static string UiLanguageRaw() { return LanguageGuess.CodeOf(Loc.Current); } [MethodImpl(MethodImplOptions.NoInlining)] private static bool IsHotkeyTakenRaw(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return ModConfig.IsOwnHotkey(key); } private static void NoteFailure(Member member, Exception ex) { try { int num = 1 << (int)member; if ((failuresNoted & num) == 0) { failuresNoted |= num; LogFailure(member, ex); } } catch (Exception) { } } [MethodImpl(MethodImplOptions.NoInlining)] private static void LogFailure(Member member, Exception ex) { Plugin.Logger.LogWarning((object)$"[SharePermissions] SharePermissionsApi.{member} failed and answered its fallback (logged once): {ex.GetType().Name}: {ex.Message}"); } } } namespace SharePermissions.Voice { internal sealed class MicRing { private readonly float[] buffer; private volatile int writeIndex; private volatile int readIndex; private volatile float level; internal float Level => level; internal int Available { get { int num = writeIndex; int num2 = readIndex; if (num < num2) { return buffer.Length - num2 + num; } return num - num2; } } internal MicRing(int capacity) { if (capacity < 2) { capacity = 2; } buffer = new float[capacity]; } internal void Write(float[] data, int count) { if (data != null && count > 0) { if (count > data.Length) { count = data.Length; } float num = 0f; for (int i = 0; i < count; i++) { num += Math.Abs(data[i]); } level = num / (float)count; int num2 = writeIndex; int num3 = buffer.Length - 1 - Available; if (count > num3) { count = num3; } for (int j = 0; j < count; j++) { buffer[num2] = data[j]; num2 = ((num2 + 1 != buffer.Length) ? (num2 + 1) : 0); } writeIndex = num2; } } internal bool Read(float[] destination) { if (destination == null || destination.Length == 0) { return false; } if (Available < destination.Length) { return false; } int num = readIndex; for (int i = 0; i < destination.Length; i++) { destination[i] = buffer[num]; num = ((num + 1 != buffer.Length) ? (num + 1) : 0); } readIndex = num; return true; } internal void Reset() { readIndex = 0; writeIndex = 0; level = 0f; } } [HarmonyPatch(typeof(MicWrapper), "Read")] internal static class MicTee { private const int RingCapacity = 48000; internal static readonly MicRing Ring = new MicRing(48000); private static volatile int samplingRate; private static volatile int channels; internal static int SamplingRate => samplingRate; internal static int Channels => channels; internal static bool IsLive { get { if (SamplingRate > 0) { return Channels > 0; } return false; } } internal static float Level => Ring.Level; [HarmonyPostfix] [HarmonyWrapSafe] private static void Read_Postfix(MicWrapper __instance, float[] buffer, bool __result) { if (__result && buffer != null && PrivateVoiceGate.WantCapture) { channels = __instance.Channels; samplingRate = __instance.SamplingRate; Ring.Write(buffer, buffer.Length); } } internal static void Flush() { Ring.Reset(); Plugin.Logger.LogDebug((object)"Private voice: mic tee flushed"); } } internal static class PrivateVoiceGate { internal static volatile bool WantCapture; } internal sealed class PeakLimiter { internal const float Ceiling = 0.891f; internal const float ReleaseSeconds = 0.25f; private float reduction = 1f; private float releaseCoefficient = 1f - (float)Math.Exp(-8.333333333333333E-05); internal void Configure(int sampleRate) { if (sampleRate > 0) { releaseCoefficient = 1f - (float)Math.Exp(-1.0 / (double)(0.25f * (float)sampleRate)); } } internal void Reset() { reduction = 1f; } internal void Process(float[] data, int channels, float gain) { if (data == null) { return; } if (channels < 1) { channels = 1; } float num = reduction; float num2 = releaseCoefficient; for (int i = 0; i + channels <= data.Length; i += channels) { float num3 = 0f; for (int j = 0; j < channels; j++) { float num4 = data[i + j] * gain; if (num4 < 0f) { num4 = 0f - num4; } if (num4 > num3) { num3 = num4; } } float num5 = ((num3 > 0.891f) ? (0.891f / num3) : 1f); if (num5 < num) { num = num5; } else { num += (1f - num) * num2; if (num > num5) { num = num5; } } float num6 = gain * num; for (int k = 0; k < channels; k++) { data[i + k] *= num6; } } reduction = num; } } internal static class PrivateSpeakers { private sealed class Entry { internal GameObject Go; internal Speaker Speaker; internal AudioSource Source; internal PrivateSpeakerGain Gain; internal int Actor; internal volatile float Amplitude; internal volatile int LastFrameTick; } private const int AmplitudeStaleMs = 300; private static readonly Dictionary<int, Entry> ByPlayerId = new Dictionary<int, Entry>(); private static readonly HashSet<Speaker> AmplitudeAttached = new HashSet<Speaker>(); private static float volume = 1f; internal static Speaker? Create(int playerId, object userData) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown if (!(userData is int num) || num <= 0) { Plugin.Logger.LogWarning((object)$"Private voice: stream {playerId} carried no usable actor claim - refusing"); return null; } if (!PrivateVoiceChannel.IsMemberActor(num)) { Plugin.Logger.LogWarning((object)$"Private voice: stream from non-member actor {num} - refusing to play"); return null; } foreach (KeyValuePair<int, Entry> item in ByPlayerId) { if (item.Key != playerId && item.Value.Actor == num) { Plugin.Logger.LogWarning((object)$"Private voice: actor {num} already claimed by another stream - refusing"); return null; } } if (ByPlayerId.TryGetValue(playerId, out Entry value)) { return value.Speaker; } GameObject val = new GameObject($"SP_PrivSpeaker_a{num}"); Object.DontDestroyOnLoad((Object)(object)val); AudioSource val2 = val.AddComponent<AudioSource>(); val2.spatialBlend = 0f; val2.mute = false; val2.dopplerLevel = 0f; val2.priority = 0; val2.outputAudioMixerGroup = null; val2.bypassEffects = false; val2.bypassListenerEffects = true; val2.bypassReverbZones = true; PrivateSpeakerGain gain = val.AddComponent<PrivateSpeakerGain>(); Speaker speaker = val.AddComponent<Speaker>(); Entry entry = new Entry { Go = val, Speaker = speaker, Source = val2, Gain = gain, Actor = num }; ByPlayerId[playerId] = entry; Apply(entry, volume); speaker.OnRemoteVoiceRemoveAction = delegate(Speaker s) { ByPlayerId.Remove(playerId); AmplitudeAttached.Remove(speaker); if ((Object)(object)s != (Object)null && (Object)(object)((Component)s).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)s).gameObject); } }; Plugin.Logger.LogInfo((object)$"Private voice: speaker for actor {num}"); return speaker; } internal static void AttachAmplitude(Speaker speaker) { if ((Object)(object)speaker == (Object)null) { return; } RemoteVoiceLink remoteVoice = speaker.RemoteVoice; if (remoteVoice == null) { return; } Entry entry = null; foreach (KeyValuePair<int, Entry> item in ByPlayerId) { if (item.Value.Speaker == speaker) { entry = item.Value; break; } } if (entry == null || !AmplitudeAttached.Add(speaker)) { return; } remoteVoice.FloatFrameDecoded += delegate(FrameOut<float> frame) { float[] buf = frame.Buf; if (buf != null && buf.Length != 0) { float num = 0f; for (int i = 0; i < buf.Length; i++) { num += ((buf[i] < 0f) ? (0f - buf[i]) : buf[i]); } entry.Amplitude = num / (float)buf.Length; entry.LastFrameTick = Environment.TickCount; } }; } internal static void DestroyAll() { foreach (KeyValuePair<int, Entry> item in ByPlayerId) { Entry value = item.Value; if ((Object)(object)value.Speaker != (Object)null) { value.Speaker.OnRemoteVoiceRemoveAction = null; } if ((Object)(object)value.Go != (Object)null) { Object.Destroy((Object)(object)value.Go); } } ByPlayerId.Clear(); AmplitudeAttached.Clear(); } internal static float AmplitudeFor(int actorNumber) { foreach (KeyValuePair<int, Entry> item in ByPlayerId) { if (item.Value.Actor == actorNumber) { Entry value = item.Value; if (Environment.TickCount - value.LastFrameTick > 300) { return 0f; } return value.Amplitude; } } return 0f; } internal static void SetVolume(float value) { if (value < 0f) { value = 0f; } float num = 3f; if (value > num) { value = num; } if (value == volume) { return; } volume = value; foreach (KeyValuePair<int, Entry> item in ByPlayerId) { Apply(item.Value, value); } } private static void Apply(Entry entry, float value) { if ((Object)(object)entry.Source != (Object)null) { entry.Source.volume = ((value > 1f) ? 1f : value); } if ((Object)(object)entry.Gain != (Object)null) { entry.Gain.Gain = ((value > 1f) ? value : 1f); } } } internal sealed class PrivateSpeakerGain : MonoBehaviour { private volatile float gain = 1f; private readonly PeakLimiter limiter = new PeakLimiter(); internal float Gain { get { return gain; } set { gain = ((value < 1f) ? 1f : value); } } private void Awake() { limiter.Configure(AudioSettings.outputSampleRate); } private void OnAudioFilterRead(float[] data, int channels) { float num = gain; if (num == 1f) { limiter.Reset(); } else { limiter.Process(data, channels, num); } } } internal sealed class PrivateVoiceClient : VoiceFollowClient { private const float ConnectWarnCooldownSeconds = 5f; private const float ConnectRetryCooldownSeconds = 3f; private const string MutedPropKey = "spm"; private static bool quitting; private string token = string.Empty; private bool? publishedMuted; private Recorder? recorder; private int userDataActor; private float nextConnectWarnAt; private float nextConnectAttemptAt; private const int OpusBitrate = 64000; internal static PrivateVoiceClient? Instance { get; private set; } internal static bool EverCreated { get; private set; } internal Recorder? PrivateRecorder => recorder; protected override bool LeaderInRoom { get { if (PhotonNetwork.InRoom) { return PrivateVoiceChannel.IsValidRoomName(token); } return false; } } protected override bool LeaderOfflineMode => PhotonNetwork.OfflineMode; internal static void Init() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown if (!((Object)(object)Instance != (Object)null)) { GameObject val = new GameObject("SP_PrivateVoice") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)val); Instance = val.AddComponent<PrivateVoiceClient>(); ((VoiceConnection)Instance).SpeakerLinked += PrivateSpeakers.AttachAmplitude; EverCreated = true; Application.quitting += delegate { quitting = true; }; Plugin.Logger.LogInfo((object)"Private voice client created"); } } internal void Provision(string roomName) { if (!PrivateVoiceChannel.IsValidRoomName(roomName)) { Plugin.Logger.LogWarning((object)"Private voice: refusing a malformed room name"); return; } PrivateVoiceGate.WantCapture = true; bool flag = PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null && PhotonNetwork.LocalPlayer.ActorNumber > 0; if ((Object)(object)recorder == (Object)null) { if (flag && MicTee.IsLive) { CreateRecorder(); } } else if (flag) { SyncUserData(recorder); } bool flag2 = ((VoiceConnection)this).Client != null && ((LoadBalancingClient)((VoiceConnection)this).Client).IsConnected; if (roomName == token && flag2) { return; } if (token.Length > 0 && roomName != token) { token = string.Empty; SafeDisconnect(); return; } if (token.Length == 0) { if (flag2) { return; } token = roomName; } if (!(((VoiceConnection)this).Client == null || flag2) && PhotonNetwork.InRoom && !(Time.unscaledTime < nextConnectAttemptAt)) { nextConnectAttemptAt = Time.unscaledTime + 3f; if (!((VoiceFollowClient)this).ConnectAndJoinRoom() && Time.unscaledTime >= nextConnectWarnAt) { nextConnectWarnAt = Time.unscaledTime + 5f; Plugin.Logger.LogWarning((object)("Private voice: connect/join refused for room " + PrivateVoiceChannel.Fingerprint(roomName))); } } } protected override void Start() { ((VoiceFollowClient)this).Start(); if (PhotonNetwork.NetworkingClient != null) { PhotonNetwork.NetworkingClient.StateChanged += OnLeaderStateChanged; } } private void OnLeaderStateChanged(ClientState fromState, ClientState toState) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) ((VoiceFollowClient)this).LeaderStateChanged(toState); } protected override void OnDestroy() { if (Instance == this && !quitting) { Plugin.Logger.LogWarning((object)"Private voice: SP_PrivateVoice is being destroyed - the private channel cannot run again this session"); } if (PhotonNetwork.NetworkingClient != null) { PhotonNetwork.NetworkingClient.StateChanged -= OnLeaderStateChanged; } ((VoiceFollowClient)this).OnDestroy(); } private void CreateRecorder() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SP_PrivateRecorder"); val.transform.SetParent(((Component)this).transform, false); Recorder val2 = val.AddComponent<Recorder>(); PrivateVoiceDsp.Mirror(val); val2.SourceType = (InputSourceType)2; val2.MicrophoneType = (MicType)1; val2.InputFactory = () => (IAudioDesc)(object)new TeeAudioReader(); userDataActor = PhotonNetwork.LocalPlayer.ActorNumber; val2.UserData = userDataActor; val2.TransmitEnabled = false; val2.VoiceDetection = false; val2.Encrypt = true; val2.DebugEchoMode = false; val2.InterestGroup = 0; val2.TargetPlayers = null; val2.SamplingRate = NearestSupportedRate(MicTee.SamplingRate); val2.Bitrate = 64000; ((VoiceConnection)this).PrimaryRecorder = val2; if (!((VoiceConnection)this).AddRecorder(val2)) { Plugin.Logger.LogWarning((object)"Private voice: AddRecorder refused - the recorder will never transmit"); Object.Destroy((Object)(object)val); } else { recorder = val2; Plugin.Logger.LogInfo((object)$"Private voice recorder ready ({MicTee.SamplingRate} Hz, {MicTee.Channels} ch, Opus {64} kbps)"); } } private void SyncUserData(Recorder rec) { int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber; if (actorNumber != userDataActor) { userDataActor = actorNumber; rec.UserData = actorNumber; Plugin.Logger.LogInfo((object)"Private voice: local actor number changed, rebinding recorder identity"); } } private static SamplingRate NearestSupportedRate(int hz) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) if (hz <= 20000) { if (hz > 10000) { if (hz <= 14000) { return (SamplingRate)12000; } return (SamplingRate)16000; } return (SamplingRate)8000; } if (hz <= 36000) { return (SamplingRate)24000; } return (SamplingRate)48000; } internal void PublishMuted(bool muted) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown if (((VoiceConnection)this).Client == null || (int)((LoadBalancingClient)((VoiceConnection)this).Client).State != 9 || ((LoadBalancingClient)((VoiceConnection)this).Client).LocalPlayer == null) { publishedMuted = null; } else if (publishedMuted != muted) { publishedMuted = muted; ((LoadBalancingClient)((VoiceConnection)this).Client).LocalPlayer.SetCustomProperties(new Hashtable { [(object)"spm"] = muted }, (Hashtable)null, (WebFlags)null); } } internal bool RemoteMuted(int actorNumber) { LoadBalancingTransport client = ((VoiceConnection)this).Client; object obj; if (client == null) { obj = null; } else { Room currentRoom = ((LoadBalancingClient)client).CurrentRoom; obj = ((currentRoom != null) ? currentRoom.Players : null); } Dictionary<int, Player> dictionary = (Dictionary<int, Player>)obj; if (dictionary == null) { return false; } PlayerRegistry instance = PlayerRegistry.Instance; if (instance == null) { return false; } string text = instance.TryGetSteamId(actorNumber); if (string.IsNullOrEmpty(text)) { return false; } bool flag = default(bool); foreach (KeyValuePair<int, Player> item in dictionary) { Player value = item.Value; if (value == null || value.IsLocal || value.IsInactive || value.UserId != text) { continue; } int num; if (value.CustomProperties != null && ((Dictionary<object, object>)(object)value.CustomProperties).TryGetValue((object)"spm", out object value2)) { if (value2 is bool) { flag = (bool)value2; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } return false; } internal bool ChannelLive(string expectedRoom) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 if (((VoiceConnection)this).Client == null || (int)((LoadBalancingClient)((VoiceConnection)this).Client).State != 9) { return false; } Room currentRoom = ((LoadBalancingClient)((VoiceConnection)this).Client).CurrentRoom; if (currentRoom != null && currentRoom.Name == expectedRoom) { return PrivateVoiceChannel.IsValidRoomName(currentRoom.Name); } return false; } private void SafeDisconnect() { if (((VoiceConnection)this).Client != null && ((LoadBalancingClient)((VoiceConnection)this).Client).IsConnected) { ((VoiceFollowClient)this).Disconnect(); } } internal void TearDown() { if ((Object)(object)recorder != (Object)null) { recorder.TransmitEnabled = false; } PrivateSpeakers.DestroyAll(); SafeDisconnect(); PrivateVoiceGate.WantCapture = false; MicTee.Flush(); token = string.Empty; nextConnectWarnAt = 0f; nextConnectAttemptAt = 0f; } protected override string GetVoiceRoomName() { return token; } protected override bool ConnectVoice() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0049: 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_00b6: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0076: Expected O, but got Unknown AppSettings val = null; ((LoadBalancingClient)((VoiceConnection)this).Client).ServerPortOverrides = PhotonNetwork.ServerPortOverrides; val = PhotonNetwork.PhotonServerSettings.AppSettings.CopyTo(new AppSettings()); if (!string.IsNullOrEmpty(PhotonNetwork.CloudRegion)) { val.FixedRegion = PhotonNetwork.CloudRegion; } ((LoadBalancingClient)((VoiceConnection)this).Client).SerializationProtocol = PhotonNetwork.NetworkingClient.SerializationProtocol; if (PhotonNetwork.AuthValues != null) { LoadBalancingTransport client = ((VoiceConnection)this).Client; if (((LoadBalancingClient)client).AuthValues == null) { AuthenticationValues val2 = new AuthenticationValues(); AuthenticationValues val3 = val2; ((LoadBalancingClient)client).AuthValues = val2; } ((LoadBalancingClient)((VoiceConnection)this).Client).AuthValues = PhotonNetwork.AuthValues.CopyTo(((LoadBalancingClient)((VoiceConnection)this).Client).AuthValues); } ((LoadBalancingClient)((VoiceConnection)this).Client).AuthMode = PhotonNetwork.NetworkingClient.AuthMode; ((LoadBalancingClient)((VoiceConnection)this).Client).EncryptionMode = PhotonNetwork.NetworkingClient.EncryptionMode; return ((VoiceConnection)this).ConnectUsingSettings(val); } protected override bool JoinVoiceRoom(string voiceRoomName) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_002c: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_006a: Expected O, but got Unknown //IL_006b: Expected O, but got Unknown if (!PrivateVoiceChannel.IsValidRoomName(voiceRoomName)) { Plugin.Logger.LogWarning((object)"Private voice: join refused, malformed room name"); return false; } RoomOptions roomOptions = new RoomOptions { IsVisible = false, IsOpen = true, MaxPlayers = 0, PlayerTtl = 2000, PublishUserId = true }; EnterRoomParams val = new EnterRoomParams { RoomName = voiceRoomName, RoomOptions = roomOptions, Lobby = new TypedLobby("spv", (LobbyType)0) }; Plugin.Logger.LogInfo((object)("Private voice: joining room " + PrivateVoiceChannel.Fingerprint(voiceRoomName))); return ((LoadBalancingClient)((VoiceConnection)this).Client).OpJoinOrCreateRoom(val); } protected override void OnOperationResponseReceived(OperationResponse operationResponse) { if (operationResponse.ReturnCode == 0) { ((VoiceFollowClient)this).OnOperationResponseReceived(operationResponse); return; } Plugin.Logger.LogWarning((object)$"Private voice: op {operationResponse.OperationCode} failed ({operationResponse.ReturnCode})"); if (operationResponse.OperationCode != 226) { ((VoiceFollowClient)this).OnOperationResponseReceived(operationResponse); return; } string roomName = token; try { token = PrivateVoiceChannel.Fingerprint(roomName); ((VoiceFollowClient)this).OnOperationResponseReceived(operationResponse); } finally { token = roomName; } } protected override Speaker? InstantiateSpeakerForRemoteVoice(int playerId, byte voiceId, object userData) { try { return PrivateSpeakers.Create(playerId, userData); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Private voice: speaker construction failed: {arg}"); return null; } } } internal static class PrivateVoiceDriver { internal enum Health { Off, Provisioning, Live, Listening, Failed } private const float ReleaseGraceSeconds = 0.3f; private const float IntrusionConfirmSeconds = 2f; private const float MissingClientWarnCooldownSeconds = 5f; private static bool lastVoiceJoined; private static float releaseAt; private static float nextMissingClientWarnAt; private static string provisionedRoom = string.Empty; private static float unexpectedOccupantFirstSeenAt = -1f; private static PlayerVoiceChat? gameRecorderOwner; private static Recorder? gameRecorder; internal static Health State { get; private set; } = Health.Off; internal static void Tick() { int num = ((PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null) ? PhotonNetwork.LocalPlayer.ActorNumber : 0); PrivateVoiceChannel.SetLocalMembership(num > 0 && PrivateVoiceChannel.IsMemberActor(num)); bool localActive = PrivateVoiceChannel.LocalActive; if (!localActive && provisionedRoom.Length > 0) { TearDown("mode off"); } RecorderTransmitPatch.Suppress = localActive || Time.realtimeSinceStartup < releaseAt; if (!localActive) { if (Time.realtimeSinceStartup < releaseAt) { ForceGameRecorderOff(); } State = Health.Off; return; } ForceGameRecorderOff(); PrivateSpeakers.SetVolume(GameVolume() * PrivateVoiceLocalPrefs.Factor); PrivateVoiceClient instance = PrivateVoiceClient.Instance; if ((Object)(object)instance != (Object)null) { instance.PublishMuted(LocalDisplayMuted()); string roomName = PrivateVoiceChannel.RoomName; if (provisionedRoom != roomName) { if (provisionedRoom.Length > 0) { TearDown("room rotated"); } else { MicTee.Flush(); } instance.Provision(roomName); provisionedRoom = roomName; State = Health.Provisioning; return; } if ((Object)(object)instance.PrivateRecorder == (Object)null) { instance.Provision(roomName); bool flag = instance.ChannelLive(roomName); if (!lastVoiceJoined && flag) { MicTee.Flush(); } lastVoiceJoined = flag; State = ((!flag) ? Health.Provisioning : Health.Listening); return; } bool flag2 = instance.ChannelLive(roomName); if (!lastVoiceJoined && flag2) { MicTee.Flush(); } if (lastVoiceJoined && !flag2) { Plugin.Logger.LogInfo((object)"Private voice: channel dropped, re-provisioning"); } lastVoiceJoined = flag2; if (!flag2) { instance.Provision(roomName); SetPrivateTransmit(on: false); State = Health.Provisioning; return; } AuditOccupants(); if (State == Health.Failed) { return; } Recorder privateRecorder = instance.PrivateRecorder; if ((Object)(object)privateRecorder != (Object)null && privateRecorder.RecordingEnabled) { if (privateRecorder.InterestGroup != 0) { privateRecorder.InterestGroup = 0; } if (privateRecorder.TargetPlayers != null) { privateRecorder.TargetPlayers = null; } } SetPrivateTransmit(GameMicAllows()); State = Health.Live; } else { if (Time.realtimeSinceStartup >= nextMissingClientWarnAt) { nextMissingClientWarnAt = Time.realtimeSinceStartup + 5f; Plugin.Logger.LogWarning((object)(PrivateVoiceClient.EverCreated ? "Private voice: the voice client was created at startup but no longer exists - something destroyed the SP_PrivateVoice object, so the channel cannot run" : "Private voice: the voice client was never created - PrivateVoiceClient.Init did not run or threw")); } State = Health.Failed; } } private static void AuditOccupants() { PrivateVoiceClient? instance = PrivateVoiceClient.Instance; object obj; if (instance == null) { obj = null; } else { LoadBalancingTransport client = ((VoiceConnection)instance).Client; if (client == null) { obj = null; } else { Room currentRoom = ((LoadBalancingClient)client).CurrentRoom; obj = ((currentRoom != null) ? currentRoom.Players : null); } } Dictionary<int, Player> dictionary = (Dictionary<int, Player>)obj; if (dictionary == null) { return; } PlayerRegistry instance2 = PlayerRegistry.Instance; if (instance2 == null) { return; } int[] array = PrivateVoiceChannel.MemberActors(); for (int i = 0; i < array.Length; i++) { if (instance2.TryGetSteamId(array[i]) == null) { return; } } bool flag = false; foreach (KeyValuePair<int, Player> item in dictionary) { Player value = item.Value; if (value == null || value.IsLocal || value.IsInactive) { continue; } string userId = value.UserId; if (string.IsNullOrEmpty(userId)) { continue; } bool flag2 = false; for (int j = 0; j < array.Length; j++) { if (instance2.TryGetSteamId(array[j]) == userId) { flag2 = true; break; } } if (!flag2) { flag = true; break; } } if (!flag) { unexpectedOccupantFirstSeenAt = -1f; return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (unexpectedOccupantFirstSeenAt < 0f) { unexpectedOccupantFirstSeenAt = realtimeSinceStartup; } else if (!(realtimeSinceStartup - unexpectedOccupantFirstSeenAt < 2f)) { Plugin.Logger.LogWarning((object)"Private voice: unexpected occupant persisted - muting the private channel"); SetPrivateTransmit(on: false); State = Health.Failed; } } private static bool GameMicAllows() { PlayerVoiceChat instance = PlayerVoiceChat.instance; if ((Object)(object)instance == (Object)null) { return false; } if (!instance.microphoneEnabled) { return false; } if ((Object)(object)DataDirector.instance == (Object)null) { return false; } if (DataDirector.instance.toggleMute) { return false; } if ((Object)(object)AudioManager.instance == (Object)null) { return false; } if (AudioManager.instance.pushToTalk && !SemiFunc.InputHold((InputKey)25)) { return false; } return true; } internal static bool LocalDisplayMuted() { PlayerVoiceChat instance = PlayerVoiceChat.instance; if ((Object)(object)instance == (Object)null || !instance.microphoneEnabled) { return true; } if (!((Object)(object)DataDirector.instance == (Object)null)) { return DataDirector.instance.toggleMute; } return true; } private static float GameVolume() { if ((Object)(object)DataDirector.instance == (Object)null) { return 1f; } float num = (float)DataDirector.instance.SettingValueFetch((Setting)13) * 0.01f; float num2 = (float)DataDirector.instance.SettingValueFetch((Setting)4) * 0.01f; float num3 = num * num2; if (num3 < 0f) { num3 = 0f; } if (num3 > 1f) { num3 = 1f; } return num3; } private static void SetPrivateTransmit(bool on) { Recorder val = PrivateVoiceClient.Instance?.PrivateRecorder; if ((Object)(object)val != (Object)null && val.TransmitEnabled != on) { val.TransmitEnabled = on; } } private static void ForceGameRecorderOff() { PlayerVoiceChat instance = PlayerVoiceChat.instance; if (!((Object)(object)instance == (Object)null)) { if ((Object)(object)gameRecorder == (Object)null || instance != gameRecorderOwner) { gameRecorderOwner = instance; gameRecorder = ((Component)instance).GetComponent<Recorder>(); } if ((Object)(object)gameRecorder != (Object)null) { RecorderTransmitPatch.ForceOff(gameRecorder); } } } internal static void TearDown(string reason) { if (provisionedRoom.Length != 0 || State != Health.Off) { Plugin.Logger.LogInfo((object)("Private voice: tearing down (" + reason + ")")); SetPrivateTransmit(on: false); PrivateVoiceClient.Instance?.TearDown(); provisionedRoom = string.Empty; lastVoiceJoined = false; unexpectedOccupantFirstSeenAt = -1f; State = Health.Off; releaseAt = Time.realtimeSinceStartup + 0.3f; } } } internal static class PrivateVoiceLocalPrefs { internal const int MaxVolumePercent = 300; internal static int VolumePercent => ModConfig.PrivateChannelVolume.Value; internal static int MicLiftDb { get { int value = ModConfig.PrivateChannelMicGain.Value; if (value >= 0) { if (value <= 40) { return value; } return 40; } return 0; } } internal static float Factor => (float)VolumePercent * 0.01f; internal static void SetVolumePercent(int value) { int num = ((value >= 0) ? ((value > 300) ? 300 : value) : 0); if (ModConfig.PrivateChannelVolume.Value != num) { ModConfig.PrivateChannelVolume.Value = num; } } internal static void SetMicLiftDb(int value) { int num = ((value >= 0) ? ((value > 40) ? 40 : value) : 0); if (ModConfig.PrivateChannelMicGain.Value != num) { ModConfig.PrivateChannelMicGain.Value = num; } } } internal static class PrivateVoiceDsp { internal const int DefaultMicGainDb = 20; internal const int MaxMicGainDb = 40; private static WebRtcAudioDsp? live; private static bool subscribed; internal static string LastResult { get; private set; } = "not attempted"; private static int MicGainDb { get { int value = ModConfig.PrivateChannelMicGain.Value; if (value >= 0) { if (value <= 40) { return value; } return 40; } return 0; } } internal static void Mirror(GameObject recorderObject) { PlayerVoiceChat instance = PlayerVoiceChat.instance; WebRtcAudioDsp val = (((Object)(object)instance != (Object)null) ? ((Component)instance).GetComponent<WebRtcAudioDsp>() : null); if ((Object)(object)val == (Object)null) { LastResult = "not mirrored (the game's recorder carries no WebRtcAudioDsp)"; Plugin.Logger.LogWarning((object)"Private voice: the game's recorder carries no WebRtcAudioDsp - the private stream keeps the raw microphone level and will stay quieter than the public channel"); return; } if (MicTee.Channels != 1) { LastResult = $"skipped (capture is {MicTee.Channels} ch, WebRtcAudioDsp is mono-only)"; Plugin.Logger.LogWarning((object)$"Private voice: capture is {MicTee.Channels} ch and WebRtcAudioDsp is mono-only - the DSP mirror is skipped and the private stream keeps the raw microphone level"); return; } WebRtcAudioDsp val2 = recorderObject.AddComponent<WebRtcAudioDsp>(); val2.AEC = val.AEC; val2.AecHighPass = val.AecHighPass; val2.ReverseStreamDelayMs = val.ReverseStreamDelayMs; val2.HighPass = val.HighPass; val2.NoiseSuppression = val.NoiseSuppression; val2.AGC = val.AGC; val2.AgcTargetLevel = val.AgcTargetLevel; val2.Bypass = val.Bypass; val2.AgcCompressionGain = MicGainDb; live = val2; if (!subscribed) { subscribed = true; ModConfig.PrivateChannelMicGain.SettingChanged += delegate { ApplyMicGainToLive(); }; } val2.VAD = false; LastResult = Describe(val2); Plugin.Logger.LogInfo((object)$"Private voice: DSP mirrored from the game's recorder (AGC {OnOff(val2.AGC)} target {val2.AgcTargetLevel} dBFS / max lift {val2.AgcCompressionGain} dB (game: {val.AgcCompressionGain}), noise suppression {OnOff(val2.NoiseSuppression)}, high pass {OnOff(val2.HighPass)}, AEC {OnOff(val2.AEC)}, VAD off by design, bypass {OnOff(val2.Bypass)})"); } private static void ApplyMicGainToLive() { WebRtcAudioDsp val = live; if (!((Object)(object)val == (Object)null)) { val.AgcCompressionGain = MicGainDb; LastResult = Describe(val); Plugin.Logger.LogInfo((object)$"Private voice: mic lift now up to {val.AgcCompressionGain} dB"); } } private static string Describe(WebRtcAudioDsp dsp) { return $"mirrored (AGC {OnOff(dsp.AGC)}, mic lift up to {dsp.AgcCompressionGain} dB, noise suppression {OnOff(dsp.NoiseSuppression)}, AEC {OnOff(dsp.AEC)})"; } private static string OnOff(bool value) { if (!value) { return "off"; } return "on"; } } internal static class PrivateVoicePresenceReporter { private static int lastReportedEpoch = -1; private static bool lastReportedConnected; internal static bool IsConnected(PrivateVoiceDriver.Health state) { if (state != PrivateVoiceDriver.Health.Live) { return state == PrivateVoiceDriver.Health.Listening; } return true; } internal static void Report(PrivateVoiceDriver.Health state) { if (!PrivateVoiceChannel.Active || !PrivateVoiceChannel.LocalActive) { lastReportedEpoch = -1; lastReportedConnected = false; return; } int epoch = PrivateVoiceChannel.Epoch; bool flag = IsConnected(state); if (epoch != lastReportedEpoch || flag != lastReportedConnected) { if (SemiFunc.IsMasterClient()) { lastReportedEpoch = epoch; lastReportedConnected = flag; PrivateVoiceSync.NoteLocalPresence(flag); } else if (CommandSender.CanSend && CommandSender.Send("PrivateVoicePresence", new object[2] { epoch.ToString(), flag ? "1" : "0" }, (ReceiverGroup)2)) { lastReportedEpoch = epoch; lastReportedConnected = flag; } } } internal static void Reset() { lastReportedEpoch = -1; lastReportedConnected = false; } } internal sealed class TeeAudioReader : IAudioReader<float>, IDataReader<float>, IDisposable, IAudioDesc { public int SamplingRate => MicTee.SamplingRate; public int Channels => MicTee.Channels; public string? Error { get { if (!MicTee.IsLive) { return "mic tee not live"; } return null; } } public bool Read(float[] buffer) { return MicTee.Ring.Read(buffer); } public void Dispose() { } } } namespace SharePermissions.UI { internal sealed class BandRow : MonoBehaviour { private sealed class Slot { internal RectTransform Rect; internal TextMeshProUGUI Label; internal KitButton? Button; internal int Order; internal string? Reserve; } private readonly List<Slot> slots = new List<Slot>(); private int settleFrames; internal static BandRow For(KitPopupPage page) { BandRow component = ((Component)page).GetComponent<BandRow>(); if (!((Object)(object)component != (Object)null)) { return ((Component)page).gameObject.AddComponent<BandRow>(); } return component; } internal void Add(KitButton button, int order, string? reserve = null) { Add(button.rectTransform, button.labelTMP, button, order, reserve); } internal void Add(KitLabel label, int order, string? reserve = null) { Add(label.rectTransform, label.labelTMP, null, order, reserve); } private void Add(RectTransform rect, TextMeshProUGUI label, KitButton? button, int order, string? reserve) { slots.Add(new Slot { Rect = rect, Label = label, Button = button, Order = order, Reserve = reserve }); slots.Sort((Slot a, Slot b) => a.Order.CompareTo(b.Order)); } internal void Relayout() { settleFrames = 2; Apply(); } private void LateUpdate() { if (settleFrames > 0) { settleFrames--; Apply(); } } private void Apply() { //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_0194: 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) slots.RemoveAll((Slot s) => (Object)(object)s.Rect == (Object)null || (Object)(object)s.Label == (Object)null); List<Slot> list = new List<Slot>(); foreach (Slot slot2 in slots) { if (((Component)slot2.Rect).gameObject.activeInHierarchy) { list.Add(slot2); } } if (list.Count == 0) { return; } Vector2[] array = (Vector2[])(object)new Vector2[list.Count]; float totalWidth = MeasureAll(list, 16f, array); float num = 6f; float num2 = ModStyle.BandRight - ModStyle.BandLeft; if (num2 <= 0f) { return; } float num3 = TabBandLayout.FitScale(totalWidth, num2 - num * (float)(list.Count - 1)); if (num3 < 1f) { MeasureAll(list, 16f * num3, array); } float[] array2 = new float[list.Count]; for (int num4 = 0; num4 < list.Count; num4++) { array2[num4] = array[num4].x; } float num5 = 0f; for (int num6 = 0; num6 < list.Count; num6++) { num5 = Mathf.Max(num5, array[num6].y); } float[] array3 = TabBandLayout.SpreadBandLeftEdges(array2, ModStyle.BandLeft, ModStyle.BandRight, num); for (int num7 = 0; num7 < list.Count; num7++) { Slot slot = list[num7]; if ((Object)(object)slot.Button != (Object)null) { MenuUiHelpers.PinButtonSize(slot.Button, new Vector2(array2[num7], num5)); } else { slot.Rect.sizeDelta = new Vector2(array2[num7], num5); ((TMP_Text)slot.Label).rectTransform.sizeDelta = new Vector2(array2[num7], num5); } ((Transform)slot.Rect).localPosition = new Vector3(array3[num7], 20f, 0f); } AlignCaptionsToOneLine(list); } private static void AlignCaptionsToOneLine(List<Slot> live) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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) if (live.Count < 2) { return; } float[] array = new float[live.Count]; for (int i = 0; i < live.Count; i++) { array[i] = CaptionCentre(live[i]); } float[] array2 = (float[])array.Clone(); Array.Sort(array2); float num = array2[array2.Length / 2]; for (int j = 0; j < live.Count; j++) { float num2 = num - array[j]; if (!(Mathf.Abs(num2) < 0.01f)) { RectTransform rect = live[j].Rect; Vector3 localPosition = ((Transform)rect).localPosition; localPosition.y = ((Transform)rect).localPosition.y + num2; ((Transform)rect).localPosition = localPosition; } } } private static float CaptionCentre(Slot s) { //IL_003e: 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) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //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_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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) TextMeshProUGUI label = s.Label; RectTransform rectTransform = ((TMP_Text)label).rectTransform; ((TMP_Text)label).ForceMeshUpdate(false, false); TMP_TextInfo textInfo = ((TMP_Text)label).textInfo; float num; if (textInfo != null && textInfo.lineCount > 0) { num = textInfo.lineInfo[0].baseline; } else { Bounds textBounds = ((TMP_Text)label).textBounds; if (((Bounds)(ref textBounds)).extents.y > 0f) { textBounds = ((TMP_Text)label).textBounds; num = ((Bounds)(ref textBounds)).center.y; } else { Rect rect = rectTransform.rect; num = ((Rect)(ref rect)).center.y; } } return ((Transform)s.Rect).localPosition.y + ((Transform)rectTransform).localPosition.y + num; } private static float MeasureAll(List<Slot> live, float fontSize, Vector2[] sizes) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) float num = 0f; for (int i = 0; i < live.Count; i++) { Slot slot = live[i]; ((TMP_Text)slot.Label).enableAutoSizing = false; ((TMP_Text)slot.Label).alignment = (TextAlignmentOptions)4097; ((TMP_Text)slot.Label).fontSize = fontSize; ((TMP_Text)slot.Label).ForceMeshUpdate(false, false); sizes[i] = ((TMP_Text)slot.Label).GetPreferredValues(slot.Reserve ?? ((TMP_Text)slot.Label).text); num += sizes[i].x; } return num; } } internal static class CapsLine { internal static string PermissionWord(string word, Language lang) { return word switch { "Kick" => Tr("Kicking", lang), "Ban" => Tr("Banning", lang), "Persistent ban" => Tr("Persistent ban", lang), "Notes" => Tr("Notes", lang), "Run control" => Tr("Run control", lang), "1-on-1 voice" => Tr("1-on-1 voice", lang), "Revive & heal" => Tr("Revive & heal", lang), _ => word, }; } private static string Tr(string key, Language lang) { return LocTable.Lookup(key, lang); } internal static string Build(ModeratorCaps caps, Func<string, string> translate, string heldHex, string lackingHex) { StringBuilder stringBuilder = new StringBuilder(); (ModeratorCaps, string)[] words = ModeratorCapsText.Words; for (int i = 0; i < words.Length; i++) { var (moderatorCaps, text) = words[i]; if (stringBuilder.Length > 0) { stringBuilder.Append("<color=" + lackingHex + "> · </color>"); } string text2 = ((moderatorCaps == ModeratorCaps.AdminMenu) ? text : translate(text)); stringBuilder.Append(((caps & moderatorCaps) != ModeratorCaps.None) ? ("<nobr><color=" + heldHex + ">" + text2 + "</color></nobr>") : ("<nobr><color=" + lackingHex + "><s>" + text2 + "</s></color></nobr>")); } return stringBuilder.ToString(); } } internal static class CaptionWidth { internal const int WideTenths = 28; private const int NarrowTenths = 10; internal const string Ellipsis = "…"; internal const int RowNameChars = 16; internal const int RowNameWidth = 22; internal const int LobbyTrim = 10; internal static bool IsWide(char c) { if ((c < 'ᄀ' || c > 'ᅟ') && (c < '⺀' || c > '〾') && (c < 'ぁ' || c > '㏿') && (c < '㐀' || c > '䶿') && (c < '一' || c > '鿿') && (c < 'ꥠ' || c > '\ua97f') && (c < '가' || c > '힣') && (c < '豈' || c > '\ufaff') && (c < '︰' || c > '\ufe4f') && (c < '!' || c > '⦆')) { if (c >= '¢') { return c <= '₩'; } return false; } return true; } internal static int Tenths(string text) { int num = 0; foreach (char c in text) { num += (IsWide(c) ? 28 : 10); } return num; } internal static string Fit(string text, int budget) { if (Tenths(text) <= budget * 10) { return text; } int num = (budget - 1) * 10; int num2 = 0; int i; for (i = 0; i < text.Length; i++) { int num3 = num2 + (IsWide(text[i]) ? 28 : 10); if (num3 > num) { break; } num2 = num3; } if (i > 0 && char.IsHighSurrogate(text[i - 1])) { i--; } return text.Substring(0, i) + "…"; } internal static string Fit(string lead, string text, float budget, Func<string, float> measure) { if (measure(lead + text) <= budget) { return text; } int num = 0; int num2 = text.Length - 1; int num3 = 0; while (num <= num2) { int num4 = (num + num2) / 2; if (measure(lead + text.Substring(0, num4) + "…") <= budget) { num3 = num4; num = num4 + 1; } else { num2 = num4 - 1; } } if (num3 > 0 && char.IsHighSurrogate(text[num3 - 1])) { num3--; } return text.Substring(0, num3) + "…"; } internal static string Fit(string text, int units, int width) { if (text.Length > units) { int num = units - 1; if (num > 0 && char.IsHighSurrogate(text[num - 1])) { num--; } text = text.Substring(0, num) + "…"; } return Fit(text, width); } internal static string FitRowName(string name) { return Fit(name, 16, 22); } internal static string FitLobby(string label) { return Fit(label, 10, 10); } } internal sealed class ChipGrid { private const float Gap = 6f; private const float Padding = 4f; private const float CornerRadius = 4f; private static readonly Color PickedFill = new Color(ModStyle.RoleModerator.r, ModStyle.RoleModerator.g, ModStyle.RoleModerator.b, 0.28f); private static readonly Color RestFill = new Color(1f, 1f, 1f, 0.06f); internal readonly RectTransform rectTransform; private readonly List<KitButton> chips = new List<KitButton>(); private readonly List<RoundedBoxGraphic> boxes = new List<RoundedBoxGraphic>(); private readonly List<string> captions = new List<string>(); private readonly List<bool> picked = new List<bool>(); private readonly List<Color?> tints = new List<Color?>(); private readonly float width; private readonly int columns; private readonly float cellHeight; private readonly float fontSize; private ChipGrid(RectTransform rect, float width, int columns, float cellHeight, float fontSize) { rectTransform = rect; this.width = width; this.columns = columns; this.cellHeight = cellHeight; this.fontSize = fontSize; } internal static ChipGrid Create(Transform parent, float width, int columns, float cellHeight, float fontSize, int count, Action<int> onPick) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Expected O, but got Unknown //IL_0110: 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_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0176: 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_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("ChipGrid", new Type[1] { typeof(RectTransform) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(parent, false); Vector2 anchorMin = (val2.anchorMax = Vector2.zero); val2.anchorMin = anchorMin; val2.pivot = Vector2.zero; val2.sizeDelta = new Vector2(width, cellHeight); ChipGrid chipGrid = new ChipGrid(val2, width, columns, cellHeight, fontSize); for (int i = 0; i < count; i++) { int index = i; KitButton kitButton = MenuKit.CreateREPOButton("", delegate { onPick(index); }, (Transform)(object)val2); GameObject val3 = new GameObject("Pill", new Type[2] { typeof(RectTransform), typeof(RoundedBoxGraphic) }); RectTransform val4 = (RectTransform)val3.transform; ((Transform)val4).SetParent((Transform)(object)kitButton.rectTransform, false); ((Transform)val4).SetAsFirstSibling(); val4.anchorMin = Vector2.zero; val4.anchorMax = Vector2.one; anchorMin = (val4.offsetMax = Vector2.zero); val4.offsetMin = anchorMin; RoundedBoxGraphic component = val3.GetComponent<RoundedBoxGraphic>(); ((Graphic)component).raycastTarget = false; component.Radius = 4f; TextMeshProUGUI labelTMP = kitButton.labelTMP; ((TMP_Text)labelTMP).richText = true; ((TMP_Text)labelTMP).enableAutoSizing = false; ((TMP_Text)labelTMP).margin = Vector4.zero; ((TMP_Text)labelTMP).alignment = (TextAlignmentOptions)514; ((TMP_Text)labelTMP).enableWordWrapping = false; ((TMP_Text)labelTMP).overflowMode = (TextOverflowModes)0; RectTransform val5 = ((TMP_Text)labelTMP).rectTransform; anchorMin = (val5.anchorMax = Vector2.zero); val5.anchorMin = anchorMin; val5.pivot = Vector2.zero; ((Transform)val5).localPosition = Vector3.zero; chipGrid.chips.Add(kitButton); chipGrid.boxes.Add(component); chipGrid.captions.Add(""); chipGrid.picked.Add(item: false); chipGrid.tints.Add(null); } return chipGrid; } internal void SetChip(int index, string caption, bool visible, bool isPicked, Color? tint = null) { captions[index] = caption; picked[index] = isPicked; tints[index] = tint; KitButton kitButton = chips[index]; if (((Component)kitButton).gameObject.activeSelf != visible) { ((Component)kitButton).gameObject.SetActive(visible); } } internal void Relayout() { //IL_0107: 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) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_0246: 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_0273: 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_0206: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020d: 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_021b: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) List<int> list = new List<int>(); float num = 0f; for (int i = 0; i < chips.Count; i++) { if (((Component)chips[i]).gameObject.activeSelf) { list.Add(i); TextMeshProUGUI labelTMP = chips[i].labelTMP; ((TMP_Text)labelTMP).fontSize = fontSize; ((TMP_Text)labelTMP).text = captions[i]; ((TMP_Text)labelTMP).ForceMeshUpdate(false, false); num = Mathf.Max(num, chips[i].GetLabelSize().x); } } float num2 = ChipGridLayout.CellWidth(width, columns, 6f); float num3 = ChipGridLayout.FitFont(fontSize, num, num2, 4f); float height; (float, float)[] array = ChipGridLayout.Cells(list.Count, columns, width, cellHeight, 6f, out height); rectTransform.sizeDelta = new Vector2(width, height); for (int j = 0; j < list.Count; j++) { int index = list[j]; KitButton kitButton = chips[index]; Color? val = tints[index]; object obj; if (!val.HasValue) { obj = (picked[index] ? "#66b2ff" : "#8a94a0"); } else { Color valueOrDefault = val.GetValueOrDefault(); obj = "#" + ColorUtility.ToHtmlStringRGB(valueOrDefault); } string text = (string)obj; ((TMP_Text)kitButton.labelTMP).fontSize = num3; ((TMP_Text)kitButton.labelTMP).text = "<color=" + text + ">" + captions[index] + "</color>"; RoundedBoxGraphic roundedBoxGraphic = boxes[index]; ? color; if (picked[index]) { if (val.HasValue) { Color valueOrDefault2 = val.GetValueOrDefault(); color = new Color(valueOrDefault2.r, valueOrDefault2.g, valueOrDefault2.b, PickedFill.a); } else { color = PickedFill; } } else { color = RestFill; } ((Graphic)roundedBoxGraphic).color = (Color)color; MenuUiHelpers.PinButtonSize(kitButton, new Vector2(num2, cellHeight)); RectTransform val2 = ((TMP_Text)kitButton.labelTMP).rectTransform; val2.sizeDelta = new Vector2(num2, cellHeight); ((Transform)val2).localPosition = Vector3.zero; ((Transform)kitButton.rectTransform).localPosition = new Vector3(array[j].Item1, array[j].Item2, 0f); } } } internal static class ChipGridLayout { internal static float CellWidth(float width, int columns, float gap) { if (columns > 0) { return (width - gap * (float)(columns - 1)) / (float)columns; } return 0f; } internal static (float X, float Y)[] Cells(int count, int columns, float width, float cellHeight, float gap, out float height) { if (count <= 0 || columns <= 0) { height = 0f; return new(float, float)[0]; } int num = (count + columns - 1) / columns; height = (float)num * cellHeight + (float)(num - 1) * gap; float num2 = CellWidth(width, columns, gap); (float, float)[] array = new(float, float)[count]; for (int i = 0; i < count; i++) { int num3 = i % columns; int num4 = i / columns; array[i] = ((float)num3 * (num2 + gap), height - (float)(num4 + 1) * cellHeight - (float)num4 * gap); } return array; } internal static float FitFont(float baseSize, float widestCaption, float cellWidth, float padding) { return baseSize * TabBandLayout.FitScale(widestCaption, cellWidth - 2f * padding); } } internal sealed class ChipRow { private const float Gap = 10f; private const float WrapSlack = 0.5f; internal readonly RectTransform rectTransform; private readonly List<KitButton> chips = new List<KitButton>(); private readonly float width; private readonly float lineHeight; private readonly bool justify; private ChipRow(RectTransform rect, float width, float lineHeight, bool justify) { rectTransform = rect; this.width = width; this.lineHeight = lineHeight; this.justify = justify; } internal static ChipRow Create(Transform parent, float width, float lineHeight, float fontSize, int count, Action<int> onPick, bool justify = false) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Chips", new Type[1] { typeof(RectTransform) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(parent, false); Vector2 anchorMin = (val2.anchorMax = Vector2.zero); val2.anchorMin = anchorMin; val2.pivot = Vector2.zero; val2.sizeDelta = new Vector2(width, lineHeight); ChipRow chipRow = new ChipRow(val2, width, lineHeight, justify); for (int i = 0; i < count; i++) { int index = i; KitButton kitButton = MenuKit.CreateREPOButton("", delegate { onPick(index); }, (Transform)(object)val2); ((TMP_Text)kitButton.labelTMP).richText = true; ((TMP_Text)kitButton.labelTMP).enableAutoSizing = false; ((TMP_Text)kitButton.labelTMP).fontSize = fontSize; ((TMP_Text)kitButton.labelTMP).margin = Vector4.zero; chipRow.chips.Add(kitButton); } return chipRow; } internal RectTransform ChipRect(int index) { return chips[index].rectTransform; } internal void SetChip(int index, string caption, bool visible) { KitButton kitButton = chips[index]; ((TMP_Text)kitButton.labelTMP).text = caption; if (((Component)kitButton).gameObject.activeSelf != visible) { ((Component)kitButton).gameObject.SetActive(visible); } } internal void Relayout() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_010d: 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_0114: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) List<Vector2> list = new List<Vector2>(chips.Count); int num = 1; float num2 = 0f; foreach (KitButton chip in chips) { if (!((Component)chip).gameObject.activeSelf) { list.Add(Vector2.zero); continue; } float num3 = Mathf.Ceil(chip.GetLabelSize().x) + 4f; if (num2 > 0f && num2 + num3 > width + 0.5f) { num++; num2 = 0f; } list.Add(new Vector2(num3, lineHeight)); num2 += num3 + 10f; } float num4 = (float)num * lineHeight; rectTransform.sizeDelta = new Vector2(width, num4); float num5 = 10f; if (justify && num == 1) { int num6 = 0; float num7 = 0f; foreach (Vector2 item in list) { if (item.x > 0f) { num6++; num7 += item.x; } } if (num6 > 1) { num5 = Mathf.Max(10f, (width - num7) / (float)(num6 - 1)); } } int num8 = 0; num2 = 0f; for (int i = 0; i < chips.Count; i++) { KitButton kitButton = chips[i]; if (((Component)kitButton).gameObject.activeSelf) { float x = list[i].x; if (num2 > 0f && num2 + x > width + 0.5f) { num8++; num2 = 0f; } kitButton.overrideButtonSize = list[i]; ((Transform)kitButton.rectTransform).localPosition = new Vector3(num2, num4 - (float)(num8 + 1) * lineHeight, 0f); num2 += x + num5; } } } } internal readonly struct DrawerBox { private const float Slack = 0.001f; internal float X { get; } internal float Y { get; } internal float Width { get; } internal float Height { get; } internal float Right => X + Width; internal float Bottom => Y + Height; internal DrawerBox(float x, float y, float width, float height) { X = x; Y = y; Width = width; Height = height; } internal DrawerBox Outset(float d) { return new DrawerBox(X - d, Y - d, Width + 2f * d, Height + 2f * d); } internal bool Overlaps(DrawerBox other) { if (X < other.Right && other.X < Right && Y < other.Bottom) { return other.Y < Bottom; } return false; } internal bool Contains(DrawerBox other) { if (other.X >= X - 0.001f && other.Right <= Right + 0.001f && other.Y >= Y - 0.001f) { return other.Bottom <= Bottom + 0.001f; } return false; } } internal sealed class DrawerGeometry { internal float Height { get; } internal float HeaderHeight { get; } internal float CellSize { get; } internal float PitchX { get; } internal float PitchY { get; } internal float Left { get; } internal float Top { get; } internal float FillInset { get; } internal float FillRadius { get; } internal float RingOutset { get; } internal float RingRadius { get; } internal float GapOutset { get; } internal float GapRadius { get; } internal float DotSize { get; } internal float HaloRadius { get; } internal float ChipBottom { get; } internal DrawerGeometry(float height, float headerHeight, float cellSize, float pitchX, float pitchY, float left, float top, float fillInset, float fillRadius, float ringOutset, float ringRadius, float gapOutset, float gapRadius, float dotSize, float haloRadius, float chipBottom) { Height = height; HeaderHeight = headerHeight; CellSize = cellSize; PitchX = pitchX; PitchY = pitchY; Left = left; Top = top; FillInset = fillInset; FillRadius = fillRadius; RingOutset = ringOutset; RingRadius = ringRadius; GapOutset = gapOutset; GapRadius = gapRadius; DotSize = dotSize; HaloRadius = haloRadius; ChipBottom = chipBottom; } } internal static class ColorDrawerLayout { internal const int Columns = 8; internal const int Rows = 2; internal const int Count = 16; internal const float CompactBelow = 124f; internal const float Width = 250f; internal const float EdgeOutset = 0.75f; internal const float EdgeRadius = 5.5f; internal const float HoverEdgeOutset = 1.2f; internal const float LabelX = 6f; internal const float SampleGap = 5f; internal const float SampleSize = 8f; internal const float NameGap = 4f; internal const float NameChipGap = 6f; internal const float ChipRight = 244f; internal const float ChipMinWidth = 40f; internal const float ChipCaptionPadding = 12f; internal const float ChipMaxWidth = 120f; internal const float ChipTop = 1.5f; internal const float ChipRingOutset = 1.5f; internal static readonly float[] MarkXs = new float[3] { 196f, 199.2f, 202.4f }; internal const float MarkWidth = 2.6f; internal const float MarkHeight = 8f; internal const float MarkY = 8f; internal const float MarkRadius = 1.1f; internal const float MarkCaptionMax = 190f; internal static readonly DrawerGeometry Regular = new DrawerGeometry(80f, 16f, 28f, 31f, 30f, 2.5f, 19f, 2f, 5f, 4f, 9f, 2f, 7f, 6f, 6f, 16f); internal static readonly DrawerGeometry CompactGeometry = new DrawerGeometry(68f, 14f, 24f, 27f, 26f, 18.5f, 16f, 1.5f, 4.5f, 3.5f, 8f, 1.5f, 6f, 5f, 5f, 14f); internal static bool Compact(float maskHeight) { return maskHeight < 124f; } internal static DrawerGeometry For(bool compact) { if (!compact) { return Regular; } return CompactGeometry; } internal static DrawerBox Drawer(bool compact) { return new DrawerBox(0f, 0f, 250f, For(compact).Height); } internal static DrawerBox Header(bool compact) { return new DrawerBox(0f, 0f, 250f, For(compact).HeaderHeight); } internal static DrawerBox Cell(int index, bool compact) { if (index < 0 || index >= 16) { throw new ArgumentOutOfRangeException("index"); } DrawerGeometry drawerGeometry = For(compact); int num = index / 8; int num2 = index % 8; return new DrawerBox(drawerGeometry.Left + (float)num2 * drawerGeometry.PitchX, drawerGeometry.Top + (float)num * drawerGeometry.PitchY, drawerGeometry.CellSize, drawerGeometry.CellSize); } internal static DrawerBox Fill(int index, bool compact) { return Cell(index, compact).Outset(0f - For(compact).FillInset); } internal static DrawerBox Ring(int index, bool compact) { return Fill(index, compact).Outset(For(compact).RingOutset); } internal static DrawerBox Dot(int index, bool compact) { DrawerBox drawerBox = Fill(index, compact); float dotSize = For(compact).DotSize; return new DrawerBox(drawerBox.X + (drawerBox.Width - dotSize) / 2f, drawerBox.Y + (drawerBox.Height - dotSize) / 2f, dotSize, dotSize); } internal static DrawerBox OriginalChip(float captionWidth, bool compact) { float num = Math.Min(120f, Math.Max(40f, captionWidth + 12f)); return new DrawerBox(244f - num, 1.5f, num, For(compact).ChipBottom - 1.5f); } internal static DrawerBox Label(float labelWidth, bool compact) { return new DrawerBox(6f, 0f, labelWidth, For(compact).HeaderHeight); } internal static DrawerBox Sample(float labelWidth, bool compact) { float headerHeight = For(compact).HeaderHeight; return new DrawerBox(6f + labelWidth + 5f, (headerHeight - 8f) / 2f, 8f, 8f); } internal static DrawerBox Name(float labelWidth, bool sample, float chipWidth, bool compact) { float num = (sample ? (Sample(labelWidth, compact).Right + 4f) : (6f + labelWidth + 5f)); float width = Math.Max(0f, 244f - chipWidth - 6f - num); return new DrawerBox(num, 0f, width, For(compact).HeaderHeight); } internal static bool DarkInk(string? hex) { if (hex == null || hex.Length != 6) { return false; } if (!TryByte(hex, 0, out var value) || !TryByte(hex, 2, out var value2) || !TryByte(hex, 4, out var value3)) { return false; } double num = 0.2126 * Linear(value) + 0.7152 * Linear(value2) + 0.0722 * Linear(value3); return num > 0.35; } private static double Linear(int channel) { double num = (double)channel / 255.0; if (!(num <= 0.04045)) { return Math.Pow((num + 0.055) / 1.055, 2.4); } return num / 12.92; } private static bool TryByte(string hex, int at, out int value) { value = 0; for (int i = at; i < at + 2; i++) { char c = hex[i]; int num = ((c >= '0' && c <= '9') ? (c - 48) : ((c >= 'a' && c <= 'f') ? (c - 97 + 10) : ((c >= 'A' && c <= 'F') ? (c - 65 + 10) : (-1)))); if (num < 0) { return false; } value = value * 16 + num; } return true; } } internal static class ColorPickerPage { private const float ContentWidth = 250f; private const float SwatchSize = 34f; private const float SwatchPitch = 38f; private const int SwatchesPerRow = 6; private static readonly IReadOnlyList<Color> FallbackPalette = (IReadOnlyList<Color>)(object)new Color[16] { ModStyle.RoleHost, ModStyle.RoleModerator, ModStyle.RoleModUser, ModStyle.RoleNone, ModStyle.SevBan, ModStyle.SevRevoke, ModStyle.SevGrant, ModStyle.SevFlood, Color.white, Color.red, Color.green, Color.blue, Color.yellow, Color.cyan, Color.magenta, Color.gray }; internal static void Open(string title, string description, Color current, Action<Color> onPicked, string? clearCaption = null, Action? onClear = null) { //IL_004c: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) KitPopupPage page = MenuUiHelpers.CreatePage(title, delegate { }); MenuUiHelpers.AddLabel(page, description); string text = NameColors.ToHex(current); string text2 = NameColors.ToHex(NameColors.Legible(current)); MenuUiHelpers.AddLabel(page, "<size=75%><color=#6f7d88>" + Loc.T("Current") + "</color> <color=#" + text2 + ">#" + text + "</color></size>"); foreach (IReadOnlyList<Color> item in Rows(Palette())) { AddSwatchRow(page, item, Choose); } AddHexInput(page, current, Choose); if (onClear != null && clearCaption != null) { MenuUiHelpers.AddScrollViewButton(page, clearCaption, delegate { MenuUiHelpers.ClosePage(page); onClear(); }); } MenuUiHelpers.OpenPage(page); void Choose(Color color) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) MenuUiHelpers.ClosePage(page); onPicked(color); } } private static void AddSwatchRow(KitPopupPage page, IReadOnlyList<Color> colors, Action<Color> onChoose) { page.AddElementToScrollView(delegate(Transform scrollView) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SharePermissions_SwatchRow", new Type[1] { typeof(RectTransform) }); RectTransform val2 = (RectTransform)val.transform; ((Transform)val2).SetParent(scrollView, false); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.zero; val2.pivot = Vector2.zero; val2.sizeDelta = new Vector2(250f, 34f); for (int i = 0; i < colors.Count; i++) { Color color = colors[i]; KitButton button = MenuKit.CreateREPOButton("", delegate { //IL_000c: Unknown result type (might be due to invalid IL or missing references) onChoose(color); }, (Transform)(object)val2, new Vector2((float)i * 38f, 0f)); MenuUiHelpers.PinButtonSize(button, new Vector2(34f, 34f)); StyleSwatch(button, color); } return val2; }, 0f, 2f); } private static void StyleSwatch(KitButton button, Color color) { //IL_0011: 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_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_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_006e: 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_007d: Unknown result type (might be due to invalid IL or missing references) Image component = ((Component)button).GetComponent<Image>(); if ((Object)(object)component != (Object)null) { ((Graphic)component).color = color; ((Graphic)component).raycastTarget = true; } if (!((Object)(object)button.menuButton == (Object)null)) { button.menuButton.resizeButton = false; button.menuButton.customColors = true; button.menuButton.colorNormal = NameColors.Dimmed(color, 0.75f); button.menuButton.colorHover = color; button.menuButton.colorClick = Color.Lerp(color, Color.white, 0.95f); } } private static void AddHexInput(KitPopupPage page, Color current, Action<Color> onChoose) { //IL_0015: 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) KitLabel hint = null; page.AddElementToScrollView(delegate(Transform scrollView) { //IL_0030: 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_0048: Unknown result type (might be due to invalid IL or missing references) string labelText = Loc.T("Hex"); Action<string> onValueChanged = delegate(string value) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (NameColors.TryParseHex(value, out var color)) { onChoose(color); } else { Plugin.Logger.LogInfo((object)("Name color ignored: '" + value + "' is not a #RRGGBB value")); if (!((Object)(object)hint == (Object)null)) { string text = "<size=75%><color=#ffa726>" + Loc.T("That is not a #RRGGBB color. Fix it and press enter.") + "</color></size>"; ((TMP_Text)hint.labelTMP).text = text; MenuUiHelpers.SizeWrappedLabel(hint, text, 250f); if ((Object)(object)page != (Object)null && (Object)(object)page.scrollView != (Object)null) { page.scrollView.UpdateElements(); } } } }; string placeholder = "#" + NameColors.ToHex(current); KitInputField kitInputField = MenuKit.CreateREPOInputField(labelText, onValueChanged, scrollView, default(Vector2), onlyNotifyOnSubmit: true, placeholder); ((TMP_Text)kitInputField.labelTMP).fontSize = 14f; return kitInputField.rectTransform; }, 4f, 2f); hint = MenuUiHelpers.AddLabel(page, "<size=75%><color=#6f7d88>" + Loc.T("Or type a color as #RRGGBB and press enter.") + "</color></size>", out KitScrollRow _); } private static IEnumerable<IReadOnlyList<Color>> Rows(IReadOnlyList<Color> colors) { for (int start = 0; start < colors.Count; start += 6) { yield return colors.Skip(start).Take(6).ToList(); } } private static IReadOnlyList<Color> Palette() { try { IReadOnlyList<Color> readOnlyList = GamePaletteRaw(); if (readOnlyList.Count > 0) { return readOnlyList; } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("[SharePermissions] Game color palette unreadable, using the built-in one: " + ex.Message)); } return FallbackPalette; } [MethodImpl(MethodImplOptions.NoInlining)] private static IReadOnlyList<Color> GamePaletteRaw() { MetaManager instance = MetaManager.instance; if ((Object)(object)instance == (Object)null || instance.colors == null) { return Array.Empty<Color>(); } List<SemiColor> order = instance.colorsUIOrder; return (from c in instance.colors where (Object)(object)c != (Object)null orderby (order != null) ? order.IndexOf(c) : 0 select c.color).ToList(); } } internal static class EscRowHeads { internal sealed class State { internal readonly float scale; internal readonly float nameShift; internal readonly float leftOfText; internal readonly string name; internal RectTransform? head; internal HeadLook look; internal Color top; internal Color bottom; internal float nextCheck; internal Vector2 placed; internal TMP_Text? shifted; internal Vector4 baseMargin; internal RectTransform? lid; internal float flap; internal State() : this(0.15f, 15f, 9.5f, "SP Esc Head") { } internal State(float scale, float nameShift, float leftOfText, string name) { this.scale = scale; this.nameShift = nameShift; this.leftOfText = leftOfText; this.name = name; } } private const float Scale = 0.15f; private const float NameShift = 15f; private const float LeftOfText = 9.5f; private const float CheckInterval = 0.25f; internal const float FlapPerLoudness = 200f; private static bool loudnessFailedLogged; private static bool colorsFailedLogged; internal static readonly Color Neutral = new Color(0.82f, 0.84f, 0.87f); internal static readonly Color NeutralJaw = new Color(0.58f, 0.61f, 0.66f); internal static void Sync(State state, TMP_Text? label, PlayerAvatar avatar) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)label == (Object)null) { return; } if (!ModConfig.ShowEscMenuHeads.Value) { Drop(state); return; } if (Due(state)) { HeadLook look = (ModConfig.ShowLobbyHeads.Value ? HeadStyles.ResolveLookForAvatar(avatar) : HeadLook.Stock); HeadColors(avatar, out var top, out var bottom); Show(state, label, look, top, bottom); } Follow(state, label, avatar); } internal static bool Due(State state) { if (!((Object)(object)state.head == (Object)null)) { return Time.unscaledTime >= state.nextCheck; } return true; } internal static void Recheck(State state) { state.nextCheck = 0f; } internal static void Show(State state, TMP_Text label, HeadLook look, Color top, Color bottom) { //IL_0059: 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_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_007a: 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_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_003e: Unknown result type (might be due to invalid IL or missing references) state.nextCheck = Time.unscaledTime + 0.25f; if (!((Object)(object)state.head != (Object)null) || !(look == state.look) || !(top == state.top) || !(bottom == state.bottom)) { DestroyHead(state); state.look = look; state.top = top; state.bottom = bottom; state.head = HeadPreview.Build(label.rectTransform, look, state.scale, Vector2.zero, top, bottom); ((Object)state.head).name = state.name; ref RectTransform? lid = ref state.lid; Transform obj = ((Transform)state.head).Find("SP Head Preview Lid"); lid = (RectTransform?)(object)((obj is RectTransform) ? obj : null); state.flap = 0f; state.placed = new Vector2(float.NaN, float.NaN); } } internal static void Follow(State state, TMP_Text label, PlayerAvatar? avatar) { ShiftName(state, label); Place(state, label); Flap(state, avatar); } private static void Flap(State state, PlayerAvatar? avatar) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)state.lid == (Object)null)) { float num = (((Object)(object)avatar != (Object)null) ? (Loudness(avatar) * 200f) : 0f); if (num != state.flap) { state.flap = num; ((Transform)state.lid).localEulerAngles = new Vector3(0f, 0f, num); } } } private static float Loudness(PlayerAvatar avatar) { try { return LoudnessRaw(avatar); } catch (Exception ex) { if (!loudnessFailedLogged) { loudnessFailedLogged = true; Plugin.Logger.LogWarning((object)("Escape menu heads: voice loudness unreadable, heads stay still: " + ex.Message)); } return 0f; } } [MethodImpl(MethodImplOptions.NoInlining)] private static float LoudnessRaw(PlayerAvatar avatar) { if (!avatar.voiceChatFetched || (Object)(object)avatar.voiceChat == (Object)null) { return 0f; } if ((Object)(object)avatar.playerDeathHead != (Object)null && avatar.playerDeathHead.spectated) { return 0f; } return avatar.voiceChat.clipLoudness; } internal static void Drop(State state) { DestroyHead(state); UnshiftName(state); } private static void DestroyHead(State state) { if ((Object)(object)state.head != (Object)null) { Object.Destroy((Object)(object)((Component)state.head).gameObject); } state.head = null; state.lid = null; } private static void ShiftName(State state, TMP_Text label) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)state.shifted == (Object)(object)label)) { UnshiftName(state); state.baseMargin = label.margin; label.margin = state.baseMargin + new Vector4(state.nameShift, 0f, 0f, 0f); state.shifted = label; state.placed = new Vector2(float.NaN, float.NaN); } } private static void UnshiftName(State state) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)state.shifted != (Object)null) { state.shifted.margin = state.baseMargin; } state.shifted = null; } private static void Place(State state, TMP_Text label) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_004a: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: 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_00dc: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)state.head == (Object)null)) { Rect rect = label.rectTransform.rect; Bounds textBounds = label.textBounds; bool flag = ((Bounds)(ref textBounds)).size.x > 0f && !float.IsNaN(((Bounds)(ref textBounds)).min.x) && !float.IsInfinity(((Bounds)(ref textBounds)).min.x); float num = (flag ? (((Bounds)(ref textBounds)).min.x - ((Rect)(ref rect)).xMin) : label.margin.x); float num2 = (flag ? (((Bounds)(ref textBounds)).center.y - ((Rect)(ref rect)).yMin) : (((Rect)(ref rect)).height / 2f)); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(num - state.leftOfText, num2); if (!(val == state.placed)) { state.placed = val; state.head.anchoredPosition = val; } } } internal static bool HeadColors(PlayerAvatar avatar, out Color top, out Color bottom) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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) try { if (HeadColorsRaw(avatar, out top, out bottom)) { return true; } } catch (Exception ex) { if (!colorsFailedLogged) { colorsFailedLogged = true; Plugin.Logger.LogWarning((object)("Escape menu heads: player head colors unreadable, drawing grey: " + ex.Message)); } } top = Neutral; bottom = NeutralJaw; return false; } [MethodImpl(MethodImplOptions.NoInlining)] private static bool HeadColorsRaw(PlayerAvatar avatar, out Color top, out Color bottom) { //IL_0004: 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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) top = (bottom = default(Color)); MetaManager instance = MetaManager.instance; PlayerCosmetics playerCosmetics = avatar.playerCosmetics; if ((Object)(object)instance == (Object)null || instance.colors == null || (Object)(object)playerCosmetics == (Object)null) { return false; } int[] colorsEquipped = playerCosmetics.colorsEquipped; if (colorsEquipped == null || colorsEquipped.Length < 7) { return false; } int num = colorsEquipped[5]; int num2 = colorsEquipped[6]; if (num < 0 || num >= instance.colors.Count || num2 < 0 || num2 >= instance.colors.Count) { return false; } if ((Object)(object)instance.colors[num] == (Object)null || (Object)(object)instance.colors[num2] == (Object)null) { return false; } top = instance.colors[num].color; bottom = instance.colors[num2].color; return true; } } internal sealed class ExtrasPanelController { private readonly struct CardKey : IEquatable<CardKey> { private readonly int[] members; private readonly PrivateVoiceScope scope; private readonly int secondsLeft; private readonly ulong absent; private readonly bool host; private readonly int pairA; internal CardKey(int[] members, PrivateVoiceScope scope, int secondsLeft, ulong absent, bool host, int pairA) { this.members = members; this.scope = scope; this.secondsLeft = secondsLeft; this.absent = absent; this.host = host; this.pairA = pairA; } public bool Equals(CardKey other) { if (members == other.members && scope == other.scope && secondsLeft == other.secondsLeft && absent == other.absent && host == other.host) { return pairA == other.pairA; } return false; } public override bool Equals(object? obj) { if (obj is CardKey other) { return Equals(other); } return false; } public override int GetHashCode() { return secondsLeft ^ (int)absent ^ (int)scope; } } private readonly KitPopupPage page; private KitScrollRow? channelHeaderElem; private KitLabel? offHint; private KitScrollRow? offHintElem; private const int OffHintTeam = 1; private const int OffHintPlayer = 2; private const int OffHintNoCap = 3; private int offHintLine; private KitButton? privateToggle; private KitScrollRow? privateToggleElem; private int toggleEnabledState = -1; private KitLabel? channelCard; private KitScrollRow? channelCardElem; private CardKey cardKey; private bool cardWritten; private const int TeamLineNone = 0; private const int TeamLineNoModerators = 1; private const int TeamLineAlone = 2; private KitLabel? teamEmptyHint; private KitScrollRow? teamEmptyHintElem; private int teamLineShown; private const int MaxOneOnOneRows = 20; private const float OneOnOneRescanSeconds = 0.25f; private KitScrollRow? oneOnOneHintElem; private KitScrollRow? oneOnOneAskHintElem; private KitScrollRow? oneOnOneManageHintElem; private KitScrollRow? oneOnOneInviteHintElem; private bool rowsAdding; private KitLabel? callLimits; private KitScrollRow? callLimitsElem; private (int Max, int Wait) callLimitsShown = (Max: -1, Wait: -1); private KitLabel? callWait; private KitScrollRow? callWaitElem; private int callWaitShown = -1; private bool callWaitHeld; private int rowsEnabledState = -1; private readonly KitButton?[] oneOnOneRows = new KitButton[20]; private readonly KitScrollRow?[] oneOnOneRowElems = new KitScrollRow[20]; private readonly int[] oneOnOneRowActors = new int[20]; private readonly string[] oneOnOneRowNames = new string[20]; private readonly PrivateVoiceSync.HostRowAction[] oneOnOneRowAction = new PrivateVoiceSync.HostRowAction[20]; private readonly bool[] oneOnOneRowOffline = new bool[20]; private readonly int[] oneOnOneCandidates = new int[20]; private int oneOnOneCount; private float nextOneOnOneRescanAt; private float nextOneOnOneOverflowWarnAt; private KitLabel? pendingLabel; private KitScrollRow? pendingLabelElem; private bool pendingLabelForMe; private int pendingLabelActor; private int pendingLabelLimit; private bool pendingLabelJoin; private int pendingLabelSeconds = -1; private KitButton? accep