Files
speckle-unity/Packages/systems.speckle.speckle-unity/Runtime/Components/ReceiveFromURL.cs
T
Jedd Morgan 9cc1c1f6bf Bump Core Deps to 2.20 (#120)
* Bump Sdk

* Fixed compiler errors
2024-08-08 14:31:07 +01:00

179 lines
5.4 KiB
C#

using System;
using System.Collections;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Speckle.Core.Api;
using Speckle.Core.Api.GraphQL.Models;
using Speckle.Core.Credentials;
using Speckle.Core.Logging;
using Speckle.Core.Models;
using UnityEngine;
namespace Speckle.ConnectorUnity.Components
{
[AddComponentMenu("Speckle/Extras/Receive from URL")]
[RequireComponent(typeof(RecursiveConverter)), ExecuteAlways]
public class ReceiveFromURL : MonoBehaviour
{
[Tooltip("Url of your speckle object/commit/branch/stream")]
public string url;
private RecursiveConverter _converter;
#nullable enable
private CancellationTokenSource? _tokenSource;
void Awake()
{
_converter = GetComponent<RecursiveConverter>();
}
[ContextMenu(nameof(Receive))]
public void Receive()
{
StartCoroutine(Receive_Routine());
}
public IEnumerator Receive_Routine()
{
if (IsBusy())
throw new InvalidOperationException("A receive operation has already started");
_tokenSource = new CancellationTokenSource();
try
{
StreamWrapper sw = new(url);
if (!sw.IsValid)
throw new InvalidOperationException(
"Speckle url input is not a valid speckle stream/branch/commit"
);
var accountTask = new Utils.Utils.WaitForTask<Account>(
async () => await GetAccount(sw),
_tokenSource.Token
);
yield return accountTask;
_tokenSource.Token.ThrowIfCancellationRequested();
using Client c = new(accountTask.Result);
var objectIdTask = new Utils.Utils.WaitForTask<(string, Commit?)>(
async () => await GetObjectID(sw, c),
_tokenSource.Token
);
yield return objectIdTask;
(string objectId, Commit? commit) = objectIdTask.Result;
Debug.Log($"Receiving from {sw.ServerUrl}...");
var receiveTask = new Utils.Utils.WaitForTask<Base>(
async () =>
await SpeckleReceiver.ReceiveAsync(
c,
sw.StreamId,
objectId,
commit,
cancellationToken: _tokenSource.Token
),
_tokenSource.Token
);
yield return receiveTask;
Debug.Log("Converting to native...");
_converter.RecursivelyConvertToNative_Sync(receiveTask.Result, transform);
}
finally
{
_tokenSource.Dispose();
_tokenSource = null;
}
}
private async Task<(string objectId, Commit? commit)> GetObjectID(
StreamWrapper sw,
Client client
)
{
string objectId;
Commit? commit = null;
//OBJECT URL
if (!string.IsNullOrEmpty(sw.ObjectId))
{
objectId = sw.ObjectId;
}
//COMMIT URL
else if (!string.IsNullOrEmpty(sw.CommitId))
{
commit = await client.CommitGet(sw.StreamId, sw.CommitId).ConfigureAwait(false);
objectId = commit.referencedObject;
}
//BRANCH URL OR STREAM URL
else
{
var branchName = string.IsNullOrEmpty(sw.BranchName) ? "main" : sw.BranchName;
var branch = await client
.BranchGet(sw.StreamId, branchName, 1)
.ConfigureAwait(false);
if (!branch.commits.items.Any())
throw new SpeckleException("The selected branch has no commits.");
commit = branch.commits.items[0];
objectId = branch.commits.items[0].referencedObject;
}
return (objectId, commit);
}
[ContextMenu(nameof(Cancel))]
public void Cancel()
{
if (IsNotBusy())
throw new InvalidOperationException(
"There are no pending receive operations to cancel"
);
_tokenSource!.Cancel();
}
[ContextMenu(nameof(Cancel), true)]
public bool IsBusy()
{
return _tokenSource is not null;
}
[ContextMenu(nameof(Receive), true)]
internal bool IsNotBusy() => !IsBusy();
private void OnDisable()
{
_tokenSource?.Cancel();
}
private async Task<Account> GetAccount(StreamWrapper sw)
{
Account account;
try
{
account = await sw.GetAccount().ConfigureAwait(false);
}
catch (SpeckleException)
{
if (string.IsNullOrEmpty(sw.StreamId))
throw;
//Fallback to a non authed account
account = new Account
{
token = "",
serverInfo = new ServerInfo { url = sw.ServerUrl },
userInfo = new UserInfo()
};
}
return account;
}
}
}