#if UNITY_EDITOR
//////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2020 Audiokinetic Inc. / All Rights Reserved
//
//////////////////////////////////////////////////////////////////////
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
///
/// This class wraps the client that communicates with the Wwise Authoring application via WAAPI.
/// Given that only one request can be pending on the websocket, a queue is used to consume all calls sequentially.
/// Messages sent to WAAPI use the JSON format and are serialized by Unity Json serialization.
/// Helper classes (\ref WaapiHelper) for serialization, keywords for WAAPI commands (\ref WaapiKeywords), and classes for serializing message arguments and deserializing responses are found in AkWaapiHelper.cs.
/// Uri.cs contains classes with fields containing URI strings for WAAPI calls and error messages.
///
[UnityEditor.InitializeOnLoad]
public partial class AkWaapiUtilities
{
static private AkWaapiClient m_WaapiClient;
static bool isDisconnecting;
static Dictionary m_ItemTransports;
public static string ErrorMessage;
///
/// Fired when the connection is closing or closed, the bool parameter represents whether the socket connection is still open (for cleaning up subscriptions).
///
public static System.Action Disconnecting;
///
/// Fired when the connection is established, should be used by external classes to subscribe to topics they are interested in.
///
public static System.Action Connected;
///
/// Fired when all commands in the queue have been executed.
///
public static System.Action QueueConsumed;
private static ConcurrentQueue waapiCommandQueue = new ConcurrentQueue();
///
/// Generic delegate function for callbacks that expect to receive a list of objects in response to a WAAPI request.
///
///
public delegate void GetResultListDelegate(List result);
///
/// Generic delegate function for callbacks that expect to receive a single object in response to a WAAPI request.
///
///
public delegate void GetResultDelegate(T result);
///
/// Used to store store UnityEngine.Application.dataPath because we can't access it outside of the main loop
///
private static string dataPath;
///
/// Bind disconnection method to compilation started delegate and start the async Waapi loop.
///
static AkWaapiUtilities()
{
#if UNITY_2019_1_OR_NEWER
UnityEditor.Compilation.CompilationPipeline.compilationStarted += (object context) => FireDisconnect(true);
#else
UnityEditor.Compilation.CompilationPipeline.assemblyCompilationStarted +=
(string assemblyPath) =>
{
if (assemblyPath == "Library/ScriptAssemblies/AK.Wwise.Unity.API.dll")
FireDisconnect(true);
};
#endif
isDisconnecting = false;
dataPath = UnityEngine.Application.dataPath;
Loop();
}
///
/// A simple structure containing an async payload function that will be executed when it is consumed by the command queue.
///
public struct WaapiCommand
{
System.Func payload;
public WaapiCommand(System.Func payload)
{
this.payload = payload;
}
public async Task Execute()
{
await payload.Invoke();
}
}
///
/// Class used to store information about a specific subscription.
///
public class SubscriptionInfo
{
public string Uri;
public Wamp.PublishHandler Callback;
public uint SubscriptionId;
public SubscriptionInfo(string uri, Wamp.PublishHandler cb)
{
Uri = uri;
Callback = cb;
SubscriptionId = 0;
}
};
///
/// Holds information about a playing transport.
///
struct TransportInfo
{
public int TransportID;
public uint SubscriptionID;
public TransportInfo(int transID, uint subsID)
{
TransportID = transID;
SubscriptionID = subsID;
}
};
///
/// Stores TransportInfo of playing Events.
///
private static Dictionary ItemTransports
{
get
{
if (m_ItemTransports == null)
m_ItemTransports = new Dictionary();
return m_ItemTransports;
}
}
///
/// WAAPI client wrapping WAMP calls. Lazy instantiated.
///
private static AkWaapiClient WaapiClient
{
get
{
if (m_WaapiClient == null)
{
m_WaapiClient = new AkWaapiClient();
m_WaapiClient.Disconnected += Disconnected;
}
return m_WaapiClient;
}
}
///
/// Check whether the client is currently connected.
///
///
public static bool IsConnected()
{
if (m_WaapiClient == null) return false;
return WaapiClient.IsConnected();
}
private static bool kill;
private static int loopSleep = 0;
private static bool projectConnected = false;
///
/// Main loop for the WAAPI API. Checks if the client is connected and consumes all commands.
///
private static async void Loop()
{
try
{
ErrorMessage = "";
if (await CheckConnection())
{
await ConsumeCommandQueue();
}
if (!kill)
{
if (loopSleep > 0)
{
await Task.Delay(loopSleep * 1000);
}
}
}
//Handle socket issues caused by closing Wwise Authoring.
catch (System.Net.WebSockets.WebSocketException)
{
UnityEngine.Debug.Log("Wwise Unity : WAAPI disconnected because Wwise Authoring was closed");
Disconnecting?.Invoke(false);
waapiCommandQueue = new ConcurrentQueue();
projectConnected = false;
try
{
await m_WaapiClient.Close();
}
//Closing the client will throw other exceptions because it tries to send messages to a closed socket.
catch (System.Net.Sockets.SocketException)
{
}
}
catch (Wamp.WampNotConnectedException e)
{
ErrorMessage = e.Message;
}
finally
{
UnityEditor.EditorApplication.delayCall += () => Loop();
}
}
///
/// Consumes all WAAPICommands in the queue and then fires QueueConsumed.
///
/// Awaitable Task
private static async Task ConsumeCommandQueue()
{
bool shouldUpdate = false;
while (waapiCommandQueue.Count > 0)
{
if (waapiCommandQueue.TryDequeue(out WaapiCommand cmd))
{
try
{
await cmd.Execute();
shouldUpdate = true;
ErrorMessage = "";
}
catch (Wamp.ErrorException e)
{
ErrorMessage msg = UnityEngine.JsonUtility.FromJson(e.Json);
if (msg != null)
{
if (msg.message != null)
ErrorMessage = msg.message;
}
switch (e.Uri)
{
case ak.wwise.error.unavailable:
case ak.wwise.error.unexpected_error:
case ak.wwise.error.wwise_console:
case ak.wwise.error.locked:
case ak.wwise.error.file_error:
waapiCommandQueue.Enqueue(cmd);
break;
case ak.wwise.error.invalid_object:
case ak.wwise.error.invalid_property:
case ak.wwise.error.invalid_query:
case ak.wwise.error.invalid_reference:
case ak.wwise.error.invalid_options:
case ak.wwise.error.invalid_json:
case ak.wwise.error.invalid_arguments:
default:
UnityEngine.Debug.Log(ErrorMessage);
break;
}
break;
}
catch (Wamp.WampNotConnectedException e)
{
waapiCommandQueue.Enqueue(cmd);
throw (e);
}
}
}
if (shouldUpdate)
{
QueueConsumed?.Invoke();
}
}
///
/// Checks the global WAAPI settings and disconnects if WAAPI is disabled or connection settings have changed.
/// If disconnected, try to connect with current settings.
///
/// True if the client is connected
private static async Task CheckConnection()
{
if (AkWwiseEditorSettings.Instance.UseWaapi)
{
// If WAAPI connection settings have changed, unsubcribe and close the connection.
if (ConnectionSettingsChanged() && WaapiClient.IsConnected())
{
FireDisconnect(false);
return true;
}
if (!WaapiClient.IsConnected())
{
try
{
await m_WaapiClient.Connect(GetUri());
}
catch (System.Exception)
{
ConnectionFailed("Connection refused");
}
}
if (WaapiClient.IsConnected())
{
var projectOpen = await CheckProjectLoaded();
if (!projectConnected && projectOpen)
{
projectConnected = true;
loopSleep = 0;
Connected?.Invoke();
}
else if (projectConnected && !projectOpen)
{
FireDisconnect(false);
return true;
}
}
}
else
{
if (WaapiClient.IsConnected() && !isDisconnecting)
{
FireDisconnect(false);
return true;
}
}
return WaapiClient.IsConnected() && projectConnected;
}
///
/// Tries to communicate with Wwise and compares the current open project with the project path specified in the Unity Wwise Editor settings.
///
/// True if the correct wwise project is open in Wwise.
private async static Task CheckProjectLoaded()
{
try
{
var result = await GetProjectInfo();
if (result.Count == 0)
{
throw new Wamp.ErrorException("Did not get a response from Wwise project");
}
var projectInfo = result[0];
#if UNITY_EDITOR_OSX
var d1 = AkUtilities.ParseOsxPathFromWinePath(projectInfo.filePath);
#else
var d1 = projectInfo.filePath;
#endif
var d2 = AkUtilities.GetFullPath(dataPath, AkWwiseEditorSettings.Instance.WwiseProjectPath);
d1 = d1.Replace("/", "\\");
d2 = d2.Replace("/", "\\");
if (d1 != d2)
{
ConnectionFailed($"The wrong project({projectInfo.name}) is open in Wwise");
return false;
}
}
catch (Wamp.ErrorException e)
{
if (e.Json != null)
{
ErrorMessage msg = UnityEngine.JsonUtility.FromJson(e.Json);
if (msg != null)
{
if (msg.message != null)
ErrorMessage = msg.message;
}
}
if (e.Uri == "ak.wwise.locked")
{
return true;
}
ConnectionFailed($"No project is open in Wwise yet");
return false;
}
return true;
}
private static void ConnectionFailed(string message)
{
loopSleep = Math.Min(Math.Max(loopSleep * 2, 1), 32);
ErrorMessage = $"{message} - Retrying in {loopSleep}s";
}
///
/// Starts the diconnection process.
/// Invokes Disconnecting() so that other classes using WAAPI can clean up and add commands to unsubscribe from topics.
/// Consumes the last batch of commands in the command queue then closes the client.
///
private static void FireDisconnect(bool killLoop)
{
projectConnected = false;
isDisconnecting = true;
Disconnecting?.Invoke(true);
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await CloseClient(killLoop)));
}
private static async Task CloseClient(bool killLoop)
{
await WaapiClient.Close();
isDisconnecting = false;
if (killLoop)
{
kill = true;
}
}
///
/// Invoked after the client has disconnected from Wwise authoring.
///
public static void Disconnected()
{
Disconnecting?.Invoke(false);
}
private static string GetUri()
{
return $"ws://{AkWwiseEditorSettings.Instance.WaapiIP}:{AkWwiseEditorSettings.Instance.WaapiPort}/waapi";
}
static string ip;
static string port;
static string projectPath;
private static bool ConnectionSettingsChanged()
{
bool changed = false;
if (ip != AkWwiseEditorSettings.Instance.WaapiIP)
{
ip = AkWwiseEditorSettings.Instance.WaapiIP;
changed = true;
}
if (port != AkWwiseEditorSettings.Instance.WaapiPort)
{
port = AkWwiseEditorSettings.Instance.WaapiPort;
changed = true;
}
if (projectPath != AkWwiseEditorSettings.Instance.WwiseProjectPath)
{
projectPath = AkWwiseEditorSettings.Instance.WwiseProjectPath;
changed = true;
}
return changed;
}
///
/// Returns a rich text string representing the current WAAPI connection status.
///
///
public static string GetStatusString()
{
var returnString = "";
if (!AkWwiseEditorSettings.Instance.UseWaapi)
{
returnString += " Waapi disabled in project settings ";
}
else if (WaapiClient.wamp != null)
{
var state = WaapiClient.wamp.SocketState();
switch (state)
{
case System.Net.WebSockets.WebSocketState.Open:
returnString += " Connected";
break;
case System.Net.WebSockets.WebSocketState.Closed:
returnString += " Disconnected ";
break;
case System.Net.WebSockets.WebSocketState.Connecting:
returnString += $" Connecting to { GetUri()}";
break;
default:
returnString += $" Connecting to { GetUri()}";
break;
}
}
else
{
returnString += " Disconnected ";
}
if (ErrorMessage != string.Empty)
returnString += $" {ErrorMessage}";
return returnString;
}
private static async Task> GetProjectInfo()
{
var args = new WaqlArgs($"from type {WaapiKeywords.PROJECT}");
var options = new ReturnOptions(new string[] { "filePath" });
var result = await WaapiClient.Call(ak.wwise.core.@object.get, args, options);
var ret = UnityEngine.JsonUtility.FromJson(result).@return;
return ParseObjectInfo(ret);
}
///
/// Use this function to enqueue a command with no expected return object.
///
/// The URI of the waapi command
/// The command-specific arguments
/// The command-specific options
public static void QueueCommand(string uri, string args, string options)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await WaapiClient.Call(uri, args, options)));
}
///
/// Use this function to enqueue a command with an expected return object of type T.
/// The command will deserialize the respone as type T and pass it to the callback.
/// ///
/// Type of the expected return object
/// The URI of the waapi command
/// The command-specific arguments
/// The command-specific options
/// Function accepting an argument of type T
public static void QueueCommandWithReturnType(string uri, GetResultDelegate callback, string args = null, string options = null)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () =>
{
var result = await WaapiClient.Call(uri, args, options);
callback(UnityEngine.JsonUtility.FromJson(result));
}));
}
///
/// Enqueues a command with a payload that desirializes the list of wwise objects from the response.
///
///
///
///
public static void QueueCommandWithReturnWwiseObjects(WaqlArgs args, ReturnOptions options, GetResultListDelegate callback)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () =>
{
var result = await WaapiClient.Call(ak.wwise.core.@object.get, args, options);
var ret = UnityEngine.JsonUtility.FromJson>(result);
callback.Invoke(ret.@return);
}));
}
///
/// Generic function for fetching a Wwise object with custom return options.
///
/// Type of the object to be deserialized from the response.
/// GUID of the target object.
/// Specifies which object properties to include in the response
/// Function accteping a list of T objects.
public static void GetWwiseObject(System.Guid guid, ReturnOptions options, GetResultListDelegate callback)
{
GetWwiseObjects(new List() { guid }, options, callback);
}
///
/// Generic function for fetching a list of Wwise objects with custom return options.
///
/// Type of the object to be deserialized from the response.
/// GUIDs of the target objects.
/// Specifies which object properties to include in the response
/// Function accteping a list of T objects.
public static void GetWwiseObjects(List guids, ReturnOptions options, GetResultListDelegate callback)
{
string guidString = "";
foreach (var guid in guids)
{
guidString += $"{guid:B} ,";
}
var args = new WaqlArgs($"from object \"{guidString}\" ");
QueueCommandWithReturnWwiseObjects(args, options, callback);
}
///
/// Enqueues a waapi command to fetch the specified object and all of its ancestors in the hierarchy.
/// Passes the list of WwiseObjectInfo containing the specified object and ancestors to the callback.
///
/// GUID of the target object.
/// Specifies which object properties to include in the response
/// Function accepting a list of WwiseObjectInfo.
public static void GetWwiseObjectAndAncestors(System.Guid guid, ReturnOptions options, GetResultListDelegate callback)
{
var args = new WaqlArgs($"from object \"{guid:B}\" select this, ancestors orderby path");
QueueCommandWithReturnWwiseObjects(args, options, callback);
}
///
/// Enqueues a waapi comand to fetch the specified object and all of its descendants in the hierarchy to a specified depth.
/// Passes the list of WwiseObjectInfo containing the specified object and descendants to the callback.
///
/// GUID of the target object.
/// Specifies which object properties to include in the response
/// Depth of descendants to fetch. If -1, fetches all descendants.
/// Function accepting a list of WwiseObjectInfo.
public static void GetWwiseObjectAndDescendants(System.Guid guid, ReturnOptions options, int depth, GetResultListDelegate callback)
{
GetWwiseObjectAndDescendants(guid.ToString("B"), options, depth, callback);
}
///
/// Composes a WAQL "from object" request based on the parameters and enqueues a WAAPI command.
/// Passes the list of WwiseObjectInfo containing the results to the callback
///
/// Can bethe target object GUID or path within the hierarchy.
/// Specifies which object properties to include in the response
/// Depth of descendants to fetch. If -1, fetches all descendants.
/// Function accepting a list of WwiseObjectInfo.
public static void GetWwiseObjectAndDescendants(string identifier, ReturnOptions options, int depth, GetResultListDelegate callback)
{
WaqlArgs args;
if (depth > 0)
{
string selectString = System.String.Join(" ", ArrayList.Repeat(" select this, children", depth).ToArray());
args = new WaqlArgs($"from object \"{identifier}\" {selectString} orderby path");
}
else
{
args = new WaqlArgs($"from object \"{identifier}\" select descendants orderby path");
}
QueueCommandWithReturnWwiseObjects(args, options, callback);
}
///
/// Composes a WAQL "search" request based on the parameters and enqueues a WAAPI command.
/// Passes the list of WwiseObjectInfo containing the search results to the callback
///
/// Characters to search for.
/// Specifies which object properties to include in the response
/// An optional object type used to filter search results.
/// Function accepting a list of WwiseObjectInfo.
public static void Search(string searchString, WwiseObjectType objectType, ReturnOptions options, GetResultListDelegate callback)
{
WaqlArgs args;
if (objectType == WwiseObjectType.None)
{
args = new WaqlArgs($"from search \"{searchString}\" orderby path");
}
else
{
args = new WaqlArgs($"from search \"{searchString}\" where type=\"{WaapiKeywords.WwiseObjectTypeStrings[objectType]}\" orderby path");
}
QueueCommandWithReturnWwiseObjects(args, options, callback);
}
///
/// Get the children of a given object.
///
/// GUID of the target object.
/// Specifies which object properties to include in the response
/// Function accepting a list of WwiseObjectInfo.
public static void GetChildren(System.Guid guid, ReturnOptions options, GetResultListDelegate callback)
{
if (guid == System.Guid.Empty)
return;
var args = new WaqlArgs($"from object \"{guid:B}\" select children orderby path");
QueueCommandWithReturnWwiseObjects(args, options, callback);
}
///
/// Get the WwiseObjectInfo for the project.
///
/// Function accepting a list of WwiseObjectInfo. The first element of the list will be the project info.
/// Specifies which object properties to include in the response
public static void GetProject(GetResultListDelegate callback, ReturnOptions options)
{
var args = new WaqlArgs($"from type {WaapiKeywords.PROJECT}");
QueueCommandWithReturnWwiseObjects(args, options, callback);
}
///
/// Parse the response WwiseObjectInfoJsonObject of a "from object" request and implicit cast the objects to WwiseObjectInfo.
///
///
///
public static List ParseObjectInfo(List returnObjects)
{
var returnInfo = new List(returnObjects.Count);
foreach (var info in returnObjects)
{
returnInfo.Add(info);
}
return returnInfo;
}
///
/// Select the object in Wwise Authoring.
/// Creates a WaapiCommand object containing a lambda call to SelectObjectInAuthoringAsync and adds it to the waapiCommandQueue.
///
/// GUID of the object to be selected.
public static void SelectObjectInAuthoring(System.Guid guid)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await SelectObjectInAuthoringAsync(guid)));
}
///
/// Creates and sends a WAAPI command to select a Wwise object.
///
/// GUID of the object to be selected.
///
static private async Task SelectObjectInAuthoringAsync(System.Guid guid)
{
if (guid == System.Guid.Empty) return;
var args = new ArgsCommand(WaapiKeywords.FIND_IN_PROJECT_EXPLORER, new string[] { guid.ToString("B") });
await WaapiClient.Call(ak.wwise.ui.commands.execute, args, null);
}
///
/// Open the OS file browser to the folder containing this object's Work Unit.
/// Creates a WaapiCommand object containing a lambda call to OpenWorkUnitInExplorerAsync and adds it to the waapiCommandQueue.
///
/// GUID of the object to be found.
public static void OpenWorkUnitInExplorer(System.Guid guid)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await OpenWorkUnitInExplorerAsync(guid)));
}
///
/// Open the OS file browser to the folder containing the generated SoundBank.
/// Creates a WaapiCommand object containing a lambda call to OpenSoundBankInExplorer and adds it to the waapiCommandQueue.
///
/// GUID of the SoundBank to be found.
public static void OpenSoundBankInExplorer(System.Guid guid)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await OpenSoundBankInExplorerAsync(guid)));
}
///
/// Uses a waapi call to get the object's file path, then opens the containing folder in the system's file browser.
///
/// GUID of the object to be found.
/// Awaitable Task.
private static async Task OpenWorkUnitInExplorerAsync(System.Guid guid)
{
var args = new WaqlArgs($"from object \"{guid:B}\"");
var options = new ReturnOptions(new string[] { "filePath" });
var result = await WaapiClient.Call(ak.wwise.core.@object.get, args, options);
var ret = UnityEngine.JsonUtility.FromJson(result);
var filePath = ret.@return[0].filePath;
filePath = filePath.Replace("\\", "/");
#if UNITY_EDITOR_OSX
filePath = AkUtilities.ParseOsxPathFromWinePath(filePath);
#endif
UnityEditor.EditorUtility.RevealInFinder(filePath);
}
///
/// Uses a waapi call to get the SoundBank's generated bank path, then opens the containing folder in the system's file browser.
///
/// GUID of the object to be found.
/// Awaitable Task.
private static async Task OpenSoundBankInExplorerAsync(System.Guid guid)
{
var args = new WaqlArgs($"from object \"{guid:B}\"");
var options = new ReturnOptions(new string[] { "soundbankBnkFilePath" });
var result = await WaapiClient.Call(ak.wwise.core.@object.get, args, options);
var ret = UnityEngine.JsonUtility.FromJson(result);
var filePath = ret.@return[0].soundbankBnkFilePath;
#if UNITY_EDITOR_OSX
filePath = AkUtilities.ParseOsxPathFromWinePath(filePath);
#endif
UnityEditor.EditorUtility.RevealInFinder(filePath);
}
///
/// Rename an object in Wwise authoring.
/// Creates a WaapiCommand object containing a lambda call to RenameAsync and adds it to the waapiCommandQueue.
///
/// GUID of the object to be renamed.
/// New name for the wwise object.
public static void Rename(System.Guid guid, string newName)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await RenameAsync(guid, newName)
));
}
///
/// Sends a WAAPI command to rename a Wwise object.
///
/// GUID of the object to be renamed.
/// New name for the wwise object.
/// Awaitable Task.
private static async Task RenameAsync(System.Guid guid, string newName)
{
var args = new ArgsRename(guid.ToString("B"), newName);
await WaapiClient.Call(ak.wwise.core.@object.setName, args, null);
}
///
/// Delete an object in wwise authoring. Work Units cannot be deleted in this manner.
/// Creates a WaapiCommand object containing a lambda call to DeleteAsync and adds it to the waapiCommandQueue.
///
/// GUID of the object to be deleted.
public static void Delete(System.Guid guid)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await DeleteAsync(guid)
));
}
///
/// Sends three WAAPI commands:
/// 1. Begin an undo group.
/// 2. Delete the specified object.
/// 3. Close the undo group.
///
/// GUID of the object to be deleted.
/// Awaitable Task.
private static async Task DeleteAsync(System.Guid guid)
{
await WaapiClient.Call(ak.wwise.core.undo.beginGroup);
await WaapiClient.Call(ak.wwise.core.@object.delete, new ArgsObject(guid.ToString("b")));
await WaapiClient.Call(ak.wwise.core.undo.endGroup, new ArgsDisplayName(WaapiKeywords.DELETE_ITEMS));
}
///
/// Checks if Wwise object is playable.
///
/// WwiseObjectType of object to check.
/// True if playable
public static bool IsPlayable(WwiseObjectType type)
{
return (type == WwiseObjectType.Event);
}
///
/// Play or pause an object in Wwise authoring.
/// Creates a WaapiCommand object containing a lambda call to TogglePlayEventAsync and adds it to the waapiCommandQueue.
///
/// Used to check whether the object is playable.
/// GUID of the object to be played.
static public void TogglePlayEvent(WwiseObjectType objectType, System.Guid guid)
{
if (IsPlayable(objectType))
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await TogglePlayEventAsync(guid)));
}
}
///
/// Play or pause an object in Wwise authoring. Opens a new transport in wwise to play the sound if it does not exist yet.
///
/// GUID of the object to be played.
///
static async private Task TogglePlayEventAsync(System.Guid guid)
{
var transportID = await GetTransport(guid);
var args = new ArgsPlay(WaapiKeywords.PLAYSTOP, transportID);
var result = await WaapiClient.Call(ak.wwise.core.transport.executeAction, args, null);
}
///
/// Find the open transport in ItemTransports or create a new one.
///
/// GUID of the object.
///
static async private Task GetTransport(System.Guid guid)
{
TransportInfo transportInfo;
if (!ItemTransports.TryGetValue(guid, out transportInfo))
{
transportInfo = await CreateTransport(guid);
}
return transportInfo.TransportID;
}
///
/// Send a WAAPI call to create a transport in Wwise.
/// Subscribe to the ak.wwise.core.transport.stateChanged topic of the new transport.
/// Add the transport info to ItemTransports.
///
/// GUID of the Event
///
static async private Task CreateTransport(System.Guid guid)
{
var args = new ArgsObject(guid.ToString("B"));
var result = await WaapiClient.Call(ak.wwise.core.transport.create, args, null, timeout: 1000);
int transportID = UnityEngine.JsonUtility.FromJson(result).transport;
var options = new TransportOptions(transportID);
uint subscriptionID = await WaapiClient.Subscribe(ak.wwise.core.transport.stateChanged, options, HandleTransportStateChanged);
var transport = new TransportInfo(transportID, subscriptionID);
ItemTransports.Add(guid, transport);
return transport;
}
///
/// Handle the messages published by a transport when its state is changed.
/// If stopped, enqueue a command with DestroyTransport as its payload.
///
///
static private void HandleTransportStateChanged(string message)
{
TransportState transport = UnityEngine.JsonUtility.FromJson(message);
System.Guid itemID = new System.Guid(transport.@object);
int transportID = transport.transport;
if (transport.state == WaapiKeywords.STOPPED)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await DestroyTransport(itemID)));
}
else if (transport.state == WaapiKeywords.PLAYING && !ItemTransports.ContainsKey(itemID))
{
ItemTransports.Add(itemID, new TransportInfo(transportID, 0));
}
}
///
/// Send a WAAPI command to stop the specific transport.
///
/// ID of the transport.
///
static private async Task StopTransport(int in_transportID)
{
var args = new ArgsPlay(WaapiKeywords.STOP, in_transportID);
var result = await WaapiClient.Call(ak.wwise.core.transport.executeAction, args, null);
}
///
/// Unsubscribe from the transport topic and send a WAAPI command to destroy the transport in Wwise.
///
/// GUID of the Event.
///
static async Task DestroyTransport(System.Guid in_itemID)
{
if (!ItemTransports.ContainsKey(in_itemID))
return null;
if (ItemTransports[in_itemID].SubscriptionID != 0)
await WaapiClient.Unsubscribe(ItemTransports[in_itemID].SubscriptionID);
var args = new ArgsTransport(ItemTransports[in_itemID].TransportID);
var result = await WaapiClient.Call(ak.wwise.core.transport.destroy, args, null);
ItemTransports.Remove(in_itemID);
return result;
}
///
/// Stops all playing transports.
/// Creates a WaapiCommand object containing a lambda call to StopAllTransportsAsync and adds it to the waapiCommandQueue.
///
static public void StopAllTransports()
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await StopAllTransportsAsync()));
}
///
/// Stops all playing transports.
///
/// Awaitable task.
static private async Task StopAllTransportsAsync()
{
foreach (var item in ItemTransports)
{
await StopTransport(item.Value.TransportID);
}
}
///
/// Subscribe to WAAPI topic. Refer to WAAPI reference documentation for a list of topics and their options.
/// Creates a WaapiCommand object containing a lambda call to SubscribeAsync and adds it to the waapiCommandQueue.
///
/// The topic URI to subscribe to.
/// Delegate function to call when the topic is published.
/// Action to be executed once the subscription has been made.
/// This should store the subscription ID so that the subscription can be cleaned up when it is no longer needed.
public static void Subscribe(string topic, Wamp.PublishHandler subscriptionCallback, System.Action handshakeCallback)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => handshakeCallback(await SubscribeAsync(new SubscriptionInfo(topic, subscriptionCallback)))));
}
///
/// Subscribe to WAAPI topic. Refer to WAAPI reference documentation for a list of topics and their options.
/// Creates and sends a WAAPI command to subscribe to the topic.
///
/// SubscriptionInfo object containing the topic URI and the message handling callback.
/// Updated SubscriptionInfo object containing the subscription ID (uint).
private static async Task SubscribeAsync(SubscriptionInfo subscription)
{
var options = new ReturnOptions(new string[] { "id", "parent", "name", "type", "childrenCount", "path", "workunitType" });
uint id = await WaapiClient.Subscribe(subscription.Uri, options, subscription.Callback);
subscription.SubscriptionId = id;
return subscription;
}
///
/// Unsubscribe from an existing subscription.
/// Creates a WaapiCommand object containing a lambda call to UnsubscribeAsync and adds it to the waapiCommandQueue.
///
/// The subscription ID received from the initial subscription.
public static void Unsubscribe(uint id)
{
waapiCommandQueue.Enqueue(new WaapiCommand(
async () => await UnsubscribeAsync(id)));
}
///
/// Unsubscribe from a subscription.
///
/// The subscription ID received from the initial subscription.
/// Awaitable Task.
private static async Task UnsubscribeAsync(uint id)
{
await WaapiClient.Unsubscribe(id);
}
///
/// Deserializes the objects published by the ak.wwise.ui.selectionChanged topic.
///
/// Json string containing the message.
/// List of WwiseObjectInfo objects.
public static List ParseSelectedObjects(string json)
{
var info = UnityEngine.JsonUtility.FromJson(json);
var ret = new List();
foreach (var child in info.objects)
{
ret.Add(child);
}
return ret;
}
///
/// Deserializes the object published by the ak.wwise.core.object.nameChanged topic.
///
/// Json string containing the message.
/// WwiseRenameInfo containing the object information and the new name.
public static WwiseRenameInfo ParseRenameObject(string json)
{
var info = UnityEngine.JsonUtility.FromJson(json);
info.ParseInfo();
return info;
}
///
/// Deserializes the object published by the ak.wwise.core.object.childAdded or ak.wwise.core.object.childRemoved topic.
///
/// Json string containing the message.
/// WwiseChildModifiedInfo containing parent and child object information.
public static WwiseChildModifiedInfo ParseChildAddedOrRemoved(string json)
{
var info = UnityEngine.JsonUtility.FromJson(json);
info.ParseInfo();
return info;
}
}
#endif