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 PerfectProximityVoice v0.2.5
PerfectProximityVoice.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using UnityEngine; 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: AssemblyCompany("PerfectProximityVoice")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PerfectProximityVoice")] [assembly: AssemblyTitle("PerfectProximityVoice")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PerfectProximityVoice { [BepInPlugin("perfect.proximity.voice", "Perfect Proximity Voice", "0.2.5")] public sealed class Plugin : BaseUnityPlugin { private sealed class NativeSliderControl { private readonly Plugin owner; private readonly long uid; private readonly GameObject gameObject; private readonly RectTransform rect; private readonly Component slider; private readonly PropertyInfo valueProperty; private bool changing; private static Sprite goldHandleSprite; internal NativeSliderControl(Plugin plugin, long playerUid, GameObject clone, Type sliderType) { owner = plugin; uid = playerUid; gameObject = clone; ref RectTransform reference = ref rect; Transform transform = clone.transform; reference = (RectTransform)(object)((transform is RectTransform) ? transform : null); slider = clone.GetComponent(sliderType); valueProperty = sliderType.GetProperty("value"); sliderType.GetProperty("minValue")?.SetValue(slider, 0f, null); sliderType.GetProperty("maxValue")?.SetValue(slider, 2f, null); sliderType.GetProperty("wholeNumbers")?.SetValue(slider, false, null); HideHandleArtwork(clone); Component[] componentsInChildren = clone.GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)slider) { continue; } switch (((object)val).GetType().Name) { case "Text": case "TextMeshProUGUI": case "TMP_Text": { PropertyInfo property = ((object)val).GetType().GetProperty("text"); if (property != null && property.CanWrite) { property.SetValue(val, "", null); } break; } } } try { object obj = sliderType.GetProperty("onValueChanged")?.GetValue(slider, null); (obj?.GetType().GetMethod("RemoveAllListeners"))?.Invoke(obj, null); MethodInfo methodInfo = obj?.GetType().GetMethod("AddListener"); if (methodInfo != null) { Type parameterType = methodInfo.GetParameters()[0].ParameterType; SliderRelay firstArgument = new SliderRelay(owner, uid); Delegate obj2 = Delegate.CreateDelegate(parameterType, firstArgument, typeof(SliderRelay).GetMethod("Changed")); methodInfo.Invoke(obj, new object[1] { obj2 }); } } catch (Exception ex) { ((BaseUnityPlugin)owner).Logger.LogWarning((object)("Could not bind native slider: " + ex.Message)); } SetValue(owner.GetPlayerVolume(uid)); } private static void HideHandleArtwork(GameObject clone) { Slider component = clone.GetComponent<Slider>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.handleRect == (Object)null)) { Graphic[] componentsInChildren = ((Component)component.handleRect).GetComponentsInChildren<Graphic>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Behaviour)componentsInChildren[i]).enabled = false; } } } private static Sprite MakeGoldHandleSprite() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0091: 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_00b3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)goldHandleSprite != (Object)null) { return goldHandleSprite; } Texture2D val = new Texture2D(48, 48, (TextureFormat)4, false); ((Object)val).name = "PPV_GoldSliderHandle"; for (int i = 0; i < 48; i++) { for (int j = 0; j < 48; j++) { float num = (float)j + 0.5f - 24f; float num2 = (float)i + 0.5f - 24f; float num3 = Mathf.Sqrt(num * num + num2 * num2); float num4 = Mathf.Clamp01((22.56f - num3) * 1.5f); Color val2 = ((num3 > 19.2f) ? new Color(0.38f, 0.23f, 0.07f, num4) : new Color(1f, 0.78f, 0.27f, num4)); val.SetPixel(j, i, val2); } } val.Apply(); goldHandleSprite = Sprite.Create(val, new Rect(0f, 0f, 48f, 48f), new Vector2(0.5f, 0.5f), 100f); return goldHandleSprite; } internal void SetValue(float value) { if (!((Object)(object)slider == (Object)null) && !(valueProperty == null) && !changing) { changing = true; try { valueProperty.SetValue(slider, Mathf.Clamp(value, 0f, 2f), null); } catch { } changing = false; } } internal void SetRect(float x, float y, float width, float height) { //IL_001f: 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) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rect == (Object)null)) { rect.anchorMin = new Vector2(0.5f, 0.5f); rect.anchorMax = new Vector2(0.5f, 0.5f); rect.pivot = new Vector2(0.5f, 0.5f); rect.anchoredPosition = new Vector2(x + width * 0.5f - (float)Screen.width * 0.5f, (float)Screen.height * 0.5f - (y + height * 0.5f)); rect.sizeDelta = new Vector2(width, height); } } internal void SetVisible(bool visible) { if ((Object)(object)gameObject != (Object)null && gameObject.activeSelf != visible) { gameObject.SetActive(visible); } } } private sealed class SliderRelay { private readonly Plugin owner; private readonly long uid; internal SliderRelay(Plugin plugin, long playerUid) { owner = plugin; uid = playerUid; } public void Changed(float value) { owner.OnNativeSliderChanged(uid, value); } } private sealed class Speaker { internal readonly AudioSource source; private readonly AudioReverbFilter dungeonReverb; private readonly AudioLowPassFilter occlusionFilter; internal float occlusionGain = 1f; internal float lastLocated; private readonly float[] ring; private float lastReverbLevel = float.NaN; private float lastReverbDecay = float.NaN; private float lastOcclusionGain = float.NaN; private float lastCutoff = float.NaN; private int write; private int read; private readonly object gate = new object(); internal Speaker(long id, int rate, float distance) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Expected O, but got Unknown GameObject val = new GameObject("PerfectProximityVoice_" + id); Object.DontDestroyOnLoad((Object)(object)val); source = val.AddComponent<AudioSource>(); source.spatialBlend = 1f; source.reverbZoneMix = 0f; source.bypassEffects = false; source.bypassListenerEffects = true; source.bypassReverbZones = true; dungeonReverb = val.AddComponent<AudioReverbFilter>(); ((Behaviour)dungeonReverb).enabled = false; occlusionFilter = val.AddComponent<AudioLowPassFilter>(); ((Behaviour)occlusionFilter).enabled = false; occlusionFilter.cutoffFrequency = 22000f; occlusionFilter.lowpassResonanceQ = 1f; dungeonReverb.reverbPreset = (AudioReverbPreset)27; dungeonReverb.dryLevel = 0f; dungeonReverb.room = -650f; dungeonReverb.roomHF = -350f; dungeonReverb.roomLF = -800f; dungeonReverb.decayTime = 1.45f; dungeonReverb.decayHFRatio = 0.72f; dungeonReverb.reflectionsLevel = -850f; dungeonReverb.reflectionsDelay = 0.035f; dungeonReverb.reverbLevel = -350f; dungeonReverb.reverbDelay = 0.045f; dungeonReverb.diffusion = 75f; dungeonReverb.density = 70f; dungeonReverb.hfReference = 5000f; dungeonReverb.lfReference = 250f; source.rolloffMode = (AudioRolloffMode)1; source.minDistance = 5f; source.maxDistance = distance; source.loop = true; ring = new float[rate * 3]; source.clip = AudioClip.Create("Voice_" + id, rate, 1, rate, true, new PCMReaderCallback(Fill)); source.Play(); } internal void SetOcclusion(float gain, float cutoff) { gain = Mathf.Clamp(gain, 0.1f, 1f); cutoff = Mathf.Clamp(cutoff, 500f, 22000f); if (Mathf.Abs(lastOcclusionGain - gain) > 0.005f) { occlusionGain = gain; lastOcclusionGain = gain; } if (!((Object)(object)occlusionFilter == (Object)null)) { if (float.IsNaN(lastCutoff) || Mathf.Abs(lastCutoff - cutoff) > 5f) { occlusionFilter.cutoffFrequency = cutoff; lastCutoff = cutoff; } bool flag = gain < 0.999f || cutoff < 21999f; if (((Behaviour)occlusionFilter).enabled != flag) { ((Behaviour)occlusionFilter).enabled = flag; } } } internal void SetDungeonReverb(bool enabled, float level, float decay) { if (!((Object)(object)dungeonReverb == (Object)null)) { level = Mathf.Clamp(level, -10000f, 2000f); decay = Mathf.Clamp(decay, 0.1f, 3f); if (float.IsNaN(lastReverbLevel) || Mathf.Abs(lastReverbLevel - level) > 1f) { dungeonReverb.reverbLevel = level; lastReverbLevel = level; } if (float.IsNaN(lastReverbDecay) || Mathf.Abs(lastReverbDecay - decay) > 0.01f) { dungeonReverb.decayTime = decay; lastReverbDecay = decay; } if (((Behaviour)dungeonReverb).enabled != enabled) { ((Behaviour)dungeonReverb).enabled = enabled; } } } internal void Push(byte[] data, float gain) { lock (gate) { for (int i = 0; i < data.Length; i++) { float num = MuLawDecode(data[i]) * gain; ring[write] = num / (1f + Mathf.Abs(num)); write = (write + 1) % ring.Length; if (write == read) { read = (read + 1) % ring.Length; } } } } private void Fill(float[] output) { lock (gate) { for (int i = 0; i < output.Length; i++) { if (read == write) { output[i] = 0f; continue; } output[i] = ring[read]; read = (read + 1) % ring.Length; } } } } private const string RpcName = "PPV_Audio_018"; private readonly string sessionToken = Guid.NewGuid().ToString("N"); private const int Rate = 16000; private const int Frame = 1600; private ConfigEntry<KeyCode> pushKey; private ConfigEntry<KeyCode> muteAllPlayersKey; private bool muteAllPlayers; private ConfigEntry<float> range; private ConfigEntry<float> volume; private ConfigEntry<float> voiceGain; private ConfigEntry<bool> caveReverbEnabled; private ConfigEntry<float> caveReverbLevel; private ConfigEntry<float> caveReverbDecay; private ConfigEntry<bool> obstructionEnabled; private ConfigEntry<float> wallVoiceVolume; private ConfigEntry<float> doorVoiceVolume; private ConfigEntry<float> wallLowPass; private ConfigEntry<float> doorLowPass; private float nextOcclusionCheck; private readonly RaycastHit[] occlusionHits = (RaycastHit[])(object)new RaycastHit[12]; private float nextMicDeviceCheck; private bool hasMicrophoneDevice; private ConfigEntry<string> playerVolumes; private readonly Dictionary<long, float> individualVolumes = new Dictionary<long, float>(); private GUIStyle voiceTitleStyle; private Vector2 voiceScroll; private bool voicePanelVisible; private float nextPlayerScan; private readonly Dictionary<long, string> knownNames = new Dictionary<long, string>(); private ConfigEntry<string> microphoneDevice; private ConfigEntry<int> microphoneChannel; private string activeDevice; private float nextMicLog; private AudioClip microphone; private string micName; private int lastMicPosition; private float[] captureInterleaved = new float[3200]; private float[] captureSamples = new float[1600]; private byte[] captureEncoded = new byte[1600]; private int captureBufferChannels = 2; private static readonly Vector3[] screenRectCorners = (Vector3[])(object)new Vector3[4]; private readonly Dictionary<long, Speaker> speakers = new Dictionary<long, Speaker>(); private object registeredOn; private MethodInfo invokeRpc; private bool warned; private bool transmitting; private GUIStyle playerLabelStyle; private GUIStyle playerValueStyle; private GUIStyle playerVoiceStyle; private Texture2D micIconTexture; private Font averiaFont; private Object averiaTmpFont; private object nativeSliderType; private Canvas playerControlsCanvas; private readonly Dictionary<long, NativeSliderControl> nativeSliders = new Dictionary<long, NativeSliderControl>(); private float nextRegistration; private bool pickupRebound; private float nextPickupBind; private float nextDungeonCheck; private float nextFontRetry; private bool dungeonReverbActive; private FieldInfo insideDungeonField; private PropertyInfo insideDungeonProperty; private void Awake() { pushKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Voice", "PushToTalk", (KeyCode)118, "Hold to transmit."); muteAllPlayersKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Voice", "MuteAllPlayersKey", (KeyCode)0, "Toggle mute for all incoming player voices. Unassigned by default."); range = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "RangeMeters", 25f, "Maximum audible distance."); volume = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "Volume", 1f, "Incoming voice volume (0 to 1)."); voiceGain = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "VoiceGain", 8f, "Incoming voice amplification. Soft limiter enabled."); caveReverbEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "DungeonReverb", true, "Enable subtle voice echo in dungeon environments."); caveReverbLevel = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DungeonReverbLevel", -350f, "Medium wet reverb level in mB (-10000 to 2000). Lower is quieter."); caveReverbDecay = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DungeonReverbDecay", 1.45f, "Medium dungeon voice reverb decay in seconds (0.1 to 3)."); obstructionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Audio", "ObstructionEnabled", true, "Muffle incoming voice when walls or doors block the listener's line of sight."); wallVoiceVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "WallVoiceVolume", 0.58f, "Voice volume multiplier through one wall (0.1 to 1)."); doorVoiceVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DoorVoiceVolume", 0.42f, "Voice volume multiplier through a door (0.1 to 1)."); wallLowPass = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "WallLowPassHz", 2200f, "Low-pass cutoff through walls (500 to 8000 Hz)."); doorLowPass = ((BaseUnityPlugin)this).Config.Bind<float>("Audio", "DoorLowPassHz", 1500f, "Low-pass cutoff through doors (500 to 8000 Hz)."); playerVolumes = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "IndividualVolumes", "", "Per-player voice volumes as uid=percent entries separated by semicolons (0-200)."); LoadPlayerVolumes(); microphoneDevice = ((BaseUnityPlugin)this).Config.Bind<string>("Voice", "MicrophoneDevice", "", "Exact microphone name from BepInEx log. Empty = Windows/Unity default microphone."); microphoneChannel = ((BaseUnityPlugin)this).Config.Bind<int>("Voice", "MicrophoneChannel", 1, "1-based input channel. Focusrite Input 2 = 2. 0 = mix all channels."); microphoneDevice.SettingChanged += delegate { StopMicrophone(); }; LogMicrophones(); LoadGameUiAssets(); LoadPushToTalkIcon(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Perfect Proximity Voice 0.2.5 initialized (reused audio/UI buffers + global mute + per-player sliders; dungeon reverb unchanged)"); } private void Update() { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime > nextRegistration) { nextRegistration = Time.unscaledTime + 2f; TryRegister(); } if (!pickupRebound && Time.unscaledTime >= nextPickupBind) { nextPickupBind = Time.unscaledTime + 3f; TryRebindAutoPickup(); } if (Time.unscaledTime >= nextMicDeviceCheck) { nextMicDeviceCheck = Time.unscaledTime + 5f; hasMicrophoneDevice = Microphone.devices.Length != 0; } if ((int)muteAllPlayersKey.Value != 0 && Input.GetKeyDown(muteAllPlayersKey.Value)) { muteAllPlayers = !muteAllPlayers; ((BaseUnityPlugin)this).Logger.LogInfo((object)("All incoming player voices " + (muteAllPlayers ? "muted." : "unmuted."))); } transmitting = Input.GetKey(pushKey.Value) && NetworkReady() && (Object)(object)Player.m_localPlayer != (Object)null && hasMicrophoneDevice; if (transmitting) { CaptureAndSend(); } else { StopMicrophone(); } if (Time.unscaledTime >= nextDungeonCheck) { nextDungeonCheck = Time.unscaledTime + 1f; dungeonReverbActive = IsLocalPlayerInDungeonOrCave(); } bool flag = Time.unscaledTime >= nextOcclusionCheck; if (flag) { nextOcclusionCheck = Time.unscaledTime + 0.25f; } Player localPlayer = Player.m_localPlayer; float unscaledTime = Time.unscaledTime; foreach (KeyValuePair<long, Speaker> speaker in speakers) { Speaker value = speaker.Value; bool flag2 = unscaledTime - value.lastLocated < 0.55f; float num = ((unscaledTime - value.lastLocated < 1.5f && !muteAllPlayers) ? (Mathf.Clamp01(volume.Value) * GetPlayerVolume(speaker.Key)) : 0f); if (flag && flag2 && (Object)(object)localPlayer != (Object)null && obstructionEnabled.Value) { ApplyVoiceOcclusion(value, localPlayer); } else if (!obstructionEnabled.Value || !flag2) { value.SetOcclusion(1f, 22000f); } value.source.volume = num * value.occlusionGain; value.source.maxDistance = Mathf.Max(1f, range.Value); value.SetDungeonReverb(dungeonReverbActive && caveReverbEnabled.Value, caveReverbLevel.Value, caveReverbDecay.Value); } } private void ApplyVoiceOcclusion(Speaker speaker, Player listener) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) try { Vector3 val = ((Component)listener).transform.position + Vector3.up * 1f; Vector3 val2 = ((Component)speaker.source).transform.position + Vector3.up * 1f - val; float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude < 0.2f) { speaker.SetOcclusion(1f, 22000f); return; } int num = Physics.RaycastNonAlloc(val, val2 / magnitude, occlusionHits, Mathf.Max(0.1f, magnitude - 0.45f), -5, (QueryTriggerInteraction)1); int num2 = 0; bool flag = false; for (int i = 0; i < num; i++) { Collider collider = ((RaycastHit)(ref occlusionHits[i])).collider; if ((Object)(object)collider == (Object)null || !collider.enabled || collider.isTrigger) { continue; } Transform transform = ((Component)collider).transform; if ((Object)(object)transform == (Object)(object)((Component)listener).transform || transform.IsChildOf(((Component)listener).transform) || (Object)(object)((Component)collider).GetComponentInParent<Character>() != (Object)null) { continue; } string text = ((Object)transform).name; Transform parent = transform.parent; int num3 = 0; while (num3 < 3 && (Object)(object)parent != (Object)null) { text = text + " " + ((Object)parent).name; num3++; parent = parent.parent; } text = text.ToLowerInvariant(); if (!text.Contains("player") && !text.Contains("character") && !text.Contains("vegetation") && !text.Contains("tree") && !text.Contains("bush") && !text.Contains("grass") && !text.Contains("piece_noforce") && !text.Contains("vfx") && !text.Contains("trigger")) { if (text.Contains("door") || text.Contains("gate") || text.Contains("hatch")) { flag = true; } else if (text.Contains("wall") || text.Contains("stone") || text.Contains("wood") || text.Contains("roof") || text.Contains("floor") || text.Contains("beam") || text.Contains("crypt") || text.Contains("piece")) { num2++; } } } if (flag) { speaker.SetOcclusion(Mathf.Clamp(doorVoiceVolume.Value, 0.1f, 1f), Mathf.Clamp(doorLowPass.Value, 500f, 8000f)); } else if (num2 > 1) { speaker.SetOcclusion(Mathf.Clamp(wallVoiceVolume.Value * 0.72f, 0.1f, 1f), Mathf.Clamp(wallLowPass.Value * 0.72f, 500f, 8000f)); } else if (num2 == 1) { speaker.SetOcclusion(Mathf.Clamp(wallVoiceVolume.Value, 0.1f, 1f), Mathf.Clamp(wallLowPass.Value, 500f, 8000f)); } else { speaker.SetOcclusion(1f, 22000f); } } catch { speaker.SetOcclusion(1f, 22000f); } } private bool IsLocalPlayerInDungeonOrCave() { //IL_023c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } try { Type type = ((object)localPlayer).GetType(); if (insideDungeonField == null && insideDungeonProperty == null) { string[] array = new string[4] { "m_insideDungeon", "m_inDungeon", "m_insideCave", "m_inCave" }; foreach (string name in array) { insideDungeonField = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (insideDungeonField != null && insideDungeonField.FieldType == typeof(bool)) { break; } insideDungeonField = null; insideDungeonProperty = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (insideDungeonProperty != null && insideDungeonProperty.PropertyType == typeof(bool)) { break; } insideDungeonProperty = null; } } if (insideDungeonField != null && (bool)insideDungeonField.GetValue(localPlayer)) { return true; } if (insideDungeonProperty != null && (bool)insideDungeonProperty.GetValue(localPlayer, null)) { return true; } EnvMan instance = EnvMan.instance; if ((Object)(object)instance != (Object)null) { Type type2 = ((object)instance).GetType(); string[] array = new string[2] { "GetCurrentEnvironment", "GetCurrentEnv" }; foreach (string name2 in array) { MethodInfo method = type2.GetMethod(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (!(method == null) && IsDungeonEnvironment(method.Invoke(instance, null))) { return true; } } array = new string[3] { "m_forceEnvironment", "m_forcedEnvironment", "m_currentEnvironment" }; foreach (string name3 in array) { FieldInfo field = type2.GetField(name3, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && IsDungeonEnvironment(field.GetValue(instance))) { return true; } } } if (((Component)localPlayer).transform.position.y > 3000f) { return true; } } catch (Exception ex) { if (!warned) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Dungeon reverb detection failed: " + ex.Message)); warned = true; } } return false; } private static bool IsDungeonEnvironment(object value) { if (value == null) { return false; } string text = value as string; if (text == null) { Type type = value.GetType(); text = type.GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(value) as string; if (text == null) { text = type.GetProperty("m_name")?.GetValue(value, null) as string; } } if (string.IsNullOrEmpty(text)) { return false; } text = text.ToLowerInvariant(); if (!text.Contains("crypt") && !text.Contains("dungeon") && !text.Contains("cave") && !text.Contains("burial") && !text.Contains("sunken") && !text.Contains("frost")) { return text.Contains("mine"); } return true; } private void TryRebindAutoPickup() { try { Type typeFromHandle = typeof(ZInput); object obj = typeFromHandle.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj == null) { obj = typeFromHandle.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null); } if (obj == null) { return; } string[] array = new string[3] { "AutoPickup", "AutoPickUp", "Auto pickup" }; foreach (string text in array) { MethodInfo[] methods = typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name != "ChangeButton" && methodInfo.Name != "SetButton") { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length < 2 || parameters[0].ParameterType != typeof(string) || parameters[1].ParameterType != typeof(KeyCode)) { continue; } object[] array2 = new object[parameters.Length]; array2[0] = text; array2[1] = (object)(KeyCode)108; bool flag = true; for (int k = 2; k < parameters.Length; k++) { if (parameters[k].HasDefaultValue) { array2[k] = parameters[k].DefaultValue; continue; } if (parameters[k].ParameterType == typeof(bool)) { array2[k] = false; continue; } flag = false; break; } if (flag) { methodInfo.Invoke(obj, array2); pickupRebound = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Vanilla Auto Pickup shortcut reassigned to L (binding: " + text + ")")); return; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not rebind vanilla Auto Pickup to L: " + ex.Message)); pickupRebound = true; } } private void OnGUI() { //IL_0056: 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) DrawPlayerVoiceControls(); if (transmitting && !((Object)(object)microphone == (Object)null) && !((Object)(object)micIconTexture == (Object)null)) { float num = 66f; float num2 = 78f; float num3 = (float)Screen.width * 0.122f - 15f; float num4 = (float)Screen.height - 190f; GUI.color = Color.white; GUI.DrawTexture(new Rect(num3, num4, num, num2), (Texture)(object)micIconTexture, (ScaleMode)2, true); } } private void LoadPushToTalkIcon() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown try { using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("PerfectProximityVoice.Assets.voice_mic.png"); if (stream == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Embedded push-to-talk microphone icon was not found."); return; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); micIconTexture = new Texture2D(2, 2, (TextureFormat)4, false); ((Object)micIconTexture).name = "PPV_PushToTalk_Mic_Stone"; ((Texture)micIconTexture).filterMode = (FilterMode)1; if (!ImageConversion.LoadImage(micIconTexture, array, true)) { Object.Destroy((Object)(object)micIconTexture); micIconTexture = null; ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not decode embedded push-to-talk microphone PNG."); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load push-to-talk microphone icon: " + ex.Message)); } } private void LoadPlayerVolumes() { individualVolumes.Clear(); string[] array = (playerVolumes.Value ?? "").Split(';'); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('='); if (array2.Length == 2 && long.TryParse(array2[0], out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { individualVolumes[result] = Mathf.Clamp(result2 / 100f, 0f, 2f); } } } private float GetPlayerVolume(long uid) { if (!individualVolumes.TryGetValue(uid, out var value)) { return 1f; } return value; } private void SetPlayerVolume(long uid, float value) { individualVolumes[uid] = Mathf.Round(Mathf.Clamp(value, 0f, 2f) * 100f) / 100f; List<string> list = new List<string>(); foreach (KeyValuePair<long, float> individualVolume in individualVolumes) { list.Add(individualVolume.Key + "=" + (individualVolume.Value * 100f).ToString("F0", CultureInfo.InvariantCulture)); } playerVolumes.Value = string.Join(";", list.ToArray()); } private bool IsVanillaPlayerListVisible() { //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) try { Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>(); foreach (Canvas val in array) { if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy) { continue; } Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(false); foreach (Component val2 in componentsInChildren) { if (!Object.op_Implicit((Object)(object)val2) || !val2.gameObject.activeInHierarchy) { continue; } Type type = ((object)val2).GetType(); if (type.Name != "Text" && type.Name != "TextMeshProUGUI" && type.Name != "TMP_Text") { continue; } string text = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetValue(val2, null) as string; if (string.IsNullOrEmpty(text)) { continue; } string text2 = text.Trim().ToLowerInvariant(); if (text2 != "список игроков" && text2 != "player list" && text2 != "players" && text2 != "$menu_players") { continue; } Transform transform = val2.transform; RectTransform val3 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (Object.op_Implicit((Object)(object)val3)) { Rect val4 = ScreenRect(val3, val); if (((Rect)(ref val4)).width > 0f && ((Rect)(ref val4)).yMax < (float)Screen.height * 0.19f && ((Rect)(ref val4)).center.x > (float)Screen.width * 0.25f && ((Rect)(ref val4)).center.x < (float)Screen.width * 0.75f) { return true; } } } } } catch { } return false; } private static Rect ScreenRect(RectTransform rt, Canvas canvas) { //IL_000c: 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_0028: 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_0034: 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) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) rt.GetWorldCorners(screenRectCorners); Camera obj = (((int)canvas.renderMode == 0) ? null : canvas.worldCamera); Vector2 val = RectTransformUtility.WorldToScreenPoint(obj, screenRectCorners[0]); Vector2 val2 = RectTransformUtility.WorldToScreenPoint(obj, screenRectCorners[2]); return Rect.MinMaxRect(val.x, (float)Screen.height - val2.y, val2.x, (float)Screen.height - val.y); } private void RefreshPlayerNames() { if (Time.unscaledTime < nextPlayerScan || (Object)(object)ZNet.instance == (Object)null) { return; } nextPlayerScan = Time.unscaledTime + 3f; try { if (!(typeof(ZNet).GetMethod("GetPeers", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(ZNet.instance, null) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item != null) { Type type = item.GetType(); object obj = type.GetField("m_uid", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item); object obj2 = type.GetField("m_playerName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item); if (obj != null && obj2 is string && !string.IsNullOrEmpty((string)obj2)) { knownNames[Convert.ToInt64(obj)] = (string)obj2; } } } } catch { } } private void LoadGameUiAssets() { try { Font[] array = Resources.FindObjectsOfTypeAll<Font>(); foreach (Font val in array) { if ((Object)(object)val != (Object)null && IsAveriaBoldName(((Object)val).name)) { averiaFont = val; break; } } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i].GetType("TMPro.TMP_FontAsset", throwOnError: false); if (type == null) { continue; } Object[] array2 = Resources.FindObjectsOfTypeAll(type); foreach (Object val2 in array2) { if (!(val2 == (Object)null) && IsAveriaBoldName(val2.name)) { averiaTmpFont = val2; object? obj = type.GetProperty("sourceFontFile", BindingFlags.Instance | BindingFlags.Public)?.GetValue(val2, null); Font val3 = (Font)((obj is Font) ? obj : null); if ((Object)(object)val3 != (Object)null) { averiaFont = val3; } break; } } if (averiaTmpFont != (Object)null) { break; } } if ((Object)(object)averiaFont == (Object)null && averiaTmpFont == (Object)null) { if (Time.unscaledTime < 60f) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"AveriaSansLibre-Bold not loaded yet; will retry after game UI initializes."); } } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Exact AveriaSansLibre-Bold font asset found."); } assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type2 = assemblies[i].GetType("UnityEngine.UI.Slider", throwOnError: false); if (!(type2 == null)) { nativeSliderType = type2; break; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not load Valheim UI assets: " + ex.Message)); } } private static bool IsAveriaBoldName(string name) { if (string.IsNullOrEmpty(name)) { return false; } string text = name.Replace(" ", "").Replace("_", "").Replace("-", "") .ToLowerInvariant(); if (text.Contains("averiasanslibre")) { return text.Contains("bold"); } return false; } private void EnsurePlayerControlsCanvas() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown if ((Object)(object)playerControlsCanvas != (Object)null) { return; } GameObject val = new GameObject("PerfectProximityVoice_PlayerControls"); Object.DontDestroyOnLoad((Object)(object)val); playerControlsCanvas = val.AddComponent<Canvas>(); playerControlsCanvas.renderMode = (RenderMode)0; playerControlsCanvas.sortingOrder = 32000; val.AddComponent<CanvasGroup>(); Type type = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { type = assemblies[i].GetType("UnityEngine.UI.GraphicRaycaster", throwOnError: false); if (type != null) { break; } } if (type != null) { val.AddComponent(type); } } private void DrawPlayerVoiceControls() { //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) if (!NetworkReady() || !IsVanillaPlayerListVisible()) { HideNativeSliders(); return; } RefreshPlayerNames(); if ((Object)(object)averiaFont == (Object)null && Time.unscaledTime >= nextFontRetry) { nextFontRetry = Time.unscaledTime + 2f; LoadGameUiAssets(); } ApplyAveriaToPlayerNames(); EnsurePlayerLabelStyles(); EnsurePlayerControlsCanvas(); HashSet<long> hashSet = new HashSet<long>(); foreach (KeyValuePair<long, string> knownName in knownNames) { if (knownName.Key == LocalUid() || !TryFindPlayerRow(knownName.Value, out var result) || ((Rect)(ref result)).width < 620f || ((Rect)(ref result)).height < 95f) { continue; } hashSet.Add(knownName.Key); float num = Mathf.Min(290f, ((Rect)(ref result)).width * 0.31f); float num2 = ((Rect)(ref result)).x + ((Rect)(ref result)).width * 0.51f - num * 0.5f; float num3 = ((Rect)(ref result)).yMax - 35f; float num4 = num2 + 78f; float num5 = num - 138f; float playerVolume = GetPlayerVolume(knownName.Key); if (nativeSliderType != null && TryCreateNativeSlider(knownName.Key, out var control)) { control.SetVisible(visible: true); control.SetValue(playerVolume); control.SetRect(num4, num3 + 3f, num5, 24f); } else { float num6 = GUI.HorizontalSlider(new Rect(num4, num3 + 7f, num5, 19f), playerVolume, 0f, 2f); if (Mathf.Abs(num6 - playerVolume) > 0.01f) { SetPlayerVolume(knownName.Key, Mathf.Round(num6 * 20f) / 20f); } } Color color = GUI.color; GUI.color = new Color(0.98f, 0.86f, 0.55f, 1f); GUI.Label(new Rect(num2, num3 - 2f, 82f, 23f), "Голос", (playerValueStyle == null) ? playerVoiceStyle : GetVoiceCaptionStyle()); GUI.color = new Color(1f, 0.88f, 0.52f, 1f); GUI.Label(new Rect(num2 + num - 51f, num3 - 2f, 55f, 23f), Mathf.RoundToInt(playerVolume * 100f) + "%", playerValueStyle); GUI.color = color; } foreach (KeyValuePair<long, NativeSliderControl> nativeSlider in nativeSliders) { nativeSlider.Value.SetVisible(hashSet.Contains(nativeSlider.Key)); } } private void ApplyAveriaToPlayerNames() { //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)averiaFont == (Object)null && averiaTmpFont == (Object)null) { return; } HashSet<string> hashSet = new HashSet<string>(knownNames.Values, StringComparer.OrdinalIgnoreCase); string localPlayerName = GetLocalPlayerName(); if (!string.IsNullOrEmpty(localPlayerName)) { hashSet.Add(localPlayerName); } if (hashSet.Count == 0) { return; } try { Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>(); foreach (Canvas val in array) { if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy) { continue; } Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(false); foreach (Component val2 in componentsInChildren) { if (!Object.op_Implicit((Object)(object)val2) || !val2.gameObject.activeInHierarchy) { continue; } Type type = ((object)val2).GetType(); if (type.Name != "Text" && type.Name != "TextMeshProUGUI" && type.Name != "TMP_Text") { continue; } string text = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetValue(val2, null) as string; if (string.IsNullOrEmpty(text)) { continue; } bool flag = text.IndexOf("(Host)", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("(Хост)", StringComparison.OrdinalIgnoreCase) >= 0; foreach (string item in hashSet) { if (!string.IsNullOrEmpty(item) && text.IndexOf(item, StringComparison.OrdinalIgnoreCase) >= 0) { flag = true; break; } } if (!flag) { continue; } Transform val3 = val2.transform; int num = 0; while (num < 5 && (Object)(object)val3 != (Object)null) { RectTransform val4 = (RectTransform)(object)((val3 is RectTransform) ? val3 : null); if (Object.op_Implicit((Object)(object)val4)) { Rect val5 = ScreenRect(val4, val); if (!(((Rect)(ref val5)).width < 620f) && !(((Rect)(ref val5)).height < 95f) && !(((Rect)(ref val5)).height > 180f) && !(((Rect)(ref val5)).y < 0f) && !(((Rect)(ref val5)).yMax > (float)Screen.height)) { ApplyAveriaFont(val2); break; } } num++; val3 = val3.parent; } } } } catch (Exception ex) { if (Time.frameCount % 600 == 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not apply Averia to player names: " + ex.Message)); } } } private string GetLocalPlayerName() { try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return null; } Type type = ((object)localPlayer).GetType(); MethodInfo method = type.GetMethod("GetPlayerName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { return method.Invoke(localPlayer, null) as string; } return type.GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(localPlayer) as string; } catch { return null; } } private void ApplyAveriaFont(Component textComponent) { if ((Object)(object)textComponent == (Object)null) { return; } try { PropertyInfo property = ((object)textComponent).GetType().GetProperty("font", BindingFlags.Instance | BindingFlags.Public); if (!(property == null) && property.CanWrite) { if ((Object)(object)averiaFont != (Object)null && property.PropertyType.IsInstanceOfType(averiaFont)) { property.SetValue(textComponent, averiaFont, null); } else if (averiaTmpFont != (Object)null && property.PropertyType.IsInstanceOfType(averiaTmpFont)) { property.SetValue(textComponent, averiaTmpFont, null); } } } catch { } } private GUIStyle GetVoiceCaptionStyle() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown if (playerVoiceStyle == null) { playerVoiceStyle = new GUIStyle(playerValueStyle); } playerVoiceStyle.font = playerValueStyle.font; playerVoiceStyle.fontSize = playerValueStyle.fontSize; playerVoiceStyle.fontStyle = playerValueStyle.fontStyle; playerVoiceStyle.alignment = (TextAnchor)3; playerVoiceStyle.normal.textColor = playerValueStyle.normal.textColor; return playerVoiceStyle; } private void EnsurePlayerLabelStyles() { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Expected O, but got Unknown if (playerLabelStyle != null) { if ((Object)(object)averiaFont != (Object)null && (Object)(object)playerLabelStyle.font != (Object)(object)averiaFont) { playerLabelStyle.font = averiaFont; playerValueStyle.font = averiaFont; playerLabelStyle.fontStyle = (FontStyle)1; playerValueStyle.fontStyle = (FontStyle)1; if (playerVoiceStyle != null) { playerVoiceStyle.font = averiaFont; playerVoiceStyle.fontStyle = (FontStyle)1; } } return; } playerLabelStyle = new GUIStyle(GUI.skin.label); playerLabelStyle.fontSize = 16; playerLabelStyle.fontStyle = (FontStyle)1; playerLabelStyle.alignment = (TextAnchor)3; playerLabelStyle.normal.textColor = new Color(0.98f, 0.86f, 0.55f, 1f); playerValueStyle = new GUIStyle(playerLabelStyle); playerValueStyle.alignment = (TextAnchor)5; playerValueStyle.fontStyle = (FontStyle)1; playerValueStyle.normal.textColor = new Color(1f, 0.88f, 0.52f, 1f); playerVoiceStyle = new GUIStyle(playerValueStyle); playerVoiceStyle.alignment = (TextAnchor)3; playerVoiceStyle.fontStyle = (FontStyle)1; if ((Object)(object)averiaFont != (Object)null) { playerLabelStyle.font = averiaFont; playerValueStyle.font = averiaFont; playerVoiceStyle.font = averiaFont; } } private bool TryCreateNativeSlider(long uid, out NativeSliderControl control) { if (nativeSliders.TryGetValue(uid, out control)) { return true; } try { Type type = nativeSliderType as Type; if (type == null) { return false; } Object[] array = Resources.FindObjectsOfTypeAll(type); Component val = null; Object[] array2 = array; foreach (Object obj in array2) { Component val2 = (Component)(object)((obj is Component) ? obj : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val2.gameObject == (Object)null || ((Object)val2.gameObject).name.StartsWith("PPV_PlayerVolume_", StringComparison.Ordinal)) { continue; } RectTransform component = val2.GetComponent<RectTransform>(); Transform val3 = ((component != null) ? ((Transform)component).Find("Handle Slide Area/Handle") : null); if ((Object)(object)val3 != (Object)null) { Image component2 = ((Component)val3).GetComponent<Image>(); if ((Object)(object)((component2 != null) ? component2.sprite : null) != (Object)null) { val = val2; break; } } if ((Object)(object)val == (Object)null && ((Object)val2.gameObject).name.IndexOf("slider", StringComparison.OrdinalIgnoreCase) >= 0) { val = val2; } } if ((Object)(object)val == (Object)null) { return false; } EnsurePlayerControlsCanvas(); GameObject val4 = Object.Instantiate<GameObject>(val.gameObject, ((Component)playerControlsCanvas).transform, false); ((Object)val4).name = "PPV_PlayerVolume_" + uid; val4.SetActive(true); control = new NativeSliderControl(this, uid, val4, type); nativeSliders[uid] = control; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Created isolated volume slider for player UID " + uid)); return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not clone Valheim slider: " + ex.Message)); return false; } } private void HideNativeSliders() { foreach (KeyValuePair<long, NativeSliderControl> nativeSlider in nativeSliders) { nativeSlider.Value.SetVisible(visible: false); } } private void OnNativeSliderChanged(long uid, float value) { SetPlayerVolume(uid, Mathf.Round(Mathf.Clamp(value, 0f, 2f) * 100f) / 100f); } private bool TryFindPlayerRow(string playerName, out Rect result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) result = default(Rect); if (string.IsNullOrEmpty(playerName)) { return false; } try { Canvas[] array = Resources.FindObjectsOfTypeAll<Canvas>(); foreach (Canvas val in array) { if (!Object.op_Implicit((Object)(object)val) || !((Component)val).gameObject.activeInHierarchy) { continue; } Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(false); foreach (Component val2 in componentsInChildren) { if (!Object.op_Implicit((Object)(object)val2) || !val2.gameObject.activeInHierarchy) { continue; } Type type = ((object)val2).GetType(); if (type.Name != "Text" && type.Name != "TextMeshProUGUI" && type.Name != "TMP_Text") { continue; } string text = type.GetProperty("text", BindingFlags.Instance | BindingFlags.Public)?.GetValue(val2, null) as string; if (string.IsNullOrEmpty(text) || text.IndexOf(playerName, StringComparison.OrdinalIgnoreCase) < 0) { continue; } Transform val3 = val2.transform; int num = 0; while (num < 5 && (Object)(object)val3 != (Object)null) { RectTransform val4 = (RectTransform)(object)((val3 is RectTransform) ? val3 : null); if (Object.op_Implicit((Object)(object)val4)) { Rect val5 = ScreenRect(val4, val); if (((Rect)(ref val5)).width >= 620f && ((Rect)(ref val5)).height >= 95f && ((Rect)(ref val5)).height <= 180f && ((Rect)(ref val5)).y >= 0f && ((Rect)(ref val5)).yMax <= (float)Screen.height) { result = val5; return true; } } num++; val3 = val3.parent; } } } } catch (Exception ex) { if (Time.frameCount % 600 == 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Player row UI: " + ex.Message)); } } return false; } private bool NetworkReady() { if ((Object)(object)ZNet.instance != (Object)null) { return ZRoutedRpc.instance != null; } return false; } private void TryRegister() { object instance = ZRoutedRpc.instance; if (instance == null || instance == registeredOn) { return; } try { Type type = instance.GetType(); MethodInfo methodInfo = null; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (methodInfo2.Name == "Register" && methodInfo2.IsGenericMethodDefinition && methodInfo2.GetGenericArguments().Length == 1 && methodInfo2.GetParameters().Length == 2) { methodInfo = methodInfo2; break; } } if (methodInfo == null) { throw new MissingMethodException("ZRoutedRpc.Register<T>"); } Action<long, ZPackage> action = Receive; methodInfo.MakeGenericMethod(typeof(ZPackage)).Invoke(instance, new object[2] { "PPV_Audio_018", action }); invokeRpc = null; methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo3 in methods) { if (methodInfo3.Name == "InvokeRoutedRPC" && methodInfo3.GetParameters().Length == 3 && methodInfo3.GetParameters()[1].ParameterType == typeof(string)) { invokeRpc = methodInfo3; break; } } if (invokeRpc == null) { throw new MissingMethodException("ZRoutedRpc.InvokeRoutedRPC"); } registeredOn = instance; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Voice RPC registered"); } catch (Exception ex) { if (!warned) { ((BaseUnityPlugin)this).Logger.LogError((object)("Voice RPC registration failed: " + ex)); warned = true; } } } private void LogMicrophones() { string[] devices = Microphone.devices; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Available microphone devices (" + devices.Length + "):")); string[] array = devices; foreach (string text in array) { ((BaseUnityPlugin)this).Logger.LogInfo((object)(" Microphone: " + text)); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Configured microphone: " + (string.IsNullOrWhiteSpace(microphoneDevice.Value) ? "<system default>" : microphoneDevice.Value))); } private void StartMicrophone() { if ((Object)(object)microphone != (Object)null) { return; } string[] devices = Microphone.devices; if (devices.Length == 0) { return; } string text = (microphoneDevice.Value ?? "").Trim(); string text2 = null; if (text.Length > 0) { bool flag = false; string[] array = devices; foreach (string text3 in array) { if (string.Equals(text3, text, StringComparison.OrdinalIgnoreCase)) { text2 = text3; flag = true; break; } } if (!flag) { if (Time.unscaledTime >= nextMicLog) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Configured microphone not found: " + text + ". Voice capture disabled until corrected.")); LogMicrophones(); nextMicLog = Time.unscaledTime + 15f; } return; } } try { micName = text2; microphone = Microphone.Start(micName, true, 2, 16000); lastMicPosition = 0; if ((Object)(object)microphone != (Object)null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Microphone started: " + (micName ?? "<system default>"))); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Microphone.Start returned null"); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Microphone start failed: " + ex)); microphone = null; } } private void StopMicrophone() { if (!((Object)(object)microphone == (Object)null)) { try { Microphone.End(micName); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Microphone stop: " + ex.Message)); } microphone = null; micName = null; lastMicPosition = 0; } } private long LocalUid() { if ((Object)(object)ZNet.instance == (Object)null) { return 0L; } try { MethodInfo method = typeof(ZNet).GetMethod("GetUID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); return (method == null) ? 0 : Convert.ToInt64(method.Invoke(ZNet.instance, null)); } catch { return 0L; } } private bool IsOwnVoice(long sender) { if ((Object)(object)ZNet.instance == (Object)null) { return false; } try { MethodInfo method = typeof(ZNet).GetMethod("GetUID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method != null) { return Convert.ToInt64(method.Invoke(ZNet.instance, null)) == sender; } } catch (Exception ex) { if (!warned) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Cannot check local voice UID: " + ex.Message)); warned = true; } } return false; } private void CaptureAndSend() { //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Expected O, but got Unknown //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) StartMicrophone(); if ((Object)(object)microphone == (Object)null || invokeRpc == null) { return; } int position = Microphone.GetPosition(micName); if (position < 0) { return; } int samples = microphone.samples; int num = (position - lastMicPosition + samples) % samples; if (num < 1600) { return; } if (num > 8000) { lastMicPosition = (position - 1600 + samples) % samples; } int num2 = Math.Max(1, microphone.channels); if (captureBufferChannels != num2 || captureInterleaved == null || captureInterleaved.Length != 1600 * num2) { captureInterleaved = new float[1600 * num2]; captureBufferChannels = num2; } if (captureSamples == null || captureSamples.Length != 1600) { captureSamples = new float[1600]; } if (captureEncoded == null || captureEncoded.Length != 1600) { captureEncoded = new byte[1600]; } if (!microphone.GetData(captureInterleaved, lastMicPosition)) { return; } lastMicPosition = (lastMicPosition + 1600) % samples; int value = microphoneChannel.Value; int num3 = Mathf.Clamp(value - 1, 0, num2 - 1); for (int i = 0; i < 1600; i++) { float num4 = 0f; int num5 = i * num2; for (int j = 0; j < num2; j++) { num4 += captureInterleaved[num5 + j]; } captureSamples[i] = ((value == 0) ? (num4 / (float)num2) : captureInterleaved[num5 + num3]); } for (int k = 0; k < 1600; k++) { captureEncoded[k] = MuLawEncode(captureSamples[k]); } try { ZPackage val = new ZPackage(); Vector3 position2 = ((Component)Player.m_localPlayer).transform.position; val.Write(sessionToken); val.Write(position2.x); val.Write(position2.y); val.Write(position2.z); val.Write(captureEncoded.Length); val.Write(captureEncoded); invokeRpc.Invoke(registeredOn, new object[3] { 0L, "PPV_Audio_018", new object[1] { val } }); } catch (Exception ex) { if (!warned) { ((BaseUnityPlugin)this).Logger.LogError((object)("Voice send failed: " + ex)); warned = true; } } } private void Receive(long sender, ZPackage packet) { //IL_005c: 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) //IL_0076: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) if (packet == null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } try { string text = packet.ReadString(); if (text == sessionToken || string.IsNullOrEmpty(text) || text.Length != 32) { return; } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(packet.ReadSingle(), packet.ReadSingle(), packet.ReadSingle()); if (float.IsNaN(val.x) || float.IsNaN(val.y) || float.IsNaN(val.z) || float.IsInfinity(val.x) || float.IsInfinity(val.y) || float.IsInfinity(val.z) || Vector3.Distance(val, ((Component)Player.m_localPlayer).transform.position) > range.Value + 2f || packet.ReadInt() != 1600) { return; } byte[] array = packet.ReadByteArray(); if (array != null && array.Length == 1600) { if (!speakers.TryGetValue(sender, out var value)) { value = new Speaker(sender, 16000, range.Value); speakers[sender] = value; } ((Component)value.source).transform.position = val; value.lastLocated = Time.unscaledTime; value.Push(array, Mathf.Clamp(voiceGain.Value, 0.1f, 24f)); } } catch (Exception ex) { if (!warned) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Voice receive failed: " + ex)); warned = true; } } } private bool FindPeerPosition(long uid, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if ((Object)(object)ZNet.instance == (Object)null) { return false; } try { MethodInfo method = typeof(ZNet).GetMethod("GetPeer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(long) }, null); object obj = ((method != null) ? method.Invoke(ZNet.instance, new object[1] { uid }) : null); if (obj == null) { return false; } FieldInfo field = obj.GetType().GetField("m_refPos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { return false; } position = (Vector3)field.GetValue(obj); return true; } catch { return false; } } private static byte MuLawEncode(float value) { int num = Mathf.Clamp(Mathf.RoundToInt(value * 32767f), -32768, 32767); int num2 = (num >> 8) & 0x80; if (num2 != 0) { num = -num; } num = Math.Min(32635, num) + 132; int num3 = 7; int num4 = 16384; while (num3 > 0 && (num & num4) == 0) { num3--; num4 >>= 1; } int num5 = (num >> num3 + 3) & 0xF; return (byte)(~(num2 | (num3 << 4) | num5)); } private static float MuLawDecode(byte encoded) { int num = ~encoded & 0xFF; int num2 = ((num & 0xF) << 3) + 132; num2 <<= (num >> 4) & 7; return Mathf.Clamp((float)(((num & 0x80) != 0) ? (132 - num2) : (num2 - 132)) / 32768f, -1f, 1f); } private void OnDestroy() { if ((Object)(object)micIconTexture != (Object)null) { Object.Destroy((Object)(object)micIconTexture); micIconTexture = null; } StopMicrophone(); foreach (Speaker value in speakers.Values) { if (Object.op_Implicit((Object)(object)value.source)) { Object.Destroy((Object)(object)((Component)value.source).gameObject); } } speakers.Clear(); if ((Object)(object)playerControlsCanvas != (Object)null) { Object.Destroy((Object)(object)((Component)playerControlsCanvas).gameObject); } nativeSliders.Clear(); } } }