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 SlowSailingBagpipes v1.0.7
plugins/SailingBagpipes.dll
Decompiled a month agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using SailingBagpipes.Logging; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("SailingBagpipes")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyInformationalVersion("1.0.7+03821e47e0b46beeaf244887b10685d972ece905")] [assembly: AssemblyProduct("SailingBagpipes")] [assembly: AssemblyTitle("SailingBagpipes")] [assembly: AssemblyVersion("1.0.7.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace SailingBagpipes { [BepInPlugin("eh.mataeo.valheim.slowsailingbagpipes", "SlowSailingBagpipes", "1.0.7")] public class Plugin : BaseUnityPlugin { private const string PluginGuid = "eh.mataeo.valheim.slowsailingbagpipes"; private const string PluginName = "SlowSailingBagpipes"; private const string PluginVersion = "1.0.7"; private const float PaddleThresholdSeconds = 0.1f; private const float ResumeGraceSeconds = 10f; private const float FadeInDuration = 1.5f; private const float FadeOutDuration = 0.5f; private const string PlaceholderTrackName = "ghost_bagpipe_track.mp3"; private const string DefaultTrackDirectoryName = "BagPipesTracks"; private static readonly string[] SupportedExtensions = new string[3] { ".mp3", ".ogg", ".wav" }; private static readonly FieldInfo? ShipPlayersField = typeof(Ship).GetField("m_players", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo? MusicManMusicSourceField = typeof(MusicMan).GetField("m_musicSource", BindingFlags.Instance | BindingFlags.NonPublic); private ConfigEntry<bool>? _enabled; private ConfigEntry<float>? _volume; private ConfigEntry<string>? _trackDirectoryConfig; private PluginLogger? _fileLogger; private GameObject? _audioHolder; private AudioSource? _audioSource; private readonly Dictionary<string, AudioClip> _clipCache = new Dictionary<string, AudioClip>(); private readonly Random _rng = new Random(); private string? _pluginDirectory; private string? _trackDirectory; private List<string> _trackPaths = new List<string>(); private Coroutine? _clipLoadRoutine; private Coroutine? _fadeRoutine; private string? _pendingTrackPath; private bool _pendingPlayback; private bool _isPlaying; private bool _isPausedForGrace; private float _paddleTimer; private float _resumeUntil; private bool _musicManSuppressed; private bool _audioSourceConfiguredFromMusicMan; private float _fadeLevel; private int _syncedMusicManId; private int _lastControlledShipId = -1; private string _lastControlledShipName = "None"; private Speed? _lastSpeedSetting; private bool _lastEligibleRowingState; private string _lastControllerDescription = "None"; private bool? _lastAttachedToShip; private void Awake() { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown _pluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location) ?? Paths.PluginPath; _fileLogger = new PluginLogger("SlowSailingBagpipes", _pluginDirectory); _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master toggle for the Slow Sailing Bagpipes mod."); _volume = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Volume", 0.85f, new ConfigDescription("Bagpipe volume, applied on top of the in-game Master and Music volume sliders (1.0 = as loud as those sliders allow).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); _trackDirectoryConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Audio", "TrackDirectory", "BagPipesTracks", "Directory (absolute or relative to the plugin folder) that contains bagpipe audio clips."); UpdateTrackDirectory(_trackDirectoryConfig.Value); _audioSource = CreateAudioSource(); ((BaseUnityPlugin)this).Config.SettingChanged += OnConfigSettingChanged; LogInfo("Initialized. Watching track directory: " + _trackDirectory); } private void OnDestroy() { ((BaseUnityPlugin)this).Config.SettingChanged -= OnConfigSettingChanged; StopBagpipes(immediate: true); ClearClipCache(); if ((Object)(object)_audioHolder != (Object)null) { Object.Destroy((Object)(object)_audioHolder); _audioHolder = null; } } private void Update() { //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> enabled = _enabled; if (enabled == null || !enabled.Value) { if (_isPlaying || _isPausedForGrace || _pendingPlayback) { StopBagpipes(immediate: true); } return; } if ((Object)(object)_audioSource == (Object)null) { LogWarn("Audio source not initialized; skipping update."); return; } SyncAudioSourceWithMusicMan(); ReassertMusicSuppression(); ApplyEffectiveVolume(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { if (_isPlaying || _isPausedForGrace || _pendingPlayback) { StopBagpipes(immediate: true); } LogControlState(null, null, null, "None"); return; } string controllerDescription; Ship val = ResolveControlledShip(localPlayer, out controllerDescription); Speed? speedSetting = ((val != null) ? new Speed?(val.GetSpeedSetting()) : ((Speed?)null)); LogControlState(localPlayer, val, speedSetting, controllerDescription); if (speedSetting.HasValue && IsRowingAtTriggerSpeed(speedSetting.Value)) { HandleRowingState(val, speedSetting.Value); } else { HandleNonRowingState(val, speedSetting); } } private void OnConfigSettingChanged(object? sender, SettingChangedEventArgs args) { if (!((Object)(object)_audioSource == (Object)null)) { if ((object)args.ChangedSetting == _volume) { ApplyEffectiveVolume(); } else if (_trackDirectoryConfig != null && (object)args.ChangedSetting == _trackDirectoryConfig) { LogInfo("Track directory changed to " + _trackDirectoryConfig.Value + "; reloading library."); StopBagpipes(immediate: true); ClearClipCache(); UpdateTrackDirectory(_trackDirectoryConfig.Value); } } } private static bool IsRowingAtTriggerSpeed(Speed speedSetting) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 if ((int)speedSetting != 2) { return (int)speedSetting == 1; } return true; } private void HandleRowingState(Ship ship, Speed speedSetting) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (!_lastEligibleRowingState) { LogInfo($"Detected eligible rowing on {GetShipDisplayName(ship)} at speed setting {speedSetting}; waiting for {0.1f:F1}s threshold."); } _lastEligibleRowingState = true; _paddleTimer += Time.deltaTime; if (_isPausedForGrace && Time.time <= _resumeUntil) { LogInfo("Resuming paused clip on " + GetShipDisplayName(ship) + " within grace window."); ResumeBagpipes(); return; } bool flag = _resumeUntil > 0f && Time.time <= _resumeUntil; if (!_isPlaying && !_pendingPlayback && !_isPausedForGrace && (_paddleTimer >= 0.1f || flag)) { LogInfo($"Rowing threshold satisfied on {GetShipDisplayName(ship)} at speed setting {speedSetting}; starting bagpipes."); StartBagpipes(); } } private void HandleNonRowingState(Ship? ship, Speed? speedSetting) { //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) if (_lastEligibleRowingState) { string arg = (((Object)(object)ship != (Object)null) ? GetShipDisplayName(ship) : _lastControlledShipName); string arg2 = (speedSetting.HasValue ? ((object)speedSetting.GetValueOrDefault()/*cast due to .constrained prefix*/).ToString() : null) ?? "None"; LogInfo($"Eligible rowing ended on {arg}; current speed setting {arg2}; timer reached {_paddleTimer:F2}s."); } _lastEligibleRowingState = false; _paddleTimer = 0f; if (_pendingPlayback) { CancelPendingPlayback(); } if (_isPlaying) { _resumeUntil = Time.time + 10f; PauseBagpipesForGrace(); } else if (_isPausedForGrace && Time.time > _resumeUntil) { StopBagpipes(immediate: true); } else if (!_isPausedForGrace && Time.time > _resumeUntil) { _resumeUntil = 0f; } } private AudioSource CreateAudioSource() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _audioHolder = new GameObject("SlowSailingBagpipes_AudioCarrier"); Object.DontDestroyOnLoad((Object)(object)_audioHolder); AudioSource obj = _audioHolder.AddComponent<AudioSource>(); obj.playOnAwake = false; obj.loop = true; obj.volume = 0f; obj.spatialBlend = 0f; obj.priority = 64; return obj; } private void SyncAudioSourceWithMusicMan() { if ((Object)(object)_audioSource == (Object)null) { return; } MusicMan instance = MusicMan.instance; if ((Object)(object)instance == (Object)null) { return; } int instanceID = ((Object)instance).GetInstanceID(); if (!_audioSourceConfiguredFromMusicMan || instanceID != _syncedMusicManId) { AudioSource musicManAudioSource = GetMusicManAudioSource(); if (!((Object)(object)musicManAudioSource == (Object)null)) { _audioSource.outputAudioMixerGroup = musicManAudioSource.outputAudioMixerGroup; _audioSource.priority = musicManAudioSource.priority; _audioSource.pitch = 1f; _audioSource.panStereo = 0f; _audioSource.reverbZoneMix = musicManAudioSource.reverbZoneMix; _audioSource.bypassEffects = musicManAudioSource.bypassEffects; _audioSource.bypassListenerEffects = musicManAudioSource.bypassListenerEffects; _audioSource.bypassReverbZones = musicManAudioSource.bypassReverbZones; _audioSource.ignoreListenerPause = musicManAudioSource.ignoreListenerPause; _audioSource.ignoreListenerVolume = musicManAudioSource.ignoreListenerVolume; _audioSource.mute = false; _audioSource.spatialBlend = 0f; _audioSource.dopplerLevel = 0f; _audioSource.spread = 0f; _audioSourceConfiguredFromMusicMan = true; _syncedMusicManId = instanceID; LogInfo("Audio source synced to Valheim music mixer settings."); } } } private void LogControlState(Player? player, Ship? ship, Speed? speedSetting, string controllerDescription) { //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { if (_lastControlledShipId != -1 || _lastSpeedSetting.HasValue || _lastControllerDescription != "None" || _lastAttachedToShip.HasValue) { LogInfo("No local player is active; cleared ship control state."); } _lastControlledShipId = -1; _lastControlledShipName = "None"; _lastSpeedSetting = null; _lastControllerDescription = "None"; _lastAttachedToShip = null; return; } bool flag = ((Character)player).IsAttachedToShip(); if ((Object)(object)ship == (Object)null) { if (_lastControlledShipId != -1 || _lastSpeedSetting.HasValue || controllerDescription != _lastControllerDescription || flag != _lastAttachedToShip) { LogInfo($"No controlled ship resolved. AttachedToShip={flag} controller={controllerDescription}."); } _lastControlledShipId = -1; _lastControlledShipName = "None"; _lastSpeedSetting = null; _lastControllerDescription = controllerDescription; _lastAttachedToShip = flag; return; } int instanceID = ((Object)ship).GetInstanceID(); string shipDisplayName = GetShipDisplayName(ship); if (instanceID != _lastControlledShipId || speedSetting != _lastSpeedSetting || controllerDescription != _lastControllerDescription || flag != _lastAttachedToShip) { LogInfo($"Ship control state: ship={shipDisplayName} speedSetting={speedSetting} speed={ship.GetSpeed():F2} rudder={ship.GetRudder():F2} attached={flag} controller={controllerDescription}."); } _lastControlledShipId = instanceID; _lastControlledShipName = shipDisplayName; _lastSpeedSetting = speedSetting; _lastControllerDescription = controllerDescription; _lastAttachedToShip = flag; } private static string GetShipDisplayName(Ship ship) { string text = (((Object)(object)((Component)ship).gameObject != (Object)null) ? ((Object)((Component)ship).gameObject).name : ((Object)ship).name); if (!string.IsNullOrWhiteSpace(text)) { return text; } return ((object)ship).GetType().Name; } private static Ship? ResolveControlledShip(Player player, out string controllerDescription) { Ship controlledShip = player.GetControlledShip(); if ((Object)(object)controlledShip != (Object)null) { controllerDescription = "Player.GetControlledShip"; return controlledShip; } IDoodadController doodadController = player.GetDoodadController(); if (doodadController == null) { controllerDescription = "None"; return null; } Component controlledComponent = doodadController.GetControlledComponent(); controllerDescription = (((Object)(object)controlledComponent == (Object)null) ? ((object)doodadController).GetType().Name : (((object)doodadController).GetType().Name + "->" + ((object)controlledComponent).GetType().Name)); Ship val = (Ship)(object)((controlledComponent is Ship) ? controlledComponent : null); if (val != null) { return val; } if ((Object)(object)controlledComponent == (Object)null) { return ResolveAttachedShip(player, ref controllerDescription); } object obj = controlledComponent.GetComponent<Ship>() ?? controlledComponent.GetComponentInParent<Ship>(); if (obj == null) { obj = ResolveAttachedShip(player, ref controllerDescription); } return (Ship?)obj; } private static Ship? ResolveAttachedShip(Player player, ref string controllerDescription) { //IL_0041: 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) Ship val = null; float num = float.MaxValue; Ship[] array = Object.FindObjectsByType<Ship>((FindObjectsSortMode)0); foreach (Ship val2 in array) { if (ShipContainsPlayer(val2, player)) { controllerDescription = AppendResolutionSource(controllerDescription, "Ship.m_players"); return val2; } if (((Character)player).IsAttachedToShip()) { float num2 = Vector3.Distance(((Component)player).transform.position, ((Component)val2).transform.position); if (num2 < num) { num = num2; val = val2; } } } if ((Object)(object)val != (Object)null && num <= 25f) { controllerDescription = AppendResolutionSource(controllerDescription, $"NearestAttachedShip({num:F1}m)"); return val; } return null; } private static bool ShipContainsPlayer(Ship ship, Player player) { if (!(ShipPlayersField?.GetValue(ship) is List<Player> list)) { return false; } return list.Contains(player); } private static string AppendResolutionSource(string currentDescription, string source) { if (string.IsNullOrWhiteSpace(currentDescription) || currentDescription == "None") { return source; } return currentDescription + "+" + source; } private void EnsurePlaceholderTrackExists(string trackDirectory) { string path = Path.Combine(trackDirectory, "ghost_bagpipe_track.mp3"); if (!File.Exists(path)) { File.WriteAllBytes(path, Array.Empty<byte>()); LogInfo("Created placeholder track file. Replace it with a real MP3, OGG, or WAV file."); } } private void RefreshTrackLibrary() { if (_trackDirectory != null) { _trackPaths = (from path in Directory.EnumerateFiles(_trackDirectory, "*.*", SearchOption.TopDirectoryOnly) where SupportedExtensions.Contains<string>(Path.GetExtension(path), StringComparer.OrdinalIgnoreCase) where new FileInfo(path).Length > 0 select path).OrderBy<string, string>((string path) => path, StringComparer.OrdinalIgnoreCase).ToList(); if (_trackPaths.Count == 0) { LogWarn("No playable bagpipe tracks found in " + _trackDirectory + ". Add non-empty MP3, OGG, or WAV files."); } else { LogInfo($"Loaded {_trackPaths.Count} playable bagpipe track(s) from {_trackDirectory}."); } } } private void UpdateTrackDirectory(string directorySetting) { if (string.IsNullOrWhiteSpace(directorySetting)) { directorySetting = "BagPipesTracks"; if (_trackDirectoryConfig != null) { _trackDirectoryConfig.Value = "BagPipesTracks"; } } string text = (_trackDirectory = ResolveTrackDirectory(directorySetting)); Directory.CreateDirectory(text); if (IsDefaultTrackDirectory(directorySetting)) { EnsurePlaceholderTrackExists(text); } RefreshTrackLibrary(); } private string ResolveTrackDirectory(string configuredPath) { if (Path.IsPathRooted(configuredPath)) { return configuredPath; } return Path.Combine(_pluginDirectory ?? Paths.PluginPath, configuredPath); } private static bool IsDefaultTrackDirectory(string directorySetting) { return string.Equals(directorySetting, "BagPipesTracks", StringComparison.OrdinalIgnoreCase); } private void StartBagpipes() { if ((Object)(object)_audioSource == (Object)null) { LogWarn("Audio source missing; cannot start playback."); return; } SyncAudioSourceWithMusicMan(); if (_trackPaths.Count == 0) { RefreshTrackLibrary(); if (_trackPaths.Count == 0) { return; } } string text = SelectRandomTrack(); if (text == null) { LogWarn("Track selection returned no result."); return; } _pendingPlayback = true; _pendingTrackPath = text; if (_clipCache.TryGetValue(text, out AudioClip value) && (Object)(object)value != (Object)null) { PlayClip(value, text); return; } CancelFade(); if (_clipLoadRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_clipLoadRoutine); } _clipLoadRoutine = ((MonoBehaviour)this).StartCoroutine(LoadClipCoroutine(text)); } private void PauseBagpipesForGrace() { if ((Object)(object)_audioSource == (Object)null || (Object)(object)_audioSource.clip == (Object)null || _isPausedForGrace) { return; } _isPlaying = false; _isPausedForGrace = true; StartFade(0f, 0.5f, delegate { if (!((Object)(object)_audioSource == (Object)null)) { if (_isPausedForGrace && Time.time <= _resumeUntil) { _audioSource.Pause(); } else { _audioSource.Stop(); _audioSource.clip = null; _isPausedForGrace = false; } SetGameMusicSuppressed(shouldSuppress: false); } }); } private void ResumeBagpipes() { if ((Object)(object)_audioSource == (Object)null || (Object)(object)_audioSource.clip == (Object)null) { _isPausedForGrace = false; StartBagpipes(); return; } CancelFade(); if (!_audioSource.isPlaying) { _audioSource.UnPause(); } _isPausedForGrace = false; _isPlaying = true; SetGameMusicSuppressed(shouldSuppress: true); StartFade(1f, 1.5f); } private void StopBagpipes(bool immediate = false) { CancelPendingPlayback(); if ((Object)(object)_audioSource == (Object)null) { _isPlaying = false; _isPausedForGrace = false; _resumeUntil = 0f; return; } CancelFade(); _isPlaying = false; _isPausedForGrace = false; _resumeUntil = 0f; if (immediate) { _audioSource.Stop(); _audioSource.clip = null; _fadeLevel = 0f; ApplyEffectiveVolume(); SetGameMusicSuppressed(shouldSuppress: false); return; } StartFade(0f, 0.5f, delegate { if (!((Object)(object)_audioSource == (Object)null)) { _audioSource.Stop(); _audioSource.clip = null; SetGameMusicSuppressed(shouldSuppress: false); } }); } private void StartFade(float targetLevel, float fadeDuration, Action? onComplete = null) { CancelFade(); _fadeRoutine = ((MonoBehaviour)this).StartCoroutine(FadeVolume(targetLevel, fadeDuration, onComplete)); } private void CancelFade() { if (_fadeRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_fadeRoutine); _fadeRoutine = null; } } private IEnumerator FadeVolume(float targetLevel, float fadeDuration, Action? onComplete = null) { if ((Object)(object)_audioSource == (Object)null) { yield break; } if (fadeDuration <= 0f) { _fadeLevel = targetLevel; ApplyEffectiveVolume(); _fadeRoutine = null; onComplete?.Invoke(); yield break; } float startLevel = _fadeLevel; float elapsed = 0f; while (elapsed < fadeDuration) { if ((Object)(object)_audioSource == (Object)null) { yield break; } elapsed += Time.deltaTime; float num = Mathf.Clamp01(elapsed / fadeDuration); _fadeLevel = Mathf.Lerp(startLevel, targetLevel, num); ApplyEffectiveVolume(); yield return null; } _fadeLevel = targetLevel; ApplyEffectiveVolume(); _fadeRoutine = null; onComplete?.Invoke(); } private void ApplyEffectiveVolume() { if (!((Object)(object)_audioSource == (Object)null) && _volume != null) { float num = Mathf.Clamp01(_volume.Value); _audioSource.volume = _fadeLevel * num * GetGameMusicVolume(); } } private static float GetGameMusicVolume() { return Mathf.Clamp01(MusicMan.m_masterMusicVolume); } private void SetGameMusicSuppressed(bool shouldSuppress) { AudioSource musicManAudioSource = GetMusicManAudioSource(); if ((Object)(object)musicManAudioSource == (Object)null) { _musicManSuppressed = false; return; } musicManAudioSource.mute = shouldSuppress; _musicManSuppressed = shouldSuppress; } private void ReassertMusicSuppression() { if (_musicManSuppressed) { AudioSource musicManAudioSource = GetMusicManAudioSource(); if ((Object)(object)musicManAudioSource != (Object)null && !musicManAudioSource.mute) { musicManAudioSource.mute = true; } } } private AudioSource? GetMusicManAudioSource() { MusicMan instance = MusicMan.instance; if ((Object)(object)instance == (Object)null || MusicManMusicSourceField == null) { return null; } try { object? value = MusicManMusicSourceField.GetValue(instance); return (AudioSource?)((value is AudioSource) ? value : null); } catch (Exception ex) { LogWarn("Unable to resolve MusicMan audio source via reflection: " + ex.GetType().Name + ": " + ex.Message); return null; } } private string? SelectRandomTrack() { if (_trackPaths.Count == 0) { return null; } int index = _rng.Next(0, _trackPaths.Count); return _trackPaths[index]; } private IEnumerator LoadClipCoroutine(string trackPath) { LogInfo("Loading track " + Path.GetFileName(trackPath) + "."); Uri uri = new Uri(trackPath); AudioType audioTypeForExtension = GetAudioTypeForExtension(Path.GetExtension(trackPath)); UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(uri.AbsoluteUri, audioTypeForExtension); try { yield return request.SendWebRequest(); if ((int)request.result != 1) { LogError("Failed to load " + trackPath + ": " + request.error); _pendingPlayback = false; _pendingTrackPath = null; _clipLoadRoutine = null; yield break; } AudioClip content = DownloadHandlerAudioClip.GetContent(request); ((Object)content).name = Path.GetFileNameWithoutExtension(trackPath); _clipCache[trackPath] = content; LogInfo($"Loaded clip metadata: length={content.length:F1}s channels={content.channels} frequency={content.frequency}."); if (!_pendingPlayback || _pendingTrackPath != trackPath) { _clipLoadRoutine = null; yield break; } PlayClip(content, trackPath); _clipLoadRoutine = null; } finally { ((IDisposable)request)?.Dispose(); } } private void PlayClip(AudioClip clip, string trackPath) { if ((Object)(object)_audioSource == (Object)null) { LogWarn("Audio source missing during playback start."); return; } LogInfo("Starting clip " + Path.GetFileName(trackPath) + "."); CancelFade(); _pendingPlayback = false; _pendingTrackPath = null; _isPausedForGrace = false; _isPlaying = true; _audioSource.clip = clip; _fadeLevel = 0f; ApplyEffectiveVolume(); _audioSource.mute = false; _audioSource.Play(); SetGameMusicSuppressed(shouldSuppress: true); StartFade(1f, 1.5f); } private void CancelPendingPlayback() { _pendingPlayback = false; _pendingTrackPath = null; if (_clipLoadRoutine != null) { ((MonoBehaviour)this).StopCoroutine(_clipLoadRoutine); _clipLoadRoutine = null; } } private void ClearClipCache() { foreach (AudioClip value in _clipCache.Values) { if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } _clipCache.Clear(); } private static AudioType GetAudioTypeForExtension(string extension) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) return (AudioType)(extension.ToLowerInvariant() switch { ".mp3" => 13, ".ogg" => 14, ".wav" => 20, _ => 0, }); } private void LogDebug(string message) { ((BaseUnityPlugin)this).Logger.LogDebug((object)message); _fileLogger?.Debug(message); } private void LogInfo(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); _fileLogger?.Info(message); } private void LogWarn(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); _fileLogger?.Warn(message); } private void LogError(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); _fileLogger?.Error(message); } } internal static class GeneratedModVersion { public const string Value = "1.0.7"; } } namespace SailingBagpipes.Logging { internal sealed class PluginLogger { private const int MaxLogFiles = 7; private readonly string _projectName; private readonly string _logDirectory; private readonly string _logPath; private readonly object _gate = new object(); internal PluginLogger(string projectName, string pluginDirectory) { _projectName = projectName; _logDirectory = Path.Combine(pluginDirectory, "Logs"); Directory.CreateDirectory(_logDirectory); _logPath = Path.Combine(_logDirectory, $"{_projectName}-{DateTime.Now:yyyy-MM-dd-HH-mm}.log"); CleanupOldLogs(); } internal void Debug(string message) { Write("DEBUG", message); } internal void Info(string message) { Write("INFO", message); } internal void Warn(string message) { Write("WARN", message); } internal void Error(string message) { Write("ERROR", message); } private void Write(string level, string message) { string text = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}][{level}] {message}"; lock (_gate) { File.AppendAllText(_logPath, text + Environment.NewLine); } } private void CleanupOldLogs() { foreach (string item in Directory.GetFiles(_logDirectory, _projectName + "-*.log").OrderByDescending(File.GetCreationTimeUtc).ToList() .Skip(7)) { try { File.Delete(item); } catch (IOException) { } } } } }