forked from nikita/muzika-gromche
171 lines
5.7 KiB
C#
171 lines
5.7 KiB
C#
using HarmonyLib;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
namespace MuzikaGromche;
|
|
|
|
internal static class AudioClipsCacheManager
|
|
{
|
|
// Cache of file names to loaded AudioClips.
|
|
// Cache is cleared at the end of each round.
|
|
static readonly Dictionary<string, AudioClip> Cache = [];
|
|
|
|
// In-flight requests
|
|
static readonly Dictionary<string, (UnityWebRequest Request, List<Action<AudioClip>> Setters)> Requests = [];
|
|
|
|
// Not just isDone status, but also whether all requests have been processed.
|
|
public static bool AllDone => Requests.Count == 0;
|
|
|
|
public static void LoadAudioTrack(IAudioTrack track)
|
|
{
|
|
GlobalBehaviour.Instance.StartCoroutine(LoadAudioTrackCoroutine(track));
|
|
}
|
|
|
|
static IEnumerator LoadAudioTrackCoroutine(IAudioTrack track)
|
|
{
|
|
List<UnityWebRequest> requests = [];
|
|
requests.Capacity = 2;
|
|
|
|
LoadAudioClip(requests, track.AudioType, track.FileNameIntro, clip => track.LoadedIntro = clip);
|
|
LoadAudioClip(requests, track.AudioType, track.FileNameLoop, clip => track.LoadedLoop = clip);
|
|
|
|
yield return new WaitUntil(() => requests.All(request => request.isDone));
|
|
|
|
if (requests.All(request => request.result == UnityWebRequest.Result.Success))
|
|
{
|
|
foreach (var request in requests)
|
|
{
|
|
foreach (var (fileName, (Request, Setters)) in Requests)
|
|
{
|
|
if (request == Request)
|
|
{
|
|
Plugin.Log.LogDebug($"Audio clip loaded successfully: {fileName}");
|
|
var clip = DownloadHandlerAudioClip.GetContent(request);
|
|
Cache[fileName] = clip;
|
|
foreach (var setter in Setters)
|
|
{
|
|
setter(clip);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var failed = Requests.Values.Where(tuple => tuple.Request.result != UnityWebRequest.Result.Success).Select(tuple => tuple.Request.GetUrl());
|
|
Plugin.Log.LogError("Could not load audio file " + string.Join(", ", failed));
|
|
}
|
|
|
|
// cleanup
|
|
foreach (var request in requests)
|
|
{
|
|
// collect matching keys first to avoid mutating Requests while iterating it
|
|
var fileNames = Requests
|
|
.Where(kv => kv.Value.Request == request)
|
|
.Select(kv => kv.Key)
|
|
.ToArray();
|
|
|
|
foreach (var fileName in fileNames)
|
|
{
|
|
if (Requests.TryGetValue(fileName, out var tuple) && tuple.Request != null)
|
|
{
|
|
tuple.Request.Dispose();
|
|
}
|
|
|
|
Requests.Remove(fileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
static readonly string dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
|
|
|
static void LoadAudioClip(List<UnityWebRequest> requests, AudioType audioType, string fileName, Action<AudioClip> setter)
|
|
{
|
|
if (Cache.TryGetValue(fileName, out var cachedClip))
|
|
{
|
|
Plugin.Log.LogDebug($"Found cached audio clip: {fileName}");
|
|
setter(cachedClip);
|
|
}
|
|
else if (Requests.TryGetValue(fileName, out var tuple))
|
|
{
|
|
Plugin.Log.LogDebug($"Found existing in-flight request for audio clip: {fileName}");
|
|
tuple.Setters.Add(setter);
|
|
}
|
|
else
|
|
{
|
|
Plugin.Log.LogDebug($"Sending request to load audio clip: {fileName}");
|
|
var request = UnityWebRequestMultimedia.GetAudioClip($"file://{dir}/{fileName}", audioType);
|
|
request.SendWebRequest();
|
|
Requests[fileName] = (request, [setter]);
|
|
requests.Add(request);
|
|
}
|
|
}
|
|
|
|
public static void Clear()
|
|
{
|
|
// Iterate over LoadedClipsCache keys and values, cross join with Plugin.Tracks list,
|
|
// find AudioTracks that reference the key (file name), and null their corresponding loaded tracks;
|
|
// then destroy tracks and clear the cache.
|
|
|
|
Plugin.Log.LogDebug($"Clearing {Cache.Count} cached audio clips and {Requests.Count} pending requests");
|
|
|
|
if (Cache.Count > 0)
|
|
{
|
|
var allTracks = Plugin.Tracks.SelectMany(t => t.GetTracks()).ToArray();
|
|
|
|
foreach (var (fileName, clip) in Cache)
|
|
{
|
|
foreach (var track in allTracks)
|
|
{
|
|
// Null out any references to this clip on matching file names.
|
|
if (track.FileNameIntro == fileName)
|
|
{
|
|
track.LoadedIntro = null;
|
|
}
|
|
if (track.FileNameLoop == fileName)
|
|
{
|
|
track.LoadedLoop = null;
|
|
}
|
|
}
|
|
|
|
if (clip != null)
|
|
{
|
|
UnityEngine.Object.Destroy(clip);
|
|
}
|
|
}
|
|
|
|
Cache.Clear();
|
|
}
|
|
|
|
foreach (var (fileName, (Request, Setters)) in Requests)
|
|
{
|
|
if (Request != null)
|
|
{
|
|
Request.Abort();
|
|
Request.Dispose();
|
|
}
|
|
}
|
|
|
|
Requests.Clear();
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(RoundManager))]
|
|
static class ClearAudioClipCachePatch
|
|
{
|
|
[HarmonyPatch(nameof(RoundManager.DespawnPropsAtEndOfRound))]
|
|
[HarmonyPatch(nameof(RoundManager.OnDestroy))]
|
|
[HarmonyPrefix]
|
|
static void OnDestroy(RoundManager __instance)
|
|
{
|
|
var _ = __instance;
|
|
AudioClipsCacheManager.Clear();
|
|
}
|
|
}
|