using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using SteelSpeaker.Config;
using SteelSpeaker.Dev;
using SteelSpeaker.Interaction;
using SteelSpeaker.Managers;
using SteelSpeaker.Models;
using SteelSpeaker.Networking;
using SteelSpeaker.Prefabs;
using SteelSpeaker.UI;
using SteelSpeaker.Utils;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.coleman71803.SteelSpeaker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+de00c14257a3a336bdc6baceca0427bf099f56e1")]
[assembly: AssemblyProduct("com.github.coleman71803.SteelSpeaker")]
[assembly: AssemblyTitle("SteelSpeaker")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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 BepInEx
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class BepInAutoPluginAttribute : Attribute
{
public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace BepInEx.Preloader.Core.Patching
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[Conditional("CodeGeneration")]
internal sealed class PatcherAutoPluginAttribute : Attribute
{
public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
{
}
}
}
namespace SteelSpeaker
{
[BepInPlugin("com.github.coleman71803.SteelSpeaker", "SteelSpeaker", "0.2.0")]
public class Plugin : BaseUnityPlugin
{
public const string Id = "com.github.coleman71803.SteelSpeaker";
internal static ManualLogSource Log { get; private set; }
public static string Name => "SteelSpeaker";
public static string Version => "0.2.0";
private void Awake()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Expected O, but got Unknown
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
ModConfig.Init(((BaseUnityPlugin)this).Config);
GameObject val = new GameObject("SteelSpeaker.ConfigHost");
Object.DontDestroyOnLoad((Object)(object)val);
val.AddComponent<MountOffsetTuner>();
GameObject val2 = new GameObject("SteelSpeaker.Core");
Object.DontDestroyOnLoad((Object)(object)val2);
val2.AddComponent<SpeakerBehavior>();
val2.AddComponent<SpeakerManager>();
val2.AddComponent<SpeakerNetSync>();
val2.AddComponent<EscapeMenuVolumeUI>();
val2.AddComponent<UIHotkeyListener>();
Log.LogInfo((object)("Plugin " + Name + " loaded. Core systems initialized (BackQuote ` UI toggle)."));
}
}
}
namespace SteelSpeaker.Utils
{
public static class AttachmentService
{
public static void Attach(GameObject item, Transform root, string bonePath, Vector3 localPos, Vector3 localEuler)
{
//IL_0057: 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)
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
Transform val = (Transform)(string.IsNullOrEmpty(bonePath) ? ((object)root) : ((object)(root.Find(bonePath) ?? root)));
Rigidbody component = item.GetComponent<Rigidbody>();
Collider component2 = item.GetComponent<Collider>();
if (Object.op_Implicit((Object)(object)component))
{
component.isKinematic = true;
}
if (Object.op_Implicit((Object)(object)component2))
{
component2.enabled = false;
}
item.transform.SetParent(val, false);
item.transform.localPosition = localPos;
item.transform.localRotation = Quaternion.Euler(localEuler);
}
public static void Detach(GameObject item, Vector3 tossImpulse)
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
Rigidbody component = item.GetComponent<Rigidbody>();
Collider component2 = item.GetComponent<Collider>();
item.transform.SetParent((Transform)null, true);
if (Object.op_Implicit((Object)(object)component))
{
component.isKinematic = false;
component.AddForce(tossImpulse, (ForceMode)1);
}
if (Object.op_Implicit((Object)(object)component2))
{
component2.enabled = true;
}
}
}
public static class HostDetector
{
public static bool IsHost()
{
try
{
Type type = Type.GetType("Unity.Netcode.NetworkManager, Unity.Netcode.Runtime");
object obj = (type?.GetProperty("Singleton", BindingFlags.Static | BindingFlags.Public))?.GetValue(null);
PropertyInfo propertyInfo = type?.GetProperty("IsServer", BindingFlags.Instance | BindingFlags.Public);
if (obj != null && propertyInfo != null && (bool)(propertyInfo.GetValue(obj) ?? ((object)false)))
{
return true;
}
}
catch
{
}
try
{
PropertyInfo propertyInfo2 = Type.GetType("Photon.Pun.PhotonNetwork, PhotonUnityNetworking")?.GetProperty("IsMasterClient", BindingFlags.Static | BindingFlags.Public);
if (propertyInfo2 != null && (bool)(propertyInfo2.GetValue(null) ?? ((object)false)))
{
return true;
}
}
catch
{
}
return false;
}
}
public static class SpeakerAttach
{
public static bool AttachToHook(GameObject speakerInstance, Transform characterHook, string clipAnchorPath = "ClipAnchor")
{
//IL_00d5: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_012e: Unknown result type (might be due to invalid IL or missing references)
//IL_0142: Unknown result type (might be due to invalid IL or missing references)
//IL_0156: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)speakerInstance == (Object)null || (Object)(object)characterHook == (Object)null)
{
Debug.LogError((object)"[SteelSpeaker] SpeakerAttach.AttachToHook called with null args");
return false;
}
Rigidbody val = speakerInstance.GetComponent<Rigidbody>() ?? speakerInstance.AddComponent<Rigidbody>();
val.mass = 1.4f;
val.drag = 0.1f;
val.angularDrag = 0.1f;
val.useGravity = true;
val.interpolation = (RigidbodyInterpolation)1;
val.isKinematic = false;
Rigidbody val2 = ((Component)characterHook).GetComponent<Rigidbody>() ?? ((Component)characterHook).gameObject.AddComponent<Rigidbody>();
val2.isKinematic = true;
Transform val3 = speakerInstance.transform.Find(clipAnchorPath);
if (!Object.op_Implicit((Object)(object)val3))
{
Debug.LogError((object)("[SteelSpeaker] ClipAnchor not found at path '" + clipAnchorPath + "'."));
return false;
}
ConfigurableJoint val4 = speakerInstance.GetComponent<ConfigurableJoint>() ?? speakerInstance.AddComponent<ConfigurableJoint>();
((Joint)val4).connectedBody = val2;
((Joint)val4).autoConfigureConnectedAnchor = false;
((Joint)val4).anchor = speakerInstance.transform.InverseTransformPoint(val3.position);
((Joint)val4).connectedAnchor = Vector3.zero;
val4.xMotion = (ConfigurableJointMotion)0;
val4.yMotion = (ConfigurableJointMotion)0;
val4.zMotion = (ConfigurableJointMotion)0;
val4.angularXMotion = (ConfigurableJointMotion)1;
val4.angularYMotion = (ConfigurableJointMotion)1;
val4.angularZMotion = (ConfigurableJointMotion)1;
SoftJointLimit val5 = default(SoftJointLimit);
((SoftJointLimit)(ref val5)).limit = -25f;
val4.lowAngularXLimit = val5;
((SoftJointLimit)(ref val5)).limit = 25f;
val4.highAngularXLimit = val5;
((SoftJointLimit)(ref val5)).limit = 45f;
val4.angularYLimit = val5;
((SoftJointLimit)(ref val5)).limit = 45f;
val4.angularZLimit = val5;
val4.projectionMode = (JointProjectionMode)1;
val4.projectionAngle = 20f;
val4.projectionDistance = 0.2f;
Collider val6 = (Collider)(((object)speakerInstance.GetComponent<Collider>()) ?? ((object)speakerInstance.AddComponent<BoxCollider>()));
val6.enabled = true;
return true;
}
public static void Detach(GameObject speakerInstance)
{
if (!((Object)(object)speakerInstance == (Object)null))
{
ConfigurableJoint component = speakerInstance.GetComponent<ConfigurableJoint>();
if ((Object)(object)component != (Object)null)
{
Object.Destroy((Object)(object)component);
}
Rigidbody component2 = speakerInstance.GetComponent<Rigidbody>();
if ((Object)(object)component2 != (Object)null)
{
component2.isKinematic = false;
}
}
}
}
}
namespace SteelSpeaker.UI
{
public class EscapeMenuVolumeUI : MonoBehaviour
{
[CompilerGenerated]
private sealed class <>c__DisplayClass27_0
{
public List<YouTubeService.SearchResult> res;
public EscapeMenuVolumeUI <>4__this;
public string query;
public bool done;
internal void <SearchRoutine>b__0()
{
try
{
res = <>4__this._yt.Search(query);
}
catch
{
res = new List<YouTubeService.SearchResult>();
}
finally
{
done = true;
}
}
}
[CompilerGenerated]
private sealed class <>c__DisplayClass29_0
{
public string mediaUrl;
public EscapeMenuVolumeUI <>4__this;
public string url;
public bool resolveDone;
internal void <QueueRoutine>b__0()
{
try
{
mediaUrl = <>4__this._yt.ResolveStreamUrl(url);
}
catch
{
mediaUrl = null;
}
finally
{
resolveDone = true;
}
}
}
[CompilerGenerated]
private sealed class <>c__DisplayClass29_1
{
public string localPath;
public bool done;
public <>c__DisplayClass29_0 CS$<>8__locals1;
internal void <QueueRoutine>b__1()
{
localPath = CS$<>8__locals1.<>4__this._yt.EnsureLocalAudio(CS$<>8__locals1.url);
done = true;
}
}
[CompilerGenerated]
private sealed class <QueueRoutine>d__29 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public EscapeMenuVolumeUI <>4__this;
public YouTubeService.SearchResult r;
private <>c__DisplayClass29_0 <>8__1;
private <>c__DisplayClass29_1 <>8__2;
private string <finalUrl>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <QueueRoutine>d__29(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>8__1 = null;
<>8__2 = null;
<finalUrl>5__2 = null;
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
EscapeMenuVolumeUI escapeMenuVolumeUI = <>4__this;
string text;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<>8__1 = new <>c__DisplayClass29_0();
<>8__1.<>4__this = <>4__this;
<>8__1.url = "https://www.youtube.com/watch?v=" + r.videoId;
<>8__1.mediaUrl = null;
<>8__1.resolveDone = false;
Task.Run(delegate
{
try
{
<>8__1.mediaUrl = <>8__1.<>4__this._yt.ResolveStreamUrl(<>8__1.url);
}
catch
{
<>8__1.mediaUrl = null;
}
finally
{
<>8__1.resolveDone = true;
}
});
goto IL_00ad;
case 1:
<>1__state = -1;
goto IL_00ad;
case 2:
{
<>1__state = -1;
goto IL_01a5;
}
IL_00ad:
if (!<>8__1.resolveDone)
{
<>2__current = null;
<>1__state = 1;
return true;
}
if (string.IsNullOrEmpty(<>8__1.mediaUrl))
{
Debug.LogError((object)"[SteelSpeaker] Failed to resolve audio stream.");
escapeMenuVolumeUI._queueInProgress = false;
return false;
}
<finalUrl>5__2 = <>8__1.mediaUrl;
text = <>8__1.mediaUrl.ToLowerInvariant();
if (text.EndsWith(".ogg") || text.EndsWith(".mp3") || text.EndsWith(".wav"))
{
break;
}
if (escapeMenuVolumeUI._yt.HasFfmpeg())
{
<>8__2 = new <>c__DisplayClass29_1();
<>8__2.CS$<>8__locals1 = <>8__1;
<>8__2.done = false;
<>8__2.localPath = null;
Task.Run(delegate
{
<>8__2.localPath = <>8__2.CS$<>8__locals1.<>4__this._yt.EnsureLocalAudio(<>8__2.CS$<>8__locals1.url);
<>8__2.done = true;
});
goto IL_01a5;
}
Debug.LogWarning((object)"[SteelSpeaker] Stream format may be unsupported by Unity. Place ffmpeg in SteelSpeaker/bin to enable mp3 conversion.");
break;
IL_01a5:
if (!<>8__2.done)
{
<>2__current = null;
<>1__state = 2;
return true;
}
if (!string.IsNullOrEmpty(<>8__2.localPath))
{
<finalUrl>5__2 = "file://" + <>8__2.localPath.Replace("\\", "/");
}
else
{
Debug.LogError((object)"[SteelSpeaker] Conversion failed; attempting to play original stream (may fail).");
}
<>8__2 = null;
break;
}
YouTubeTrack t = new YouTubeTrack(r.videoId, r.title, <finalUrl>5__2, r.duration);
escapeMenuVolumeUI._net.EnqueueTrack(t);
escapeMenuVolumeUI._queueInProgress = false;
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <SearchRoutine>d__27 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public EscapeMenuVolumeUI <>4__this;
public string query;
private <>c__DisplayClass27_0 <>8__1;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <SearchRoutine>d__27(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>8__1 = null;
<>1__state = -2;
}
private bool MoveNext()
{
int num = <>1__state;
EscapeMenuVolumeUI escapeMenuVolumeUI = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
<>8__1 = new <>c__DisplayClass27_0();
<>8__1.<>4__this = <>4__this;
<>8__1.query = query;
escapeMenuVolumeUI._isSearching = true;
escapeMenuVolumeUI._searchStatus = "Searching...";
<>8__1.res = null;
<>8__1.done = false;
Task.Run(delegate
{
try
{
<>8__1.res = <>8__1.<>4__this._yt.Search(<>8__1.query);
}
catch
{
<>8__1.res = new List<YouTubeService.SearchResult>();
}
finally
{
<>8__1.done = true;
}
});
break;
case 1:
<>1__state = -1;
break;
}
if (!<>8__1.done)
{
<>2__current = null;
<>1__state = 1;
return true;
}
escapeMenuVolumeUI._results = <>8__1.res ?? new List<YouTubeService.SearchResult>();
escapeMenuVolumeUI._searchStatus = ((escapeMenuVolumeUI._results.Count == 0) ? "No results" : string.Empty);
escapeMenuVolumeUI._isSearching = false;
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private bool _show;
private string _query = string.Empty;
private Vector2 _scroll;
private List<YouTubeService.SearchResult> _results = new List<YouTubeService.SearchResult>();
private YouTubeService _yt = new YouTubeService();
private SpeakerNetSync _net;
private bool? _ytOk;
private bool? _ffOk;
private bool _prevCursorVisible;
private CursorLockMode _prevCursorLock;
private bool _queueInProgress;
private bool _isSearching;
private string _searchStatus = string.Empty;
private int _volSlider = 50;
private float _baselineVol = 1f;
private bool _volInitialized;
private KeyCode _toggleKey = (KeyCode)291;
private KeyboardShortcut _toggleShortcut = new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>());
private const string BuildMark = "MARK_2025-09-01_12-20";
public static EscapeMenuVolumeUI? Instance { get; private set; }
private void Awake()
{
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: 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)
//IL_0046: 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_007e: Unknown result type (might be due to invalid IL or missing references)
try
{
Instance = this;
_net = Object.FindObjectOfType<SpeakerNetSync>() ?? ((Component)this).gameObject.AddComponent<SpeakerNetSync>();
try
{
KeyboardShortcut toggleShortcut = (KeyboardShortcut)(((??)ModConfig.UIToggleShortcut?.Value) ?? new KeyboardShortcut((KeyCode)0, Array.Empty<KeyCode>()));
if ((int)((KeyboardShortcut)(ref toggleShortcut)).MainKey != 0)
{
_toggleShortcut = toggleShortcut;
}
else
{
string value = ModConfig.ToggleKey?.Value ?? "F10";
if (!Enum.TryParse<KeyCode>(value, ignoreCase: true, out _toggleKey))
{
_toggleKey = (KeyCode)291;
}
}
}
catch
{
_toggleKey = (KeyCode)291;
}
Debug.Log((object)"[SteelSpeaker] UI ready. Toggle with BackQuote (`) only.");
Task.Run(delegate
{
try
{
_ytOk = _yt.HasBinary();
}
catch
{
_ytOk = false;
}
});
Task.Run(delegate
{
try
{
_ffOk = _yt.HasFfmpeg();
}
catch
{
_ffOk = false;
}
});
}
catch (Exception ex)
{
Debug.LogError((object)("[SteelSpeaker] UI Awake error: " + ex));
}
}
private void Update()
{
}
private void OnGUI()
{
//IL_06c1: Unknown result type (might be due to invalid IL or missing references)
//IL_06c7: Unknown result type (might be due to invalid IL or missing references)
//IL_06ce: 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_002e: 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_0035: 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_0050: 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_0065: 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_0079: 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_03cf: Unknown result type (might be due to invalid IL or missing references)
//IL_03e7: Unknown result type (might be due to invalid IL or missing references)
//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
if (!_show)
{
return;
}
float num = 520f;
float num2 = 420f;
Rect val = default(Rect);
((Rect)(ref val))..ctor(20f, 20f, num, num2);
Color color = GUI.color;
Color backgroundColor = GUI.backgroundColor;
Color contentColor = GUI.contentColor;
try
{
GUI.color = new Color(0f, 0f, 0f, 0.7f);
GUI.Box(val, GUIContent.none);
GUI.color = Color.white;
GUI.backgroundColor = Color.white;
GUI.contentColor = Color.white;
GUILayout.BeginArea(val);
GUILayout.Label("SteelSpeaker (`) MARK_2025-09-01_12-20", Array.Empty<GUILayoutOption>());
SpeakerManager instance = SpeakerManager.Instance;
if ((Object)(object)instance != (Object)null)
{
if (!_volInitialized)
{
try
{
_baselineVol = Mathf.Clamp01(instance.GetVolume());
}
catch
{
_baselineVol = 1f;
}
_volSlider = 50;
_volInitialized = true;
}
string text = ((instance.Current != null) ? instance.Current.Title : "<none>");
string text2 = (instance.IsPaused ? "Paused" : (instance.IsPlaying ? "Playing" : "Idle"));
GUILayout.Label("Now: " + text + " [" + text2 + "]", Array.Empty<GUILayoutOption>());
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
if (!instance.IsPlaying)
{
if (GUILayout.Button("Play", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }))
{
try
{
if (PhotonNetwork.IsConnected && !PhotonNetwork.IsMasterClient)
{
_net.RequestPlay();
}
else
{
SpeakerManager.Instance?.StartOrResume();
if (PhotonNetwork.IsConnected)
{
_net.BroadcastPause(paused: false);
}
}
}
catch
{
SpeakerManager.Instance?.StartOrResume();
}
}
}
else if (GUILayout.Button(instance.IsPaused ? "Resume" : "Pause", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }))
{
try
{
if (PhotonNetwork.IsConnected && !PhotonNetwork.IsMasterClient)
{
_net.RequestTogglePause();
}
else
{
SpeakerManager instance2 = SpeakerManager.Instance;
instance2?.TogglePause();
if (PhotonNetwork.IsConnected && (Object)(object)instance2 != (Object)null)
{
_net.BroadcastPause(instance2.IsPaused);
}
}
}
catch
{
SpeakerManager.Instance?.TogglePause();
}
}
if (GUILayout.Button("Skip", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }))
{
try
{
if (PhotonNetwork.IsConnected && !PhotonNetwork.IsMasterClient)
{
_net.RequestSkip();
}
else
{
SpeakerManager.Instance?.Skip();
}
}
catch
{
SpeakerManager.Instance?.Skip();
}
}
if (GUILayout.Button("Clear", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }))
{
_query = string.Empty;
_results.Clear();
_searchStatus = string.Empty;
}
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
GUI.enabled = !_queueInProgress;
_query = GUILayout.TextField(_query, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num - 140f) });
if (GUILayout.Button(_queueInProgress ? "Busy..." : "Search", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }))
{
DoSearch();
}
GUI.enabled = true;
GUILayout.EndHorizontal();
if (!string.IsNullOrEmpty(_searchStatus))
{
GUILayout.Label(_searchStatus, Array.Empty<GUILayoutOption>());
}
GUILayout.Space(6f);
_scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(140f) });
foreach (YouTubeService.SearchResult result in _results)
{
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
GUILayout.Label(Truncate(result.title, 48), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num - 180f) });
GUILayout.Label(FormatDur(result.duration), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
bool enabled = GUI.enabled;
GUI.enabled = !_queueInProgress;
if (GUILayout.Button("Queue", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }))
{
Queue(result);
}
GUI.enabled = enabled;
GUILayout.EndHorizontal();
}
GUILayout.EndScrollView();
GUILayout.Space(6f);
if ((Object)(object)instance != (Object)null)
{
List<YouTubeTrack> queueSnapshot = instance.GetQueueSnapshot();
GUILayout.Label("Queue:", Array.Empty<GUILayoutOption>());
if (queueSnapshot.Count == 0)
{
GUILayout.Label("<empty>", Array.Empty<GUILayoutOption>());
}
for (int i = 0; i < queueSnapshot.Count; i++)
{
YouTubeTrack youTubeTrack = queueSnapshot[i];
GUILayout.Label($"{i + 1}. {Truncate(youTubeTrack.Title, 56)}", Array.Empty<GUILayoutOption>());
}
GUILayout.Space(6f);
}
if (!_ffOk.HasValue)
{
GUILayout.Label("Checking ffmpeg...", Array.Empty<GUILayoutOption>());
}
else
{
GUILayout.Label(_ffOk.GetValueOrDefault() ? "ffmpeg OK (will convert if needed)" : "ffmpeg not found ? some tracks may not play. Place ffmpeg in SteelSpeaker/bin", Array.Empty<GUILayoutOption>());
}
if ((Object)(object)instance != (Object)null)
{
if (!_volInitialized)
{
try
{
_baselineVol = Mathf.Clamp01(instance.GetVolume());
}
catch
{
_baselineVol = 1f;
}
_volSlider = 50;
_volInitialized = true;
}
GUILayout.Space(6f);
GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
GUILayout.Label("Volume (50=baseline)", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) });
int num3 = Mathf.RoundToInt(GUILayout.HorizontalSlider((float)_volSlider, 1f, 100f, Array.Empty<GUILayoutOption>()));
GUILayout.Label(num3.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) });
GUILayout.EndHorizontal();
if (num3 != _volSlider)
{
_volSlider = num3;
float volume = SliderToVolume(_volSlider, _baselineVol);
try
{
instance.SetVolume(volume);
}
catch
{
}
}
}
GUILayout.EndArea();
}
catch (Exception ex)
{
Debug.LogError((object)("[SteelSpeaker] UI render error: " + ex));
}
finally
{
GUI.color = color;
GUI.backgroundColor = backgroundColor;
GUI.contentColor = contentColor;
}
}
private void DoSearch()
{
_results.Clear();
if (!string.IsNullOrWhiteSpace(_query) && !_isSearching)
{
((MonoBehaviour)this).StartCoroutine(SearchRoutine(_query));
}
}
[IteratorStateMachine(typeof(<SearchRoutine>d__27))]
private IEnumerator SearchRoutine(string query)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <SearchRoutine>d__27(0)
{
<>4__this = this,
query = query
};
}
private void Queue(YouTubeService.SearchResult r)
{
if (!_queueInProgress)
{
_queueInProgress = true;
((MonoBehaviour)this).StartCoroutine(QueueRoutine(r));
}
}
[IteratorStateMachine(typeof(<QueueRoutine>d__29))]
private IEnumerator QueueRoutine(YouTubeService.SearchResult r)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <QueueRoutine>d__29(0)
{
<>4__this = this,
r = r
};
}
private static string Truncate(string s, int len)
{
if (!string.IsNullOrEmpty(s))
{
if (s.Length > len)
{
return s.Substring(0, len - 1) + "...";
}
return s;
}
return s;
}
private static string FormatDur(float s)
{
if (s <= 0f)
{
return "--:--";
}
int num = Mathf.FloorToInt(s / 60f);
int num2 = Mathf.FloorToInt(s % 60f);
return num.ToString("0") + ":" + num2.ToString("00");
}
public void Open()
{
//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)
if (!_show)
{
_show = true;
_prevCursorVisible = Cursor.visible;
_prevCursorLock = Cursor.lockState;
Cursor.visible = true;
Cursor.lockState = (CursorLockMode)0;
_volInitialized = false;
}
}
public void Close()
{
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
if (_show)
{
_show = false;
Cursor.visible = _prevCursorVisible;
Cursor.lockState = _prevCursorLock;
}
}
public void Toggle()
{
if (_show)
{
Close();
}
else
{
Open();
}
}
private static float SliderToVolume(int s, float baseline)
{
s = Mathf.Clamp(s, 1, 100);
baseline = Mathf.Clamp01(baseline);
if (s <= 50)
{
return Mathf.Clamp01(baseline * ((float)s / 50f));
}
float num = ((float)s - 50f) / 50f;
return Mathf.Clamp01(baseline + (1f - baseline) * num);
}
}
public class HolderUI : MonoBehaviour
{
}
public static class UIHelpers
{
public static Rect AlignTopLeft(this Rect r, float x, float y)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
return new Rect(x, y, ((Rect)(ref r)).width, ((Rect)(ref r)).height);
}
}
public class UIHotkeyListener : MonoBehaviour
{
private void Awake()
{
Debug.Log((object)"[SteelSpeaker] Hotkey listener armed: BackQuote (`) only.");
}
private void Update()
{
try
{
if (Input.GetKeyDown((KeyCode)96))
{
Debug.Log((object)"[SteelSpeaker] BackQuote pressed; toggling UI.");
EnsureUI().Toggle();
}
}
catch (Exception ex)
{
Debug.LogError((object)("[SteelSpeaker] Hotkey listener error: " + ex));
}
}
private EscapeMenuVolumeUI EnsureUI()
{
EscapeMenuVolumeUI instance = EscapeMenuVolumeUI.Instance;
if ((Object)(object)instance != (Object)null)
{
return instance;
}
instance = ((Component)this).GetComponent<EscapeMenuVolumeUI>();
if ((Object)(object)instance == (Object)null)
{
instance = ((Component)this).gameObject.AddComponent<EscapeMenuVolumeUI>();
}
return instance;
}
}
}
namespace SteelSpeaker.Prefabs
{
public static class BundleLoader
{
private static AssetBundle? _bundle;
public static GameObject LoadSpeakerPrefab()
{
//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_bundle == (Object)null)
{
string text = Path.Combine(Paths.PluginPath, "SteelSpeaker", "AssetBundles");
string[] array = new string[4] { "speakerbundle", "steelspeaker", "speakerbundle.bundle", "steelspeaker.bundle" };
string[] array2 = array;
foreach (string path in array2)
{
string text2 = Path.Combine(text, path);
_bundle = AssetBundle.LoadFromFile(text2);
if (!((Object)(object)_bundle != (Object)null))
{
continue;
}
Debug.Log((object)("[SteelSpeaker] Loaded AssetBundle: " + text2));
try
{
ConfigEntry<bool>? debugDumpBundleAssets = ModConfig.DebugDumpBundleAssets;
if (debugDumpBundleAssets != null && debugDumpBundleAssets.Value)
{
string[] allAssetNames = _bundle.GetAllAssetNames();
Debug.Log((object)("[SteelSpeaker] Bundle assets:" + Environment.NewLine + string.Join("\n - ", allAssetNames)));
}
}
catch
{
}
break;
}
if ((Object)(object)_bundle == (Object)null)
{
Debug.LogError((object)("[SteelSpeaker] Failed to load AssetBundle. Tried: " + string.Join(", ", array) + " in " + text));
throw new FileNotFoundException("AssetBundle load failed", Path.Combine(text, array[0]));
}
}
GameObject val = _bundle.LoadAsset<GameObject>("Speaker3D");
if ((Object)(object)val == (Object)null)
{
string[] allAssetNames2 = _bundle.GetAllAssetNames();
string text3 = null;
string[] array3 = allAssetNames2;
foreach (string text4 in array3)
{
string text5 = text4.Replace("\\", "/").ToLowerInvariant();
if (text5.EndsWith("speaker3d.prefab"))
{
text3 = text4;
break;
}
}
if (text3 == null)
{
Debug.LogError((object)("[SteelSpeaker] Prefab 'Speaker3D' not found in bundle. Assets: " + string.Join(", ", allAssetNames2)));
throw new MissingReferenceException("Speaker3D prefab not found");
}
Debug.Log((object)("[SteelSpeaker] Using asset '" + text3 + "' from bundle."));
val = _bundle.LoadAsset<GameObject>(text3);
}
return val;
}
}
public class SpeakerBehavior : MonoBehaviour
{
public enum AttachTarget
{
None,
LocalPlayer,
Host
}
[CompilerGenerated]
private sealed class <EnsureAttachedLoop>d__20 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public float initialDelay;
public SpeakerBehavior <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <EnsureAttachedLoop>d__20(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Expected O, but got Unknown
//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Expected O, but got Unknown
int num = <>1__state;
SpeakerBehavior speakerBehavior = <>4__this;
Transform val;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
if (initialDelay > 0f)
{
Debug.Log((object)$"[SteelSpeaker] Waiting {initialDelay:F1}s before attempting attach...");
<>2__current = (object)new WaitForSeconds(initialDelay);
<>1__state = 1;
return true;
}
goto IL_0079;
case 1:
<>1__state = -1;
goto IL_0079;
case 2:
<>1__state = -1;
return false;
case 3:
<>1__state = -1;
goto IL_0079;
case 4:
{
<>1__state = -1;
goto IL_0079;
}
IL_0079:
if (speakerBehavior.attachMode == AttachTarget.None)
{
<>2__current = speakerBehavior.RepositionNearPlayer(speakerBehavior.spawnDistance, speakerBehavior.spawnDelaySeconds, ensureVisual: true);
<>1__state = 2;
return true;
}
val = null;
if (speakerBehavior.attachMode == AttachTarget.Host)
{
val = speakerBehavior.TryFindHostTransform();
if ((Object)(object)val == (Object)null)
{
val = speakerBehavior.TryFindLocalPlayerTransform();
}
}
else if (speakerBehavior.attachMode == AttachTarget.LocalPlayer)
{
val = speakerBehavior.TryFindLocalPlayerTransform();
}
if ((Object)(object)val != (Object)null)
{
speakerBehavior.EnsureVisual();
speakerBehavior.AttachToTarget(val);
<>2__current = (object)new WaitForSeconds(1f);
<>1__state = 3;
return true;
}
<>2__current = null;
<>1__state = 4;
return true;
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <RepositionNearPlayer>d__19 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public float delaySeconds;
public float distance;
public bool ensureVisual;
public SpeakerBehavior <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <RepositionNearPlayer>d__19(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Expected O, but got Unknown
//IL_009b: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: 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_00d6: 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_00f2: 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)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Unknown result type (might be due to invalid IL or missing references)
//IL_010b: Unknown result type (might be due to invalid IL or missing references)
//IL_0120: Unknown result type (might be due to invalid IL or missing references)
//IL_015a: Unknown result type (might be due to invalid IL or missing references)
//IL_0166: Unknown result type (might be due to invalid IL or missing references)
int num = <>1__state;
SpeakerBehavior speakerBehavior = <>4__this;
switch (num)
{
default:
return false;
case 0:
<>1__state = -1;
if (delaySeconds > 0f)
{
<>2__current = (object)new WaitForSeconds(delaySeconds);
<>1__state = 1;
return true;
}
<>2__current = null;
<>1__state = 2;
return true;
case 1:
<>1__state = -1;
break;
case 2:
<>1__state = -1;
break;
}
Camera main = Camera.main;
Vector3 val = (Vector3)(((Object)(object)main != (Object)null) ? (((Component)main).transform.position + ((Component)main).transform.forward * Mathf.Max(0.1f, distance)) : new Vector3(0f, 2f, 0f));
RaycastHit val2 = default(RaycastHit);
if (Physics.Raycast(val + Vector3.up * 2f, Vector3.down, ref val2, 10f, -1, (QueryTriggerInteraction)1))
{
val = ((RaycastHit)(ref val2)).point + Vector3.up * 0.02f;
}
if (ensureVisual)
{
speakerBehavior.EnsureVisual();
}
((Component)speakerBehavior).transform.position = val;
Rigidbody val3 = (((Object)(object)speakerBehavior._visual != (Object)null) ? speakerBehavior._visual.GetComponent<Rigidbody>() : null) ?? ((Component)speakerBehavior).GetComponent<Rigidbody>();
if ((Object)(object)val3 != (Object)null)
{
val3.velocity = Vector3.zero;
val3.angularVelocity = Vector3.zero;
}
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[Header("Spawn Timing")]
public float spawnDelaySeconds = 5f;
public float initialAttachDelaySeconds = 15f;
public float spawnDistance = 10f;
[Header("Auto Attach")]
public AttachTarget attachMode = AttachTarget.Host;
public string mountPath = "";
public Vector3 mountLocalPos = new Vector3(0.2f, -0.1f, 0.05f);
public Vector3 mountLocalEuler = new Vector3(0f, 90f, 0f);
[Tooltip("Path to the ClipAnchor transform inside the speaker prefab")]
public string clipAnchorPath = "ClipAnchor";
private AudioManager _audio;
private GameObject? _visual;
public static SpeakerBehavior? Instance { get; private set; }
public bool IsPlaying => _audio.IsPlaying;
private void Awake()
{
if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
{
Object.Destroy((Object)(object)((Component)this).gameObject);
return;
}
Instance = this;
((Object)((Component)this).gameObject).name = "SteelSpeaker.Speaker";
Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
if (ModConfig.InitialAttachDelay != null)
{
initialAttachDelaySeconds = ModConfig.InitialAttachDelay.Value;
}
_audio = new AudioManager(((Component)this).gameObject);
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDestroy()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
((MonoBehaviour)this).StopAllCoroutines();
((MonoBehaviour)this).StartCoroutine(EnsureAttachedLoop(initialAttachDelaySeconds));
}
private void EnsureVisual()
{
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_visual != (Object)null)
{
return;
}
try
{
GameObject val = BundleLoader.LoadSpeakerPrefab();
_visual = Object.Instantiate<GameObject>(val, ((Component)this).transform);
((Object)_visual).name = "SteelSpeaker3D";
_visual.transform.localPosition = Vector3.zero;
_visual.transform.localRotation = Quaternion.identity;
_visual.transform.localScale = Vector3.one;
AudioSource componentInChildren = _visual.GetComponentInChildren<AudioSource>();
if ((Object)(object)componentInChildren != (Object)null)
{
componentInChildren.spatialBlend = 1f;
componentInChildren.rolloffMode = (AudioRolloffMode)0;
componentInChildren.minDistance = 1f;
componentInChildren.maxDistance = 20f;
}
FixupVisualRenderers(_visual);
Rigidbody val2 = _visual.GetComponent<Rigidbody>() ?? _visual.AddComponent<Rigidbody>();
Collider val3 = (Collider)(((object)_visual.GetComponent<Collider>()) ?? ((object)_visual.AddComponent<BoxCollider>()));
if ((Object)(object)_visual.GetComponent<SpeakerInteractable>() == (Object)null)
{
_visual.AddComponent<SpeakerInteractable>();
}
LogVisualDiagnostics("EnsureVisual");
}
catch (Exception ex)
{
Debug.LogError((object)("[SteelSpeaker] Failed to spawn speaker visual: " + ex.Message));
}
}
[IteratorStateMachine(typeof(<RepositionNearPlayer>d__19))]
private IEnumerator RepositionNearPlayer(float distance, float delaySeconds, bool ensureVisual)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <RepositionNearPlayer>d__19(0)
{
<>4__this = this,
distance = distance,
delaySeconds = delaySeconds,
ensureVisual = ensureVisual
};
}
[IteratorStateMachine(typeof(<EnsureAttachedLoop>d__20))]
private IEnumerator EnsureAttachedLoop(float initialDelay)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <EnsureAttachedLoop>d__20(0)
{
<>4__this = this,
initialDelay = initialDelay
};
}
private void AttachToTarget(Transform player)
{
//IL_0026: 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_004f: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Expected O, but got Unknown
//IL_0156: Unknown result type (might be due to invalid IL or missing references)
//IL_015e: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
//IL_011a: Unknown result type (might be due to invalid IL or missing references)
//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
//IL_031f: Unknown result type (might be due to invalid IL or missing references)
if (ModConfig.PosX != null)
{
mountLocalPos = new Vector3(ModConfig.PosX.Value, ModConfig.PosY.Value, ModConfig.PosZ.Value);
mountLocalEuler = new Vector3(ModConfig.RotX.Value, ModConfig.RotY.Value, ModConfig.RotZ.Value);
}
Transform val = player;
Character componentInParent = ((Component)player).GetComponentInParent<Character>();
if ((Object)(object)componentInParent != (Object)null)
{
try
{
val = ((Component)componentInParent.GetBodypart((BodypartType)0)).transform;
}
catch
{
}
}
if (!string.IsNullOrEmpty(mountPath))
{
Transform val2 = player.Find(mountPath);
if ((Object)(object)val2 != (Object)null)
{
val = val2;
}
}
((Component)this).transform.SetParent(val, false);
Transform val3 = val.Find("HookAnchor_SteelSpeaker");
if ((Object)(object)val3 == (Object)null)
{
GameObject val4 = new GameObject("HookAnchor_SteelSpeaker");
val3 = val4.transform;
val3.SetParent(val, false);
}
Vector3 localPosition = mountLocalPos;
if ((Object)(object)_visual != (Object)null)
{
Collider componentInChildren = _visual.GetComponentInChildren<Collider>();
if ((Object)(object)componentInChildren != (Object)null)
{
Bounds bounds = componentInChildren.bounds;
float num = Mathf.Max(((Bounds)(ref bounds)).extents.x, 0.1f) + 0.05f;
((Vector3)(ref localPosition))..ctor(num, mountLocalPos.y, mountLocalPos.z);
}
}
val3.localPosition = localPosition;
val3.localRotation = Quaternion.Euler(mountLocalEuler);
if ((Object)(object)_visual != (Object)null && !SpeakerAttach.AttachToHook(_visual, val3, clipAnchorPath))
{
Debug.LogWarning((object)"[SteelSpeaker] Joint attach failed; falling back to fixed mount.");
_visual.transform.SetParent(((Component)this).transform, false);
_visual.transform.localPosition = Vector3.zero;
_visual.transform.localRotation = Quaternion.identity;
Rigidbody component = _visual.GetComponent<Rigidbody>();
Collider component2 = _visual.GetComponent<Collider>();
if ((Object)(object)component != (Object)null)
{
component.isKinematic = true;
}
if ((Object)(object)component2 != (Object)null)
{
component2.enabled = false;
}
}
if ((Object)(object)_visual != (Object)null)
{
ConfigEntry<bool>? forceDefaultLayer = ModConfig.ForceDefaultLayer;
if (forceDefaultLayer != null && forceDefaultLayer.Value)
{
SetLayerRecursive(_visual.transform, 0);
Debug.Log((object)"[SteelSpeaker] Forced visual to Default layer (debug option enabled).");
}
else
{
SetLayerRecursive(_visual.transform, ((Component)player).gameObject.layer);
}
}
LogVisualDiagnostics("AttachToTarget");
if ((Object)(object)_visual != (Object)null && _visual.GetComponentsInChildren<Renderer>(true).Length == 0)
{
GameObject val5 = GameObject.CreatePrimitive((PrimitiveType)3);
Collider component3 = val5.GetComponent<Collider>();
if (Object.op_Implicit((Object)(object)component3))
{
Object.Destroy((Object)(object)component3);
}
((Object)val5).name = "SteelSpeaker.DebugCube";
val5.transform.SetParent(((Component)this).transform, false);
val5.transform.localPosition = Vector3.zero;
val5.transform.localRotation = Quaternion.identity;
val5.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
SetLayerRecursive(val5.transform, ((Component)player).gameObject.layer);
Debug.LogWarning((object)"[SteelSpeaker] Visual prefab has no renderers; spawned debug cube placeholder.");
}
Debug.Log((object)"[SteelSpeaker] Attached speaker to player (joint-based).");
}
private void SetLayerRecursive(Transform t, int layer)
{
((Component)t).gameObject.layer = layer;
for (int i = 0; i < t.childCount; i++)
{
SetLayerRecursive(t.GetChild(i), layer);
}
}
private void LogVisualDiagnostics(string where)
{
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_0168: Unknown result type (might be due to invalid IL or missing references)
//IL_016d: Unknown result type (might be due to invalid IL or missing references)
//IL_0171: Unknown result type (might be due to invalid IL or missing references)
//IL_017f: Unknown result type (might be due to invalid IL or missing references)
//IL_0184: Unknown result type (might be due to invalid IL or missing references)
//IL_0188: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_visual == (Object)null)
{
Debug.Log((object)("[SteelSpeaker] " + where + ": No visual object."));
return;
}
Renderer[] componentsInChildren = _visual.GetComponentsInChildren<Renderer>(true);
Debug.Log((object)$"[SteelSpeaker] {where}: Visual '{((Object)_visual).name}' active={_visual.activeInHierarchy}, layer={_visual.layer}({LayerMask.LayerToName(_visual.layer)}), renderers={componentsInChildren.Length}, pos={_visual.transform.position}");
for (int i = 0; i < componentsInChildren.Length; i++)
{
Renderer val = componentsInChildren[i];
if (!((Object)(object)val == (Object)null))
{
Material[] sharedMaterials = val.sharedMaterials;
string text = "<none>";
if (sharedMaterials != null && sharedMaterials.Length != 0 && (Object)(object)sharedMaterials[0] != (Object)null && (Object)(object)sharedMaterials[0].shader != (Object)null)
{
text = ((Object)sharedMaterials[0].shader).name;
}
object[] obj = new object[9]
{
where,
i,
((Object)((Component)val).gameObject).name,
val.enabled,
((Component)val).gameObject.layer,
LayerMask.LayerToName(((Component)val).gameObject.layer),
null,
null,
null
};
Bounds bounds = val.bounds;
obj[6] = ((Bounds)(ref bounds)).center;
bounds = val.bounds;
obj[7] = ((Bounds)(ref bounds)).extents;
obj[8] = text;
Debug.Log((object)string.Format("[SteelSpeaker] {0}: Renderer[{1}] '{2}', enabled={3}, layer={4}({5}), bounds.center={6}, extents={7}, shader={8}", obj));
}
}
}
private void FixupVisualRenderers(GameObject visual)
{
Renderer[] componentsInChildren = visual.GetComponentsInChildren<Renderer>(true);
Renderer[] array = componentsInChildren;
foreach (Renderer val in array)
{
if ((Object)(object)val == (Object)null)
{
continue;
}
val.enabled = true;
Material[] sharedMaterials = val.sharedMaterials;
if (sharedMaterials == null)
{
continue;
}
foreach (Material val2 in sharedMaterials)
{
if ((Object)(object)val2 == (Object)null || (Object)(object)val2.shader == (Object)null)
{
continue;
}
string text = ((Object)val2.shader).name ?? string.Empty;
if (text.Contains("Universal", StringComparison.OrdinalIgnoreCase) || text.Contains("URP", StringComparison.OrdinalIgnoreCase) || text.Contains("HDRP", StringComparison.OrdinalIgnoreCase))
{
Shader val3 = Shader.Find("Standard");
if ((Object)(object)val3 == (Object)null)
{
val3 = Shader.Find("Legacy Shaders/Diffuse");
}
if ((Object)(object)val3 != (Object)null)
{
val2.shader = val3;
}
}
}
}
}
public void ShowDebugCube()
{
//IL_005d: 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_008c: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)_visual != (Object)null) || _visual.GetComponentsInChildren<Renderer>(true).Length == 0)
{
GameObject val = GameObject.CreatePrimitive((PrimitiveType)3);
Collider component = val.GetComponent<Collider>();
if (Object.op_Implicit((Object)(object)component))
{
Object.Destroy((Object)(object)component);
}
((Object)val).name = "SteelSpeaker.DebugCube";
val.transform.SetParent(((Component)this).transform, false);
val.transform.localPosition = Vector3.zero;
val.transform.localRotation = Quaternion.identity;
val.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
}
}
private Transform? TryFindLocalPlayerTransform()
{
try
{
if ((Object)(object)Character.localCharacter != (Object)null)
{
return ((Component)Character.localCharacter).transform;
}
}
catch
{
}
Camera main = Camera.main;
if ((Object)(object)main == (Object)null)
{
return null;
}
if (!((Object)(object)((Component)main).transform.parent != (Object)null))
{
return ((Component)main).transform;
}
return ((Component)main).transform.parent;
}
private Transform? TryFindHostTransform()
{
try
{
if (PhotonNetwork.IsMasterClient)
{
return TryFindLocalPlayerTransform();
}
Character[] array = Object.FindObjectsOfType<Character>();
Character[] array2 = array;
foreach (Character val in array2)
{
PhotonView component = ((Component)val).GetComponent<PhotonView>();
if ((Object)(object)component != (Object)null && component.Owner != null && component.Owner.IsMasterClient)
{
return ((Component)val).transform;
}
}
}
catch
{
}
return null;
}
public void SetVolume(float v)
{
_audio.SetVolume(v);
}
public void PlayLocalFile(string fullPath)
{
_audio.PlayLocalFile((MonoBehaviour)(object)this, fullPath);
}
public void Stop()
{
_audio.Stop();
}
}
}
namespace SteelSpeaker.Networking
{
[DisallowMultipleComponent]
public class SpeakerNetSync : MonoBehaviourPun
{
private SpeakerManager _speaker;
private void Awake()
{
_speaker = Object.FindObjectOfType<SpeakerManager>() ?? ((Component)this).gameObject.AddComponent<SpeakerManager>();
}
public void EnqueueTrack(YouTubeTrack t)
{
_speaker.QueueTrack(t);
if (!PhotonNetwork.IsConnected)
{
return;
}
try
{
if (!PhotonNetwork.IsMasterClient && (Object)(object)((MonoBehaviourPun)this).photonView != (Object)null)
{
((MonoBehaviourPun)this).photonView.RPC("RpcEnqueue", (RpcTarget)2, new object[4] { t.VideoId, t.Title, t.Url, t.Duration });
}
}
catch
{
}
}
[PunRPC]
private void RpcEnqueue(string videoId, string title, string url, float duration, PhotonMessageInfo info)
{
if (PhotonNetwork.IsMasterClient)
{
_speaker.QueueTrack(new YouTubeTrack(videoId, title, url, duration));
}
}
public void RequestPlay()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (PhotonNetwork.IsConnected)
{
((MonoBehaviourPun)this).photonView.RPC("RpcRequestPlay", (RpcTarget)2, Array.Empty<object>());
}
else
{
RpcRequestPlay(default(PhotonMessageInfo));
}
}
[PunRPC]
private void RpcRequestPlay(PhotonMessageInfo info)
{
if (!PhotonNetwork.IsConnected || PhotonNetwork.IsMasterClient)
{
SpeakerManager speakerManager = Object.FindObjectOfType<SpeakerManager>();
if ((Object)(object)speakerManager != (Object)null)
{
speakerManager.StartOrResume();
}
}
}
public void BroadcastPlay(string videoId, string title, string mediaUrl, float duration)
{
//IL_0050: 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)
if (PhotonNetwork.IsConnected)
{
((MonoBehaviourPun)this).photonView.RPC("RpcPlayNow", (RpcTarget)0, new object[5]
{
videoId,
title,
mediaUrl,
duration,
(float)PhotonNetwork.Time
});
}
else
{
RpcPlayNow(videoId, title, mediaUrl, duration, 0f, default(PhotonMessageInfo));
}
}
[PunRPC]
private void RpcPlayNow(string videoId, string title, string mediaUrl, float duration, float networkTime, PhotonMessageInfo info)
{
YouTubeTrack track = new YouTubeTrack(videoId, title, mediaUrl, duration);
SpeakerManager speakerManager = Object.FindObjectOfType<SpeakerManager>() ?? ((Component)this).gameObject.AddComponent<SpeakerManager>();
speakerManager.StopPlayback();
speakerManager.QueueTrack(track);
}
public void BroadcastStop()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (PhotonNetwork.IsConnected)
{
((MonoBehaviourPun)this).photonView.RPC("RpcStop", (RpcTarget)0, Array.Empty<object>());
}
else
{
RpcStop(default(PhotonMessageInfo));
}
}
[PunRPC]
private void RpcStop(PhotonMessageInfo info)
{
SpeakerManager speakerManager = Object.FindObjectOfType<SpeakerManager>();
if ((Object)(object)speakerManager != (Object)null)
{
speakerManager.StopPlayback();
}
}
public void BroadcastPause(bool paused)
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
if (PhotonNetwork.IsConnected)
{
((MonoBehaviourPun)this).photonView.RPC("RpcPause", (RpcTarget)0, new object[1] { paused });
}
else
{
RpcPause(paused, default(PhotonMessageInfo));
}
}
[PunRPC]
private void RpcPause(bool paused, PhotonMessageInfo info)
{
SpeakerManager speakerManager = Object.FindObjectOfType<SpeakerManager>();
if ((Object)(object)speakerManager != (Object)null)
{
speakerManager.Pause(paused);
}
}
public void RequestSkip()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (PhotonNetwork.IsConnected)
{
((MonoBehaviourPun)this).photonView.RPC("RpcRequestSkip", (RpcTarget)2, Array.Empty<object>());
}
else
{
RpcRequestSkip(default(PhotonMessageInfo));
}
}
[PunRPC]
private void RpcRequestSkip(PhotonMessageInfo info)
{
if (!PhotonNetwork.IsConnected || PhotonNetwork.IsMasterClient)
{
SpeakerManager speakerManager = Object.FindObjectOfType<SpeakerManager>();
if ((Object)(object)speakerManager != (Object)null)
{
speakerManager.Skip();
}
}
}
public void RequestTogglePause()
{
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (PhotonNetwork.IsConnected)
{
((MonoBehaviourPun)this).photonView.RPC("RpcRequestTogglePause", (RpcTarget)2, Array.Empty<object>());
}
else
{
RpcRequestTogglePause(default(PhotonMessageInfo));
}
}
[PunRPC]
private void RpcRequestTogglePause(PhotonMessageInfo info)
{
if (PhotonNetwork.IsConnected && !PhotonNetwork.IsMasterClient)
{
return;
}
SpeakerManager speakerManager = Object.FindObjectOfType<SpeakerManager>();
if ((Object)(object)speakerManager != (Object)null)
{
speakerManager.TogglePause();
if (PhotonNetwork.IsConnected)
{
BroadcastPause(speakerManager.IsPaused);
}
}
}
}
public static class SyncMessages
{
}
}
namespace SteelSpeaker.Models
{
public class YouTubeTrack
{
public string VideoId { get; set; }
public string Title { get; set; }
public string Url { get; set; }
public float Duration { get; set; }
public YouTubeTrack(string videoId, string title, string url, float duration)
{
VideoId = videoId;
Title = title;
Url = url;
Duration = duration;
}
}
}
namespace SteelSpeaker.Managers
{
public class AudioManager
{
private class DummyRunner : MonoBehaviour
{
public void Run(IEnumerator e)
{
((MonoBehaviour)this).StartCoroutine(e);
}
}
[CompilerGenerated]
private sealed class <LoadAndPlay>d__13 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public string url;
public AudioType type;
public AudioManager <>4__this;
private UnityWebRequest <req>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <LoadAndPlay>d__13(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
int num = <>1__state;
if (num == -3 || num == 1)
{
try
{
}
finally
{
<>m__Finally1();
}
}
<req>5__2 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_002b: 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_0075: Invalid comparison between Unknown and I4
bool result;
try
{
int num = <>1__state;
AudioManager audioManager = <>4__this;
switch (num)
{
default:
result = false;
break;
case 0:
<>1__state = -1;
<req>5__2 = UnityWebRequestMultimedia.GetAudioClip(url, type);
<>1__state = -3;
<>2__current = <req>5__2.SendWebRequest();
<>1__state = 1;
result = true;
break;
case 1:
<>1__state = -3;
if ((int)<req>5__2.result != 1)
{
Debug.LogError((object)$"[SteelSpeaker] Audio load error: {<req>5__2.error} (code {<req>5__2.responseCode}) for {url}");
result = false;
}
else
{
AudioClip content = DownloadHandlerAudioClip.GetContent(<req>5__2);
if (!((Object)(object)content == (Object)null))
{
audioManager._audio.clip = content;
audioManager._audio.Play();
audioManager.IsPaused = false;
Debug.Log((object)("[SteelSpeaker] Playing: " + url));
<>m__Finally1();
<req>5__2 = null;
result = false;
break;
}
Debug.LogError((object)"[SteelSpeaker] Null audio clip after load.");
result = false;
}
<>m__Finally1();
break;
}
}
catch
{
//try-fault
((IDisposable)this).Dispose();
throw;
}
return result;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
private void <>m__Finally1()
{
<>1__state = -1;
if (<req>5__2 != null)
{
((IDisposable)<req>5__2).Dispose();
}
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private readonly AudioSource _audio;
public bool IsPlaying
{
get
{
if ((Object)(object)_audio != (Object)null)
{
return _audio.isPlaying;
}
return false;
}
}
public bool IsPaused { get; private set; }
public AudioManager(GameObject host)
{
_audio = host.GetComponentInChildren<AudioSource>() ?? host.AddComponent<AudioSource>();
_audio.playOnAwake = false;
_audio.loop = false;
_audio.spatialBlend = 1f;
_audio.rolloffMode = (AudioRolloffMode)0;
_audio.minDistance = 1f;
_audio.maxDistance = 20f;
}
public void SetVolume(float v)
{
_audio.volume = Mathf.Clamp01(v);
}
public float GetVolume()
{
if (!((Object)(object)_audio != (Object)null))
{
return 1f;
}
return _audio.volume;
}
public void PlayLocalFile(MonoBehaviour runner, string fullPath)
{
//IL_0041: 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_0063: 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_0075: Unknown result type (might be due to invalid IL or missing references)
if (!File.Exists(fullPath))
{
Debug.LogError((object)("[SteelSpeaker] File not found: " + fullPath));
return;
}
string url = "file://" + fullPath.Replace("\\", "/");
string text = Path.GetExtension(fullPath).ToLowerInvariant();
AudioType type = (AudioType)0;
switch (text)
{
case ".ogg":
type = (AudioType)14;
break;
case ".wav":
type = (AudioType)20;
break;
case ".mp3":
type = (AudioType)13;
break;
}
runner.StartCoroutine(LoadAndPlay(url, type));
}
public void PlayYouTubeUrl(string url)
{
//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_000d: 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)
AudioType val = GuessAudioType(url);
Debug.Log((object)$"[SteelSpeaker] Playing stream: {url} (type {val})");
DummyRunner dummyRunner = ((Component)_audio).gameObject.GetComponent<DummyRunner>() ?? ((Component)_audio).gameObject.AddComponent<DummyRunner>();
dummyRunner.Run(LoadAndPlay(url, val));
}
public void Stop()
{
if (_audio.isPlaying)
{
_audio.Stop();
}
IsPaused = false;
}
[IteratorStateMachine(typeof(<LoadAndPlay>d__13))]
private IEnumerator LoadAndPlay(string url, AudioType type)
{
//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)
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <LoadAndPlay>d__13(0)
{
<>4__this = this,
url = url,
type = type
};
}
public void Pause()
{
if (!((Object)(object)_audio == (Object)null))
{
_audio.Pause();
IsPaused = true;
}
}
public void UnPause()
{
if (!((Object)(object)_audio == (Object)null))
{
_audio.UnPause();
IsPaused = false;
}
}
public void TogglePause()
{
if (IsPaused)
{
UnPause();
}
else
{
Pause();
}
}
private static AudioType GuessAudioType(string url)
{
string text = url.ToLowerInvariant();
if (!text.Contains(".ogg") && !text.Contains(".opus"))
{
if (!text.Contains(".mp3"))
{
if (!text.Contains(".wav"))
{
return (AudioType)13;
}
return (AudioType)20;
}
return (AudioType)13;
}
return (AudioType)14;
}
}
public class SpeakerManager : MonoBehaviour
{
public static SpeakerManager Instance;
private Queue<YouTubeTrack> _queue = new Queue<YouTubeTrack>();
private AudioManager _audioManager;
private bool _isPlaying;
private bool _isPaused;
private YouTubeTrack? _current;
private SpeakerNetSync? _net;
public bool IsPlaying => _isPlaying;
public bool IsPaused => _isPaused;
public YouTubeTrack? Current => _current;
private void Awake()
{
if ((Object)(object)Instance == (Object)null)
{
Instance = this;
_audioManager = new AudioManager(((Component)this).gameObject);
_net = Object.FindObjectOfType<SpeakerNetSync>();
}
else
{
Object.Destroy((Object)(object)this);
}
}
public void QueueTrack(YouTubeTrack track)
{
_queue.Enqueue(track);
Debug.Log((object)("[SteelSpeaker] Queued track: " + track.Title));
if (!_isPlaying)
{
PlayNext();
}
}
private void PlayNext()
{
if (_queue.Count == 0)
{
_isPlaying = false;
return;
}
YouTubeTrack youTubeTrack = _queue.Dequeue();
_isPlaying = true;
_isPaused = false;
_current = youTubeTrack;
Debug.Log((object)("[SteelSpeaker] Now playing: " + youTubeTrack.Title));
_audioManager.PlayYouTubeUrl(youTubeTrack.Url);
try
{
if ((Object)(object)_net != (Object)null && PhotonNetwork.IsMasterClient)
{
_net.BroadcastPlay(youTubeTrack.VideoId, youTubeTrack.Title, youTubeTrack.Url, youTubeTrack.Duration);
}
}
catch
{
}
}
public void StartOrResume()
{
if (_isPlaying)
{
if (_isPaused)
{
_audioManager.UnPause();
_isPaused = false;
}
}
else if (_queue.Count > 0)
{
PlayNext();
}
}
public void StopPlayback()
{
_audioManager.Stop();
_isPlaying = false;
_isPaused = false;
_current = null;
}
public void SetVolume(float volume)
{
_audioManager.SetVolume(volume);
}
public float GetVolume()
{
if (_audioManager == null)
{
return 1f;
}
return _audioManager.GetVolume();
}
private void Update()
{
if (_isPlaying && !_audioManager.IsPlaying && !_isPaused)
{
if (_queue.Count > 0)
{
PlayNext();
}
else
{
_isPlaying = false;
}
}
}
public void TogglePause()
{
if (_isPlaying)
{
if (_isPaused)
{
_audioManager.UnPause();
_isPaused = false;
}
else
{
_audioManager.Pause();
_isPaused = true;
}
}
}
public void Pause(bool paused)
{
if (_isPlaying)
{
if (paused && !_isPaused)
{
_audioManager.Pause();
_isPaused = true;
}
else if (!paused && _isPaused)
{
_audioManager.UnPause();
_isPaused = false;
}
}
}
public void Skip()
{
if (_queue.Count > 0)
{
PlayNext();
}
else
{
StopPlayback();
}
}
public List<YouTubeTrack> GetQueueSnapshot()
{
return new List<YouTubeTrack>(_queue);
}
}
public class YouTubeService
{
public class SearchResult
{
public string videoId = string.Empty;
public string title = string.Empty;
public float duration;
}
private readonly string _ytDlpPath;
private readonly string _ffmpegPath;
private bool? _ffmpegOkCache;
private bool? _ytOkCache;
public YouTubeService()
{
string path = Path.Combine(Paths.PluginPath, "SteelSpeaker", "bin");
string text = Path.Combine(path, YtDlpFilename());
_ytDlpPath = (File.Exists(text) ? text : YtDlpFilename());
string text2 = Path.Combine(path, FFmpegFilename());
_ffmpegPath = (File.Exists(text2) ? text2 : FFmpegFilename());
}
private static string YtDlpFilename()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Invalid comparison between Unknown and I4
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Invalid comparison between Unknown and I4
if ((int)Application.platform != 2 && (int)Application.platform != 7)
{
return "yt-dlp";
}
return "yt-dlp.exe";
}
private static string FFmpegFilename()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Invalid comparison between Unknown and I4
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Invalid comparison between Unknown and I4
if ((int)Application.platform != 2 && (int)Application.platform != 7)
{
return "ffmpeg";
}
return "ffmpeg.exe";
}
public bool HasBinary()
{
try
{
Process process = StartYtDlpProcess("--version");
process.WaitForExit(2000);
return process.ExitCode == 0;
}
catch
{
return false;
}
}
public Task<bool> HasBinaryAsync()
{
return Task.Run(() => HasBinary());
}
public bool HasFfmpeg()
{
try
{
Process process = StartProcess(_ffmpegPath, "-version");
process.WaitForExit(2000);
return process.ExitCode == 0;
}
catch
{
return false;
}
}
public Task<bool> HasFfmpegAsync()
{
return Task.Run(() => HasFfmpeg());
}
public List<SearchResult> Search(string query, int count = 10)
{
List<SearchResult> list = new List<SearchResult>();
try
{
string args = $"ytsearch{Mathf.Clamp(count, 1, 50)}:{EscapeArg(query)} --dump-json --flat-playlist";
Process process = StartYtDlpProcess(args);
var (s, text) = ReadToEnd(process);
if (process.ExitCode != 0)
{
Debug.LogError((object)("[SteelSpeaker] yt-dlp search failed: " + text));
return list;
}
using StringReader stringReader = new StringReader(s);
string jsonLine;
while ((jsonLine = stringReader.ReadLine()) != null)
{
try
{
string text2 = ExtractJsonString(jsonLine, "id");
string text3 = ExtractJsonString(jsonLine, "title");
string s2 = ExtractJsonNumber(jsonLine, "duration");
float duration = 0f;
if (float.TryParse(s2, out var result))
{
duration = result;
}
if (!string.IsNullOrEmpty(text2))
{
list.Add(new SearchResult
{
videoId = text2,
title = (string.IsNullOrEmpty(text3) ? text2 : text3),
duration = duration
});
}
}
catch
{
}
}
}
catch (Exception arg)
{
Debug.LogError((object)$"[SteelSpeaker] Search exception: {arg}");
}
return list;
}
public Task<List<SearchResult>> SearchAsync(string query, int count = 10)
{
string query2 = query;
return Task.Run(() => Search(query2, count));
}
public string? ResolveStreamUrl(string videoIdOrUrl)
{
try
{
string args = "-f bestaudio -g " + EscapeArg(videoIdOrUrl);
Process process = StartYtDlpProcess(args);
var (text, text2) = ReadToEnd(process);
if (process.ExitCode != 0)
{
Debug.LogError((object)("[SteelSpeaker] yt-dlp -g failed: " + text2));
return null;
}
string text3 = text.Trim().Split('\n')[0].Trim();
return string.IsNullOrWhiteSpace(text3) ? null : text3;
}
catch (Exception arg)
{
Debug.LogError((object)$"[SteelSpeaker] ResolveStreamUrl exception: {arg}");
return null;
}
}
public Task<string?> ResolveStreamUrlAsync(string videoIdOrUrl)
{
string videoIdOrUrl2 = videoIdOrUrl;
return Task.Run(() => ResolveStreamUrl(videoIdOrUrl2));
}
public string GetCacheDir()
{
string text = Path.Combine(Paths.PluginPath, "SteelSpeaker", "Cache");
Directory.CreateDirectory(text);
return text;
}
public long GetCacheSizeBytes()
{
try
{
string cacheDir = GetCacheDir();
long num = 0L;
string[] files = Directory.GetFiles(cacheDir, "*.*", SearchOption.TopDirectoryOnly);
foreach (string fileName in files)
{
try
{
num += new FileInfo(fileName).Length;
}
catch
{
}
}
return num;
}
catch
{
return 0L;
}
}
public void ClearCache()
{
try
{
string cacheDir = GetCacheDir();
string[] files = Directory.GetFiles(cacheDir, "*.*", SearchOption.TopDirectoryOnly);
foreach (string path in files)
{
try
{
File.Delete(path);
}
catch
{
}
}
}
catch
{
}
}
public string? EnsureLocalAudio(string videoIdOrUrl)
{
try
{
string cacheDir = GetCacheDir();
string text = ((videoIdOrUrl.Contains("youtube.com") || videoIdOrUrl.Length > 11) ? ExtractVideoId(videoIdOrUrl) : videoIdOrUrl);
string text2 = Path.Combine(cacheDir, text + ".mp3");
if (File.Exists(text2))
{
return text2;
}
string args = "-f bestaudio -x --audio-format mp3 -o " + EscapeArg(Path.Combine(cacheDir, "%(id)s.%(ext)s")) + " " + EscapeArg(videoIdOrUrl);
Process process = StartYtDlpProcess(args);
var (text3, text4) = ReadToEnd(process);
if (process.ExitCode != 0)
{
Debug.LogError((object)("[SteelSpeaker] yt-dlp download failed: " + text4));
return null;
}
string text5 = ((videoIdOrUrl.Contains("youtube.com") || videoIdOrUrl.Length > 11) ? ExtractVideoId(videoIdOrUrl) : videoIdOrUrl);
string[] files = Directory.GetFiles(cacheDir, text5 + ".mp3", SearchOption.TopDirectoryOnly);
if (files.Length != 0)
{
return files[0];
}
string[] files2 = Directory.GetFiles(cacheDir, "*.mp3");
if (files2.Length != 0)
{
Array.Sort(files2, (string a, string b) => File.GetLastWriteTimeUtc(b).CompareTo(File.GetLastWriteTimeUtc(a)));
return files2[0];
}
}
catch (Exception arg)
{
Debug.LogError((object)$"[SteelSpeaker] EnsureLocalAudio exception: {arg}");
}
return null;
}
private static string ExtractVideoId(string url)
{
try
{
if (url.Contains("v="))
{
int num = url.IndexOf("v=");
string text = url.Substring(num + 2);
int num2 = text.IndexOf('&');
return (num2 >= 0) ? text.Substring(0, num2) : text;
}
int num3 = url.IndexOf("youtu.be/");
if (num3 >= 0)
{
string text2 = url.Substring(num3 + 9);
int num4 = text2.IndexOf('?');
return (num4 >= 0) ? text2.Substring(0, num4) : text2;
}
}
catch
{
}
return url;
}
private Process StartYtDlpProcess(string args)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = _ytDlpPath,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8
};
string directoryName = Path.GetDirectoryName(_ffmpegPath);
if (!string.IsNullOrEmpty(directoryName) && Directory.Exists(directoryName))
{
processStartInfo.EnvironmentVariables["PATH"] = directoryName + Path.PathSeparator + (processStartInfo.EnvironmentVariables.ContainsKey("PATH") ? processStartInfo.EnvironmentVariables["PATH"] : string.Empty);
}
return Process.Start(processStartInfo);
}
private static Process StartProcess(string file, string args)
{
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = file,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
return Process.Start(startInfo);
}
private static (string stdout, string stderr) ReadToEnd(Process p)
{
string item = p.StandardOutput.ReadToEnd();
string item2 = p.StandardError.ReadToEnd();
p.WaitForExit();
return (item, item2);
}
private static string EscapeArg(string s)
{
if (string.IsNullOrEmpty(s))
{
return "";
}
if (s.Contains('"'))
{
s = s.Replace("\"", "\\\"");
}
return "\"" + s + "\"";
}
private static string ExtractJsonString(string jsonLine, string key)
{
string text = "\"" + key + "\":";
int num = jsonLine.IndexOf(text);
if (num < 0)
{
return string.Empty;
}
for (num += text.Length; num < jsonLine.Length && char.IsWhiteSpace(jsonLine[num]); num++)
{
}
if (num >= jsonLine.Length || jsonLine[num] != '"')
{
return string.Empty;
}
num++;
StringBuilder stringBuilder = new StringBuilder();
while (num < jsonLine.Length)
{
char c = jsonLine[num++];
if (c == '"')
{
break;
}
if (c == '\\' && num < jsonLine.Length)
{
char value = jsonLine[num++];
stringBuilder.Append(value);
}
else
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString();
}
private static string ExtractJsonNumber(string jsonLine, string key)
{
string text = "\"" + key + "\":";
int num = jsonLine.IndexOf(text);
if (num < 0)
{
return string.Empty;
}
for (num += text.Length; num < jsonLine.Length && char.IsWhiteSpace(jsonLine[num]); num++)
{
}
int num2 = num;
for (; num < jsonLine.Length && (char.IsDigit(jsonLine[num]) || jsonLine[num] == '.'); num++)
{
}
return jsonLine.Substring(num2, num - num2);
}
}
}
namespace SteelSpeaker.Interaction
{
public class SpeakerInteractable : MonoBehaviour
{
[Header("Interact")]
public KeyCode interactKey = (KeyCode)101;
public float interactDistance = 2f;
[Header("Mount")]
public string mountPath = "";
public Vector3 mountLocalPos = new Vector3(0.18f, -0.12f, 0.06f);
public Vector3 mountLocalEuler = new Vector3(10f, 80f, 0f);
private Rigidbody _rb;
private Collider _col;
private Transform? _holder;
private AudioSource? _audio;
private void Awake()
{
_rb = ((Component)this).GetComponent<Rigidbody>();
if ((Object)(object)_rb == (Object)null)
{
_rb = ((Component)this).gameObject.AddComponent<Rigidbody>();
}
_col = ((Component)this).GetComponent<Collider>();
if ((Object)(object)_col == (Object)null)
{
_col = (Collider)(object)((Component)this).gameObject.AddComponent<BoxCollider>();
}
_audio = ((Component)this).GetComponentInChildren<AudioSource>();
}
private void Update()
{
//IL_0021: 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_004f: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)((Component)this).GetComponent<ConfigurableJoint>() != (Object)null)
{
return;
}
Transform val = FindLocalPlayer();
if ((Object)(object)val == (Object)null)
{
return;
}
float num = Vector3.Distance(val.position, ((Component)this).transform.position);
if ((Object)(object)_holder == (Object)null)
{
if (num <= interactDistance && Input.GetKeyDown(interactKey))
{
Pickup(val);
}
}
else if (Input.GetMouseButtonDown(1) || Input.GetKeyDown((KeyCode)103))
{
Drop(val);
}
}
private Transform? FindLocalPlayer()
{
Camera main = Camera.main;
if (!Object.op_Implicit((Object)(object)main))
{
return null;
}
return ((Component)main).transform;
}
private Transform GetMount(Transform player)
{
if (!string.IsNullOrEmpty(mountPath))
{
Transform val = player.Find(mountPath);
if ((Object)(object)val != (Object)null)
{
return val;
}
}
return player;
}
private void Pickup(Transform player)
{
//IL_003e: 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_0054: Unknown result type (might be due to invalid IL or missing references)
_holder = GetMount(player);
_rb.isKinematic = true;
_col.enabled = false;
((Component)this).transform.SetParent(_holder, false);
((Component)this).transform.localPosition = mountLocalPos;
((Component)this).transform.localRotation = Quaternion.Euler(mountLocalEuler);
if (Object.op_Implicit((Object)(object)_audio))
{
_audio.spatialBlend = 1f;
}
}
private void Drop(Transform player)
{
//IL_0026: 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_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: 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_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
((Component)this).transform.SetParent((Transform)null, true);
_rb.isKinematic = false;
_col.enabled = true;
Vector3 forward = player.forward;
_rb.AddForce(forward * 2.5f + Vector3.up * 1f, (ForceMode)1);
_holder = null;
}
}
}
namespace SteelSpeaker.Dev
{
public class MountOffsetTuner : MonoBehaviour
{
private bool _active;
private float _step = 0.01f;
private void Update()
{
if (Input.GetKeyDown((KeyCode)287))
{
_active = !_active;
}
if (Input.GetKeyDown((KeyCode)288))
{
SpeakerBehavior.Instance?.ShowDebugCube();
}
if (_active)
{
bool flag = false;
if (Input.GetKey((KeyCode)106))
{
ConfigEntry<float>? posX = ModConfig.PosX;
posX.Value -= _step;
flag = true;
}
if (Input.GetKey((KeyCode)108))
{
ConfigEntry<float>? posX2 = ModConfig.PosX;
posX2.Value += _step;
flag = true;
}
if (Input.GetKey((KeyCode)105))
{
ConfigEntry<float>? posZ = ModConfig.PosZ;
posZ.Value += _step;
flag = true;
}
if (Input.GetKey((KeyCode)107))
{
ConfigEntry<float>? posZ2 = ModConfig.PosZ;
posZ2.Value -= _step;
flag = true;
}
if (Input.GetKey((KeyCode)117))
{
ConfigEntry<float>? posY = ModConfig.PosY;
posY.Value += _step;
flag = true;
}
if (Input.GetKey((KeyCode)111))
{
ConfigEntry<float>? posY2 = ModConfig.PosY;
posY2.Value -= _step;
flag = true;
}
if (Input.GetKey((KeyCode)104))
{
ConfigEntry<float>? rotY = ModConfig.RotY;
rotY.Value -= _step * 50f;
flag = true;
}
if (Input.GetKey((KeyCode)110))
{
ConfigEntry<float>? rotY2 = ModConfig.RotY;
rotY2.Value += _step * 50f;
flag = true;
}
if (Input.GetKey((KeyCode)121))
{
ConfigEntry<float>? rotX = ModConfig.RotX;
rotX.Value -= _step * 50f;
flag = true;
}
if (Input.GetKey((KeyCode)103))
{
ConfigEntry<float>? rotX2 = ModConfig.RotX;
rotX2.Value += _step * 50f;
flag = true;
}
if (Input.GetKey((KeyCode)116))
{
ConfigEntry<float>? rotZ = ModConfig.RotZ;
rotZ.Value -= _step * 50f;
flag = true;
}
if (Input.GetKey((KeyCode)98))
{
ConfigEntry<float>? rotZ2 = ModConfig.RotZ;
rotZ2.Value += _step * 50f;
flag = true;
}
if (Input.GetKeyDown((KeyCode)61) || Input.GetKeyDown((KeyCode)43))
{
_step = Mathf.Min(_step * 2f, 0.2f);
}
if (Input.GetKeyDown((KeyCode)45) || Input.GetKeyDown((KeyCode)95))
{
_step = Mathf.Max(_step * 0.5f, 0.0025f);
}
if (Input.GetKeyDown((KeyCode)115))
{
ModConfig.Save();
}
if (flag)
{
ApplyToSpeaker();
}
}
}
private void ApplyToSpeaker()
{
//IL_0034: 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)
SpeakerBehavior instance = SpeakerBehavior.Instance;
if (!((Object)(object)instance == (Object)null))
{
((Component)instance).transform.localPosition = new Vector3(ModConfig.PosX.Value, ModConfig.PosY.Value, ModConfig.PosZ.Value);
((Component)instance).transform.localRotation = Quaternion.Euler(ModConfig.RotX.Value, ModConfig.RotY.Value, ModConfig.RotZ.Value);
}
}
private void OnGUI()
{
//IL_0038: 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_004d: 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)
if (_active)
{
Rect val = default(Rect);
((Rect)(ref val))..ctor(12f, 12f, 360f, 140f);
GUI.color = new Color(0f, 0f, 0f, 0.6f);
GUI.Box(val, GUIContent.none);
GUI.color = Color.white;
GUILayout.BeginArea(val);
GUILayout.Label("SteelSpeaker Mount Tuner (F6 to toggle)", Array.Empty<GUILayoutOption>());
GUILayout.Label($"Step: {_step:F3} | Pos X:{ModConfig.PosX.Value:F3} Y:{ModConfig.PosY.Value:F3} Z:{ModConfig.PosZ.Value:F3}", Array.Empty<GUILayoutOption>());
GUILayout.Label($"Rot X:{ModConfig.RotX.Value:F1} Y:{ModConfig.RotY.Value:F1} Z:{ModConfig.RotZ.Value:F1}", Array.Empty<GUILayoutOption>());
GUILayout.Label("Move: J/L=X-,X+ | I/K=Z+,Z- | U/O=Y+,Y-", Array.Empty<GUILayoutOption>());
GUILayout.Label("Rotate: H/N=Yaw | Y/G=Pitch | T/B=Roll | +/- step", Array.Empty<GUILayoutOption>());
GUILayout.EndArea();
}
}
}
}
namespace SteelSpeaker.Config
{
public static class ModConfig
{
public static ConfigFile? File;
public static ConfigEntry<float>? PosX;
public static ConfigEntry<float>? PosY;
public static ConfigEntry<float>? PosZ;
public static ConfigEntry<float>? RotX;
public static ConfigEntry<float>? RotY;
public static ConfigEntry<float>? RotZ;
public static ConfigEntry<float>? InitialAttachDelay;
public static ConfigEntry<string>? ToggleKey;
public static ConfigEntry<KeyboardShortcut>? UIToggleShortcut;
public static ConfigEntry<bool>? ForceDefaultLayer;
public static ConfigEntry<bool>? DebugDumpBundleAssets;
public static void Init(ConfigFile cfg)
{
//IL_0113: Unknown result type (might be due to invalid IL or missing references)
File = cfg;
PosX = cfg.Bind<float>("Mount", "PosX", 0.2f, "Local X offset from hip mount");
PosY = cfg.Bind<float>("Mount", "PosY", -0.1f, "Local Y offset from hip mount");
PosZ = cfg.Bind<float>("Mount", "PosZ", 0.05f, "Local Z offset from hip mount");
RotX = cfg.Bind<float>("Mount", "RotX", 0f, "Local rotation X (pitch) in degrees");
RotY = cfg.Bind<float>("Mount", "RotY", 90f, "Local rotation Y (yaw) in degrees");
RotZ = cfg.Bind<float>("Mount", "RotZ", 0f, "Local rotation Z (roll) in degrees");
InitialAttachDelay = cfg.Bind<float>("General", "InitialAttachDelaySeconds", 15f, "Wait time before first attach after scene load");
ToggleKey = cfg.Bind<string>("General", "UIToggleKey", "F10", "Deprecated: string key name. Prefer UIToggleShortcut.");
UIToggleShortcut = cfg.Bind<KeyboardShortcut>("General", "UIToggleShortcut", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Key (with optional modifiers) to toggle SteelSpeaker UI.");
ForceDefaultLayer = cfg.Bind<bool>("Debug", "ForceDefaultLayer", false, "If true, forces the speaker visual to Unity's Default layer (0) to bypass camera culling masks.");
DebugDumpBundleAssets = cfg.Bind<bool>("Debug", "DumpBundleAssetsOnLoad", false, "If true, logs all asset names found in the loaded AssetBundle.");
}
public static void Save()
{
ConfigFile? file = File;
if (file != null)
{
file.Save();
}
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
}