using Cysharp.Threading.Tasks; using Speckle.ConnectorUnity.Args; using Speckle.ConnectorUnity.Models; using Speckle.Core.Api; using Speckle.Core.Api.SubscriptionModels; using Speckle.Core.Credentials; using Speckle.Core.Kits; using Speckle.Core.Logging; using Speckle.Core.Models; using UnityEngine; using UnityEngine.Events; namespace Speckle.ConnectorUnity.Ops { /// /// A Speckle Receiver, it's a wrapper around a basic Speckle Client /// that handles conversions and subscriptions for you /// [ExecuteAlways] [AddComponentMenu(SpeckleUnity.NAMESPACE + "/Receiver")] public class Receiver : ClientBehaviour { [SerializeField] ReceiveMode _mode; [SerializeField] bool _autoReceive; [SerializeField] bool _sendReceive; [SerializeField] bool _deleteOld = true; [SerializeField] Texture _preview; [SerializeField] bool _showPreview = true; [SerializeField] bool _renderPreview = true; public Texture preview => _preview; public bool showPreview { get => _showPreview; set => _showPreview = value; } /// /// Gets and converts the data of the last commit on the Stream /// /// public override async UniTask Run() { var args = new ReceiveWorkArgs() { message = "", referenceObj = "", client = this, success = false }; token = this.GetCancellationTokenOnDestroy(); try { progress = 0f; isWorking = true; SpeckleUnity.Console.Log("Receive Started"); if (!IsValid()) { args.message = "Invalid Client"; SpeckleUnity.Console.Warn($"{args.client}-" + args.message); return args; } if (converter == null) { args.message = "No active converter found"; SpeckleUnity.Console.Warn($"{args.client}-" + args.message); await UniTask.Yield(); return args; } _root = new GameObject().AddComponent(); // NOTE: this might now need to happen switch (_stream.type) { case StreamWrapperType.Commit: var c = await _client.CommitGet(_stream.id, _stream.commit.id); // TODO: check if this getting the commit updates the instance if (_sendReceive) _client.CommitReceived(new CommitReceivedInput() { streamId = _stream.id, commitId = c.id, message = "Received Commit from Unity", sourceApplication = SpeckleUnity.APP }).Forget(); _root.source = await _client.ObjectGet(_stream.id, c.referencedObject); break; case StreamWrapperType.Object: _root.source = await _client.ObjectGet(_stream.id, _stream.@object.id); break; case StreamWrapperType.Branch: case StreamWrapperType.Stream: SpeckleUnity.Console.Warn("A commit or object needs to be set in this stream in order to receive something"); break; case StreamWrapperType.Undefined: default: SpeckleUnity.Console.Error("Stream is not properly ready to receive"); break; } if (!_root.IsValid()) { args.message = "The reference object pulled down from this stream is not valid"; SpeckleUnity.Console.Warn($"{args.client}-" + args.message); return args; } Base @base = await SpeckleOps.Receive(_client, _stream.id, _root.id, HandleProgress, HandleError, HandleChildCount); if (@base == null) { args.message = "Data from Commit was not valid"; SpeckleUnity.Console.Warn($"{args.client}-" + args.message); return args; } args.referenceObj = _root.id; // TODO: handle the process for update objects and not just force deleting if (_deleteOld) _root.Purge(); // TODO: Handle separating the operation call from the conversion await _root.DataToScene(@base, converter, token); args.success = true; args.message = $"Completed {nameof(Run)}"; OnNodeComplete?.Invoke(_root); } catch (SpeckleException e) { SpeckleUnity.Console.Warn(e.Message); } finally { progress = 0f; isWorking = false; await UniTask.Yield(); HandleRefresh(); } return args; } protected override void SetSubscriptions() { if (_client != null && _client.IsValid()) { _client.source.SubscribeCommitCreated(stream.id); _client.source.OnCommitCreated += (_, c) => OnCommitCreated?.Invoke(c); _client.source.SubscribeCommitUpdated(stream.id); _client.source.OnCommitUpdated += (_, c) => OnCommitUpdated?.Invoke(c); } } protected override UniTask PostLoadCommit() { UpdatePreview().Forget(); return UniTask.CompletedTask; } async UniTask UpdatePreview() { if (!IsValid()) await UniTask.Yield(); _preview = await SpeckleUnity.GetPreview(_stream.GetUrl(true, account.serverInfo.url)); OnPreviewSet?.Invoke(); await UniTask.Yield(); } #region Events public event UnityAction OnNodeComplete; public event UnityAction OnPreviewSet; #endregion #region Subscriptions public event UnityAction OnCommitCreated; public event UnityAction OnCommitUpdated; #endregion } }