Guest User

Untitled

a guest
Aug 28th, 2020
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 36.55 KB | None | 0 0
  1. [QUOTE]using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Net.Http.Headers;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using JetBrains.Annotations;
  11. using Unity.Cloud.Collaborate.Assets;
  12. using Unity.Cloud.Collaborate.Models.Api;
  13. using Unity.Cloud.Collaborate.Models.Enums;
  14. using Unity.Cloud.Collaborate.Models.Structures;
  15. using Unity.Cloud.Collaborate.Utilities;
  16. using UnityEditor;
  17. using UnityEditor.Collaboration;
  18. using UnityEditor.Connect;
  19. using UnityEditorInternal;
  20. using UnityEngine;
  21. using UnityEngine.Assertions;
  22. using static UnityEditor.Collaboration.Collab;
  23. using ProgressInfo = UnityEditor.Collaboration.ProgressInfo;
  24.  
  25. namespace Unity.Cloud.Collaborate.Models.Providers
  26. {
  27. internal class Collab : ISourceControlProvider
  28. {
  29. const string k_KServiceUrl = "developer.cloud.unity3d.com";
  30.  
  31. readonly RevisionsService m_RevisionsService;
  32.  
  33. /// <inheritdoc />
  34. public event Action UpdatedChangeList;
  35.  
  36. /// <inheritdoc />
  37. public event Action<IReadOnlyList<string>> UpdatedSelectedChangeList;
  38.  
  39. /// <inheritdoc />
  40. public event Action<bool> UpdatedConflictState;
  41.  
  42. /// <inheritdoc />
  43. public event Action<bool> UpdatedRemoteRevisionsAvailability;
  44.  
  45. /// <inheritdoc />
  46. public event Action<ProjectStatus> UpdatedProjectStatus;
  47.  
  48. /// <inheritdoc />
  49. public event Action<bool> UpdatedOperationStatus;
  50.  
  51. /// <inheritdoc />
  52. public event Action<IProgressInfo> UpdatedOperationProgress;
  53.  
  54. /// <inheritdoc />
  55. public event Action<IErrorInfo> ErrorOccurred;
  56.  
  57. /// <inheritdoc />
  58. public event Action ErrorCleared;
  59.  
  60. readonly List<IChangeEntry> m_Changes;
  61. bool m_ConflictCachedState;
  62. bool m_RemoteRevisionsAvailableState;
  63.  
  64. // History entry requesting bits and bobs.
  65. readonly Queue<(int offset, int size, Action<int?, IReadOnlyList<IHistoryEntry>>)> m_HistoryRequests;
  66. [NotNull]
  67. IReadOnlyList<IHistoryEntry> m_HistoryEntries;
  68. (int offset, int size)? m_HistoryEntriesCache;
  69. [CanBeNull]
  70. IHistoryEntry m_HistoryEntryCache;
  71. int? m_HistoryEntryCountCache;
  72. string m_TipCache;
  73.  
  74. [CanBeNull]
  75. IErrorInfo m_ErrorInfo;
  76. [CanBeNull]
  77. IProgressInfo m_ProgressInfo;
  78.  
  79. ProjectStatus m_ProjectStatus;
  80.  
  81. public Collab()
  82. {
  83. m_RevisionsService = new RevisionsService(instance, UnityConnect.instance);
  84. m_Changes = new List<IChangeEntry>();
  85. m_HistoryEntries = new List<IHistoryEntry>();
  86. m_HistoryRequests = new Queue<(int offset, int size, Action<int?, IReadOnlyList<IHistoryEntry>>)>();
  87.  
  88. // Get initial values.
  89. var info = instance.collabInfo;
  90. m_ConflictCachedState = info.conflict;
  91. m_RemoteRevisionsAvailableState = info.update;
  92. m_TipCache = info.tip;
  93. m_ProgressInfo = info.inProgress ? ProgressInfoFromCollab(instance.GetJobProgress(0)) : null;
  94. m_ErrorInfo = instance.GetError(UnityConnect.UnityErrorFilter.ByContext | UnityConnect.UnityErrorFilter.ByChild, out var errInfo)
  95. ? ErrorInfoFromUnity(errInfo)
  96. : null;
  97. m_ProjectStatus = GetNewProjectStatus(info, UnityConnect.instance.connectInfo, UnityConnect.instance.projectInfo);
  98.  
  99. SetupEvents();
  100. }
  101.  
  102. /// <summary>
  103. /// Setup events for the provider.
  104. /// </summary>
  105. void SetupEvents()
  106. {
  107. // just connect notifier events.
  108. instance.ChangeItemsChanged += OnChangeItemsChanged;
  109. instance.SelectedChangeItemsChanged += OnSelectedChangeItemsChanged;
  110. instance.RevisionUpdated_V2 += OnRevisionUpdated;
  111.  
  112. instance.CollabInfoChanged += OnCollabInfoChanged;
  113. instance.JobsCompleted += OnJobsCompleted;
  114. instance.ErrorOccurred_V2 += OnErrorOccurred;
  115. instance.ErrorCleared += OnErrorCleared;
  116.  
  117. instance.StateChanged += OnCollabStateChanged;
  118. UnityConnect.instance.StateChanged += OnUnityConnectStateChanged;
  119. UnityConnect.instance.ProjectStateChanged += OnUnityConnectProjectStateChanged;
  120.  
  121. m_RevisionsService.FetchRevisionsCallback += OnReceiveHistoryEntries;
  122. }
  123.  
  124. #region Callback & Helper Methods
  125.  
  126. /// <summary>
  127. /// Event handler for when the change list has changed.
  128. /// </summary>
  129. /// <param name="changes">New change list.</param>
  130. /// <param name="isFiltered">Whether or not the list is filtered. Should always be false.</param>
  131. void OnChangeItemsChanged(ChangeItem[] changes, bool isFiltered)
  132. {
  133. UpdateChanges(changes);
  134. UpdatedChangeList?.Invoke();
  135. }
  136.  
  137. /// <summary>
  138. /// WIP method to handle partial publish in collab.
  139. /// </summary>
  140. /// <param name="changes">Received changes.</param>
  141. /// <param name="isFiltered">Whether or not it's a partial publish. Should always be true.</param>
  142. void OnSelectedChangeItemsChanged(ChangeItem[] changes, bool isFiltered)
  143. {
  144. // This is used by selective commit. Assert all API calls to here are setting isFiltered to true !
  145. Debug.Assert(isFiltered);
  146. var selectedChanges = changes.Select(e => e.Path).ToList();
  147. UpdatedSelectedChangeList?.Invoke(selectedChanges);
  148. }
  149.  
  150. /// <summary>
  151. /// Event handler for when a revision has been created or updated. It's not called 100% of the time when a user
  152. /// publishes a new revision.
  153. /// </summary>
  154. /// <param name="info">New collab info.</param>
  155. /// <param name="rev">New revision id.</param>
  156. /// <param name="action">Action that occured.</param>
  157. void OnRevisionUpdated(CollabInfo info, string rev, string action)
  158. {
  159. // Invalidate the cache.
  160. m_HistoryEntriesCache = null;
  161. m_HistoryEntryCache = null;
  162. m_HistoryEntryCountCache = null;
  163. // Send update event.
  164. UpdatedHistoryEntries?.Invoke();
  165.  
  166. OnCollabInfoChanged(info);
  167. }
  168.  
  169. void OnCollabInfoChanged(CollabInfo info)
  170. {
  171. // Update conflict state.
  172. if (m_ConflictCachedState != info.conflict)
  173. {
  174. m_ConflictCachedState = info.conflict;
  175. UpdatedConflictState?.Invoke(info.conflict);
  176. }
  177.  
  178. // Update revisions available state.
  179. if (m_RemoteRevisionsAvailableState != info.update)
  180. {
  181. m_RemoteRevisionsAvailableState = info.update;
  182. UpdatedRemoteRevisionsAvailability?.Invoke(info.update);
  183. }
  184.  
  185. // Update history list if the tip has changed.
  186. if (m_TipCache != info.tip)
  187. {
  188. m_TipCache = info.tip;
  189.  
  190. // Invalidate the cache.
  191. m_HistoryEntriesCache = null;
  192. m_HistoryEntryCache = null;
  193. m_HistoryEntryCountCache = null;
  194.  
  195. // Send update event.
  196. UpdatedHistoryEntries?.Invoke();
  197. }
  198.  
  199. // Update project state
  200. UpdateProjectStatus(info, UnityConnect.instance.connectInfo, UnityConnect.instance.projectInfo);
  201.  
  202. // Update progress state.
  203. if (info.inProgress)
  204. {
  205. // Get progress info.
  206. var progressInfo = instance.GetJobProgress(0);
  207. Assert.IsNotNull(progressInfo);
  208.  
  209. // Trigger start operation if not already known.
  210. if (m_ProgressInfo == null)
  211. {
  212. UpdatedOperationStatus?.Invoke(true);
  213. }
  214.  
  215. // Send progress info.
  216. m_ProgressInfo = ProgressInfoFromCollab(progressInfo);
  217. UpdatedOperationProgress?.Invoke(m_ProgressInfo);
  218. }
  219. else if (m_ProgressInfo != null)
  220. {
  221. // Signal end of job if job still exists
  222. m_ProgressInfo = null;
  223. UpdatedOperationStatus?.Invoke(false);
  224. }
  225. }
  226.  
  227. void OnJobsCompleted(CollabInfo info)
  228. {
  229. // NOTE: The first start of collab sends a completion event with no prior progress info.
  230. // To handle this, skip sending completion event if there has been no start event.
  231. if (m_ProgressInfo == null) return;
  232.  
  233. Assert.IsFalse(info.inProgress);
  234. m_ProgressInfo = null;
  235. UpdatedOperationStatus?.Invoke(false);
  236. }
  237.  
  238. void OnErrorOccurred(UnityErrorInfo error)
  239. {
  240. if (m_ErrorInfo?.Code == error.code) return;
  241. m_ErrorInfo = ErrorInfoFromUnity(error);
  242. ErrorOccurred?.Invoke(m_ErrorInfo);
  243. }
  244.  
  245. void OnErrorCleared()
  246. {
  247. m_ErrorInfo = null;
  248. ErrorCleared?.Invoke();
  249. }
  250.  
  251. /// <summary>
  252. /// On receiving history result, remove the oldest request, send the received data, then make the next request.
  253. /// </summary>
  254. /// <param name="revisionsResult">Result from the history request.</param>
  255. void OnReceiveHistoryEntries(RevisionsResult revisionsResult)
  256. {
  257. Assert.AreNotEqual(0, m_HistoryRequests.Count, "There should be a history request.");
  258. var (offset, size, callback) = m_HistoryRequests.Dequeue();
  259.  
  260. // Get results, cache, then send them.
  261. var results = revisionsResult?.Revisions.Select(RevisionToHistoryEntry).ToList();
  262. if (results != null)
  263. {
  264. m_HistoryEntries = results;
  265. m_HistoryEntriesCache = (offset, size);
  266. m_HistoryEntryCountCache = revisionsResult.RevisionsInRepo;
  267. callback(revisionsResult.RevisionsInRepo, m_HistoryEntries);
  268. }
  269.  
  270. // Start the next request --> has to be outside of the callback.
  271. EditorApplication.delayCall += () => ConsumeHistoryQueue();
  272. }
  273.  
  274. /// <summary>
  275. /// Event handler for receiving unity connect project state changes.
  276. /// </summary>
  277. /// <param name="projectInfo">New project info.</param>
  278. void OnUnityConnectProjectStateChanged(ProjectInfo projectInfo)
  279. {
  280. UpdateProjectStatus(instance.collabInfo, UnityConnect.instance.connectInfo, projectInfo);
  281. }
  282.  
  283. /// <summary>
  284. /// Event handler for receiving collab state changes.
  285. /// </summary>
  286. /// <param name="info">New collab state.</param>
  287. void OnCollabStateChanged(CollabInfo info)
  288. {
  289. OnCollabInfoChanged(info);
  290. }
  291.  
  292. /// <summary>
  293. /// Event handler for receiving collab state changes.
  294. /// </summary>
  295. /// <param name="connectInfo">UnityConnect connect info.</param>
  296. void OnUnityConnectStateChanged(ConnectInfo connectInfo)
  297. {
  298. UpdateProjectStatus(instance.collabInfo, connectInfo, UnityConnect.instance.projectInfo);
  299. }
  300.  
  301. /// <summary>
  302. /// Update cached ready value and send event if it has changed.
  303. /// </summary>
  304. void UpdateProjectStatus(CollabInfo collabInfo, ConnectInfo connectInfo, ProjectInfo projectInfo)
  305. {
  306. var currentStatus = GetNewProjectStatus(collabInfo, connectInfo, projectInfo);
  307. if (m_ProjectStatus == currentStatus) return;
  308. m_ProjectStatus = currentStatus;
  309. UpdatedProjectStatus?.Invoke(m_ProjectStatus);
  310. }
  311.  
  312. /// <summary>
  313. /// Returns the current project status.
  314. /// </summary>
  315. /// <returns>Current status of this project.</returns>
  316. static ProjectStatus GetNewProjectStatus(CollabInfo collabInfo, ConnectInfo connectInfo, ProjectInfo projectInfo)
  317. {
  318. // No UPID.
  319. if (!projectInfo.projectBound)
  320. {
  321. return ProjectStatus.Unbound;
  322. }
  323.  
  324. if (!connectInfo.online)
  325. {
  326. return ProjectStatus.Offline;
  327. }
  328.  
  329. if (connectInfo.maintenance || collabInfo.maintenance)
  330. {
  331. return ProjectStatus.Maintenance;
  332. }
  333.  
  334. if (!connectInfo.loggedIn)
  335. {
  336. return ProjectStatus.LoggedOut;
  337. }
  338.  
  339. if (!collabInfo.seat)
  340. {
  341. return ProjectStatus.NoSeat;
  342. }
  343.  
  344. // UPID exists, but collab off.
  345. if (!instance.IsCollabEnabledForCurrentProject())
  346. {
  347. return ProjectStatus.Bound;
  348. }
  349.  
  350. // Waiting for collab to connect and be ready.
  351. if (!instance.IsConnected() || !collabInfo.ready)
  352. {
  353. return ProjectStatus.Loading;
  354. }
  355.  
  356. return ProjectStatus.Ready;
  357. }
  358.  
  359. /// <summary>
  360. /// Consume the next entry on the history queue.
  361. /// </summary>
  362. /// <param name="afterEnqueue">True if an entry was just inserted. Starts the consumption cycle.</param>
  363. void ConsumeHistoryQueue(bool afterEnqueue = false)
  364. {
  365. // Start consuming the queue if the first entry was just enqueued.
  366. if (afterEnqueue && m_HistoryRequests.Count != 1) return;
  367.  
  368. // Can't consume an empty queue.
  369. if (m_HistoryRequests.Count == 0) return;
  370.  
  371. var (offset, size, callback) = m_HistoryRequests.Peek();
  372. // Execute next request. Discard if exception.
  373. try
  374. {
  375. m_RevisionsService.GetRevisions(offset, size);
  376. }
  377. catch (Exception e)
  378. {
  379. Debug.LogException(e);
  380. // Remove request and send failure callback.
  381. m_HistoryRequests.Dequeue();
  382. callback(null, null);
  383. }
  384. }
  385.  
  386. /// <summary>
  387. /// Make a history request.
  388. /// </summary>
  389. /// <param name="offset">Offset for the request to start from.</param>
  390. /// <param name="size">Target length of the resultant list.</param>
  391. /// <param name="callback">Callback for the result.</param>
  392. void QueueHistoryRequest(int offset, int size, Action<int?, IReadOnlyList<IHistoryEntry>> callback)
  393. {
  394. m_HistoryRequests.Enqueue((offset, size, callback));
  395. ConsumeHistoryQueue(true);
  396. }
  397.  
  398. /// <summary>
  399. /// Update cache of converted change entries from provided collab changes.
  400. /// </summary>
  401. /// <param name="changes">Received list of changes from collab.</param>
  402. void UpdateChanges(IEnumerable<Change> changes)
  403. {
  404. m_Changes.Clear();
  405. m_Changes.AddRange(changes.Select(change =>
  406. new ChangeEntry(change.path, change.path, ChangeEntryStatusFromCollabState(change.state),
  407. false, IsCollabStateFlagSet(change.state, CollabStates.kCollabConflicted | CollabStates.kCollabPendingMerge), change))
  408. .Cast<IChangeEntry>());
  409. }
  410.  
  411. /// <summary>
  412. /// Update cache of converted change entries from provided collab changes.
  413. /// </summary>
  414. /// <param name="changes">Received list of changes from collab.</param>
  415. void UpdateChanges(IEnumerable<ChangeItem> changes)
  416. {
  417. m_Changes.Clear();
  418. m_Changes.AddRange(changes.Select(change =>
  419. new ChangeEntry(change.Path, change.Path, ChangeEntryStatusFromCollabState(change.State),
  420. false, IsCollabStateFlagSet(change.State, CollabStates.kCollabConflicted | CollabStates.kCollabPendingMerge), change))
  421. .Cast<IChangeEntry>());
  422. }
  423.  
  424. /// <inheritdoc />
  425. public bool GetRemoteRevisionAvailability()
  426. {
  427. // Return cached value.
  428. return m_RemoteRevisionsAvailableState;
  429. }
  430.  
  431. /// <inheritdoc />
  432. public bool GetConflictedState()
  433. {
  434. // Return cached value.
  435. return m_ConflictCachedState;
  436. }
  437.  
  438. /// <inheritdoc />
  439. public IProgressInfo GetProgressState()
  440. {
  441. // Return cached value.
  442. return m_ProgressInfo;
  443. }
  444.  
  445. /// <inheritdoc />
  446. public IErrorInfo GetErrorState()
  447. {
  448. return m_ErrorInfo;
  449. }
  450.  
  451. /// <inheritdoc />
  452. public virtual ProjectStatus GetProjectStatus()
  453. {
  454. return m_ProjectStatus;
  455. }
  456.  
  457. /// <inheritdoc />
  458. public void RequestChangeList(Action<IReadOnlyList<IChangeEntry>> callback)
  459. {
  460. var changes = instance.GetChangesToPublish_V2().changes;
  461. UpdateChanges(changes);
  462. callback(m_Changes);
  463.  
  464. // Also check for errors.
  465. if (instance.GetError(UnityConnect.UnityErrorFilter.All, out var error) &&
  466. (CollabErrorCode)error.code != CollabErrorCode.Collab_ErrNone)
  467. {
  468. ErrorOccurred?.Invoke(ErrorInfoFromUnity(error));
  469. }
  470. }
  471.  
  472. /// <inheritdoc />
  473. public void RequestPublish(string message, IReadOnlyList<IChangeEntry> changeEntries = null)
  474. {
  475. var changeItems = changeEntries?.Select(EntryToChangeItem).ToArray();
  476.  
  477. instance.PublishAssetsAsync(message, changeItems);
  478.  
  479. ChangeItem EntryToChangeItem(IChangeEntry entry)
  480. {
  481. return entry.Tag as ChangeItem;
  482. }
  483. }
  484.  
  485. #endregion
  486.  
  487. #region SourceControlHistoryCommands
  488.  
  489. /// <inheritdoc />
  490. public event Action UpdatedHistoryEntries;
  491.  
  492. /// <inheritdoc />
  493. public void RequestHistoryEntry(string revisionId, Action<IHistoryEntry> callback)
  494. {
  495. // Return cached entry if possible.
  496. if (m_HistoryEntryCache?.RevisionId == revisionId)
  497. {
  498. callback(m_HistoryEntryCache);
  499. return;
  500. }
  501.  
  502. // Ensure that a cleanup occurs in the case of an exception.
  503. m_RevisionsService.FetchSingleRevisionCallback += OnFetchRevisionCallback;
  504. try
  505. {
  506. m_RevisionsService.GetRevision(revisionId);
  507. }
  508. catch (Exception e)
  509. {
  510. Debug.LogException(e);
  511. m_RevisionsService.FetchSingleRevisionCallback -= OnFetchRevisionCallback;
  512. callback(null);
  513. }
  514.  
  515. void OnFetchRevisionCallback(Revision? revision)
  516. {
  517. m_RevisionsService.laFetchSingleRevisionCallback -= OnFetchRevisionCallback;
  518. // Failing to find the revision can result in a null revision or an empty revisionID.
  519. callback(string.IsNullOrEmpty(revision?.revisionID)
  520. ? null
  521. : RevisionToHistoryEntry(revision.GetValueOrDefault()));
  522. }
  523. }
  524.  
  525. /// <inheritdoc />
  526. public void RequestHistoryPage(int offset, int pageSize, Action<IReadOnlyList<IHistoryEntry>> callback)
  527. {
  528. // Return cached entry is possible.
  529. if (m_HistoryEntriesCache?.offset == offset && m_HistoryEntriesCache?.size == pageSize)
  530. {
  531. callback(m_HistoryEntries);
  532. return;
  533. }
  534.  
  535. // Queue up the request.
  536. QueueHistoryRequest(offset, pageSize, (_, r) => callback(r));
  537. }
  538.  
  539. /// <inheritdoc />
  540. public void RequestHistoryCount(Action<int?> callback)
  541. {
  542. // Return cached value if possible.
  543. if (m_HistoryEntryCountCache != null)
  544. {
  545. callback(m_HistoryEntryCountCache);
  546. return;
  547. }
  548.  
  549. QueueHistoryRequest(0, 0, (c, _) => callback(c));
  550. }
  551.  
  552. /// <inheritdoc />
  553. public void RequestDiscard(IChangeEntry entry)
  554. {
  555. // Collab cannot revert a new file as it has nothing to go back to. So, instead we delete them.
  556. if (entry.Status == ChangeEntryStatus.Added)
  557. {
  558. File.Delete(entry.Path);
  559. // Notify ADB to refresh since a change has been made.
  560. AssetDatabase.Refresh();
  561. }
  562. else
  563. {
  564. instance.RevertFile(entry.Path, true);
  565. }
  566. }
  567.  
  568. /// <inheritdoc />
  569. public void RequestBulkDiscard(IReadOnlyList<IChangeEntry> entries)
  570. {
  571. var revertEntries = new List<ChangeItem>();
  572. var deleteOccured = false;
  573. foreach (var entry in entries)
  574. {
  575. // Collab cannot revert a new file as it has nothing to go back to. So, instead we delete them.
  576. if (entry.Status == ChangeEntryStatus.Added)
  577. {
  578. File.Delete(entry.Path);
  579. deleteOccured = true;
  580. }
  581. else
  582. {
  583. revertEntries.Add((ChangeItem)entry.Tag);
  584. }
  585. }
  586.  
  587. // If a change has been made, notify the ADB to refresh.
  588. if (deleteOccured)
  589. {
  590. AssetDatabase.Refresh();
  591. }
  592.  
  593. instance.RevertFiles(revertEntries.ToArray(), true);
  594. }
  595.  
  596. /// <inheritdoc />
  597. public void RequestDiffChanges(string path)
  598. {
  599. instance.ShowDifferences(path);
  600. }
  601.  
  602. /// <inheritdoc />
  603. public bool SupportsRevert { get; } = false;
  604.  
  605. /// <inheritdoc />
  606. public void RequestRevert(string revisionId, IReadOnlyList<string> files)
  607. {
  608. throw new NotImplementedException();
  609. }
  610.  
  611. /// <inheritdoc />
  612. public void RequestUpdateTo(string revisionId)
  613. {
  614. instance.Update(revisionId, true);
  615. }
  616.  
  617. /// <inheritdoc />
  618. public void RequestRestoreTo(string revisionId)
  619. {
  620. instance.ResyncToRevision(revisionId);
  621. }
  622.  
  623. /// <inheritdoc />
  624. public void RequestGoBackTo(string revisionId)
  625. {
  626. instance.GoBackToRevision(revisionId, false);
  627. }
  628.  
  629. /// <inheritdoc />
  630. public void ClearError()
  631. {
  632. instance.ClearErrors();
  633. }
  634.  
  635. /// <inheritdoc />
  636. public void RequestShowConflictedDifferences(string path)
  637. {
  638. if (UnityEditor.Collaboration.Collab.IsDiffToolsAvailable())
  639. {
  640. instance.ShowConflictDifferences(path);
  641. }
  642. else
  643. {
  644. Debug.Log(StringAssets.noMergeToolIsConfigured);
  645. }
  646. }
  647.  
  648. /// <inheritdoc />
  649. public void RequestChooseMerge(string path)
  650. {
  651. if (UnityEditor.Collaboration.Collab.IsDiffToolsAvailable())
  652. {
  653. instance.LaunchConflictExternalMerge(path);
  654. }
  655. else
  656. {
  657. Debug.Log(StringAssets.noMergeToolIsConfigured);
  658. }
  659. }
  660.  
  661. /// <inheritdoc />
  662. public void RequestChooseMine(string[] paths)
  663. {
  664. instance.SetConflictsResolvedMine(paths);
  665. }
  666.  
  667. /// <inheritdoc />
  668. public void RequestChooseRemote(string[] paths)
  669. {
  670. instance.SetConflictsResolvedTheirs(paths);
  671. }
  672.  
  673. /// <inheritdoc />
  674. public void RequestSync()
  675. {
  676. QueueHistoryRequest(0, 1, Callback);
  677.  
  678. void Callback(int? count, IReadOnlyList<IHistoryEntry> revisions)
  679. {
  680. if (revisions != null && revisions.Count > 0)
  681. {
  682. instance.Update(revisions[0].RevisionId, true);
  683. }
  684. else
  685. {
  686. Debug.LogError("Remote revision id is unknown. Please try again.");
  687. }
  688. }
  689. }
  690.  
  691. /// <inheritdoc />
  692. public void RequestCancelJob()
  693. {
  694. instance.CancelJob(0);
  695. }
  696.  
  697. /// <inheritdoc />
  698. public virtual void ShowServicePage()
  699. {
  700. SettingsService.OpenProjectSettings("Project/Services/Collaborate");
  701. }
  702.  
  703. /// <inheritdoc />
  704. public void ShowLoginPage()
  705. {
  706. UnityConnect.instance.ShowLogin();
  707. }
  708.  
  709. /// <inheritdoc />
  710. public void ShowNoSeatPage()
  711. {
  712. var unityConnect = UnityConnect.instance;
  713. var env = unityConnect.GetEnvironment();
  714. // Map environment to url - prod is special
  715. if (env == "production")
  716. env = "";
  717. else
  718. env += "-";
  719.  
  720. var url = "https://" + env + k_KServiceUrl
  721. + "/orgs/" + unityConnect.GetOrganizationId()
  722. + "/projects/" + unityConnect.GetProjectName()
  723. + "/unity-teams/";
  724. Application.OpenURL(url);
  725. }
  726.  
  727. /// <inheritdoc />
  728. public async void RequestTurnOnService()
  729. {
  730. try
  731. {
  732. await RequestTurnOnServiceInternal();
  733. }
  734. catch (Exception e)
  735. {
  736. Debug.LogException(e);
  737. }
  738. }
  739.  
  740. protected async Task RequestTurnOnServiceInternal()
  741. {
  742. Assert.IsTrue(Threading.IsMainThread, "This must be run on the main thread.");
  743.  
  744. // Fire up the update Genesis service flag request.
  745. var http = new HttpClientHandler { CookieContainer = new CookieContainer() };
  746. var client = new HttpClient(http);
  747.  
  748. var projectGuid = UnityConnect.instance.projectInfo.projectGUID;
  749. var accessToken = UnityConnect.instance.GetAccessToken();
  750. client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
  751. client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  752. client.DefaultRequestHeaders.TryAddWithoutValidation("X-UNITY-VERSION", InternalEditorUtility.GetFullUnityVersion());
  753.  
  754. var fullUrl = $"{UnityConnect.instance.GetConfigurationURL(CloudConfigUrl.CloudCore)}/api/projects/{projectGuid}/service_flags";
  755. const string json = @"{ ""service_flags"": { ""collab"" : true} }";
  756. var content = new StringContent(json, Encoding.UTF8, "application/json");
  757.  
  758. var response = await PutAsync(client, fullUrl, content);
  759.  
  760. // Success.
  761. if (response?.StatusCode == HttpStatusCode.OK)
  762. {
  763. SaveAssets();
  764. TurnOnCollabInternal();
  765. }
  766. // Error.
  767. else if (response?.StatusCode == HttpStatusCode.Forbidden)
  768. {
  769. ShowCredentialsError();
  770. }
  771. else
  772. {
  773. ShowGeneralError();
  774. }
  775. }
  776.  
  777. protected virtual void SaveAssets()
  778. {
  779. instance.SaveAssets();
  780. }
  781.  
  782. protected virtual Task<HttpResponseMessage> PutAsync(HttpClient client, string fullUrl, StringContent content)
  783. {
  784. return client.PutAsync(fullUrl, content);
  785. }
  786.  
  787. protected virtual void TurnOnCollabInternal()
  788. {
  789. // enable the server from the client..
  790. instance.SetCollabEnabledForCurrentProject(true);
  791. // persist by marking collab on in settings
  792. PlayerSettings.SetCloudServiceEnabled("Collab", true);
  793. }
  794.  
  795. protected virtual void ShowCredentialsError()
  796. {
  797. // TODO - ahmad :- Show an Error UI.
  798. Debug.LogError("You need owner privilege to enable or disable collab.");
  799. }
  800.  
  801. protected virtual void ShowGeneralError()
  802. {
  803. // TODO - ahmad :- Show an Error UI.
  804. Debug.LogError("cannot enable collab");
  805. }
  806.  
  807. #endregion
  808.  
  809. #region Static Helper Methods
  810.  
  811. /// <summary>
  812. /// Converts a Collab Revision to an IHistoryEntry.
  813. /// </summary>
  814. /// <param name="revision">Revision to convert.</param>
  815. /// <returns>Resultant IHistoryEntry</returns>
  816. IHistoryEntry RevisionToHistoryEntry(Revision revision)
  817. {
  818. var time = DateTimeOffset.FromUnixTimeSeconds((long)revision.timeStamp);
  819. var entries = revision.entries.Select(ChangeActionToChangeEntry).ToList();
  820. var status = HistoryEntryStatus.Ahead;
  821. if (revision.isObtained)
  822. status = HistoryEntryStatus.Behind;
  823. if (revision.revisionID == m_RevisionsService.tipRevision)
  824. status = HistoryEntryStatus.Current;
  825. return new HistoryEntry(revision.revisionID, status, revision.author, revision.comment, time, entries);
  826. }
  827.  
  828. /// <summary>
  829. /// Converts a Collab ChangeAction to an IChangeEntry.
  830. /// </summary>
  831. /// <param name="action">ChangeAction to convert.</param>
  832. /// <returns>Resultant IChangeEntry</returns>
  833. static IChangeEntry ChangeActionToChangeEntry(ChangeAction action)
  834. {
  835. var unmerged = false;
  836. var status = ChangeEntryStatus.None;
  837. switch (action.action.ToLower())
  838. {
  839. case "added":
  840. status = ChangeEntryStatus.Added;
  841. break;
  842. case "conflict":
  843. status = ChangeEntryStatus.Unmerged;
  844. unmerged = true;
  845. break;
  846. case "deleted":
  847. status = ChangeEntryStatus.Deleted;
  848. break;
  849. case "ignored":
  850. status = ChangeEntryStatus.Ignored;
  851. break;
  852. case "renamed":
  853. case "moved":
  854. status = ChangeEntryStatus.Renamed;
  855. break;
  856. case "updated":
  857. status = ChangeEntryStatus.Modified;
  858. break;
  859. default:
  860. Debug.LogError($"Unknown file status: {action.action}");
  861. break;
  862. }
  863.  
  864. return new ChangeEntry(action.path, status: status, unmerged: unmerged);
  865. }
  866.  
  867. /// <summary>
  868. /// Converts a Collab CollabStates to an ChangeEntryStatus.
  869. /// Note that CollabStates is a bitwise flag, while
  870. /// ChangeEntryStatus is an enum, so ordering matters.
  871. /// </summary>
  872. /// <param name="state">ChangeAction to convert.</param>
  873. /// <returns>Resultant ChangeEntryStatus</returns>
  874. static ChangeEntryStatus ChangeEntryStatusFromCollabState(CollabStates state)
  875. {
  876. if (IsCollabStateFlagSet(state, CollabStates.kCollabIgnored))
  877. {
  878. return ChangeEntryStatus.Ignored;
  879. }
  880. if (IsCollabStateFlagSet(state, CollabStates.kCollabConflicted | CollabStates.kCollabPendingMerge))
  881. {
  882. return ChangeEntryStatus.Unmerged;
  883. }
  884. if (IsCollabStateFlagSet(state, CollabStates.kCollabAddedLocal))
  885. {
  886. return ChangeEntryStatus.Added;
  887. }
  888. if (IsCollabStateFlagSet(state, CollabStates.kCollabMovedLocal))
  889. {
  890. return ChangeEntryStatus.Renamed;
  891. }
  892. if (IsCollabStateFlagSet(state, CollabStates.kCollabDeletedLocal))
  893. {
  894. return ChangeEntryStatus.Deleted;
  895. }
  896. if (IsCollabStateFlagSet(state, CollabStates.kCollabCheckedOutLocal))
  897. {
  898. return ChangeEntryStatus.Modified;
  899. }
  900.  
  901. return ChangeEntryStatus.Unknown;
  902. }
  903.  
  904. /// <summary>
  905. /// Checks the state of a flag in CollabStates.
  906. /// </summary>
  907. /// <param name="state">State to check from.</param>
  908. /// <param name="flag">Flag to check in the state.</param>
  909. /// <returns>True if flag is set.</returns>
  910. static bool IsCollabStateFlagSet(CollabStates state, CollabStates flag)
  911. {
  912. return (state & flag) != 0;
  913. }
  914.  
  915. static IProgressInfo ProgressInfoFromCollab([CanBeNull] ProgressInfo collabProgress)
  916. {
  917. if (collabProgress == null) return null;
  918. return new Structures.ProgressInfo(
  919. collabProgress.title,
  920. collabProgress.extraInfo,
  921. collabProgress.currentCount,
  922. collabProgress.totalCount,
  923. collabProgress.lastErrorString,
  924. collabProgress.lastError,
  925. collabProgress.canCancel,
  926. collabProgress.isProgressTypePercent,
  927. collabProgress.percentComplete);
  928. }
  929.  
  930. static IErrorInfo ErrorInfoFromUnity(UnityErrorInfo error)
  931. {
  932. return new ErrorInfo(
  933. error.code,
  934. error.priority,
  935. error.behaviour,
  936. error.msg,
  937. error.shortMsg,
  938. error.codeStr);
  939. }
  940.  
  941. #endregion
  942.  
  943. enum CollabErrorCode
  944. {
  945. Collab_ErrNone = 0,
  946. Collab_Error,
  947. Collab_ErrProjectNotLinked,
  948. Collab_ErrNoSuchRepository,
  949. Collab_ErrNotLoggedIn,
  950. Collab_ErrNotConnected,
  951. Collab_ErrLocalCache,
  952. Collab_ErrNotUpToDate,
  953. Collab_ErrCannotGetRevision,
  954. Collab_ErrCannotGetRemote,
  955. Collab_ErrCannotGetLocal,
  956. Collab_ErrInvalidHost,
  957. Collab_ErrInvalidPort,
  958. Collab_ErrInvalidRevision,
  959. Collab_ErrNotSnapshot,
  960. Collab_ErrNoSuchRemoteFile,
  961. Collab_ErrNoSuchLocalFile,
  962. Collab_ErrJobNotDefined,
  963. Collab_ErrJobAlreadyRunning,
  964. Collab_ErrAlreadyUpToDate,
  965. Collab_ErrJobNotRunning,
  966. Collab_ErrNotSupported,
  967. Collab_ErrJobCancelled,
  968. Collab_ErrCannotSubmitChanges,
  969. Collab_ErrMD5DoesNotMatch,
  970. Collab_ErrRemoteChanged,
  971. Collab_ErrCannotCreateTempDir,
  972. Collab_ErrCannotDownloadEntry,
  973. Collab_ErrCannotCreatePath,
  974. Collab_ErrCannotCreateFile,
  975. Collab_ErrCannotCopyFile,
  976. Collab_ErrCannotMoveFile,
  977. Collab_ErrCannotDeleteFile,
  978. Collab_ErrCannotGetProjects,
  979. Collab_ErrCannotRestoreSnapshot,
  980. Collab_ErrFileWasAddedLocally,
  981. Collab_ErrFileIsModified,
  982. Collab_ErrFileIsMissing,
  983. Collab_ErrFileAlreadyExists,
  984. Collab_ErrAutomaticMergeBaseIsMissing,
  985. Collab_ErrSmartMergeConflicts,
  986. Collab_ErrTextMergeConflicts,
  987. Collab_ErrAutomaticMerge,
  988. Collab_ErrSmartMerge,
  989. Collab_ErrTextMerge,
  990. Collab_ErrExternalDiff,
  991. Collab_ErrExternalMerge,
  992. Collab_ErrParseJson,
  993. Collab_ErrWrongSerializationMode,
  994. Collab_ErrNoDiffRevisions,
  995. Collab_ErrWorkspaceChanged,
  996. Collab_ErrRefreshChannelAccess,
  997. Collab_ErrUpdateInProgress,
  998. Collab_ErrSoftLocksJobRunning,
  999. Collab_ErrCannotGetSoftLocks,
  1000. Collab_ErrPostSoftLocks,
  1001. Collab_ErrRequestCancelled,
  1002. Collab_ErrCollabInErrorState,
  1003. Collab_ErrUsageExceeded,
  1004. Collab_ErrRepositoryLocked,
  1005. Collab_ErrJobWaitingForSubTasks,
  1006. Collab_ErrBadRequest = 400,
  1007. Collab_ErrNotAuthorized = 401,
  1008. Collab_ErrInternalServerError = 500,
  1009. Collab_ErrBadGateway = 502,
  1010. Collab_ErrServerUnavailable = 503,
  1011. Collab_ErrSmartMergeSetConflictState,
  1012. Collab_ErrTextMergeSetConflictState,
  1013. Collab_ErrExternalMergeSetConflictState,
  1014. Collab_ErrNoDiffMergeToolsConfigured,
  1015. Collab_ErrUnsupportedDiffMergeToolConfigured,
  1016. Collab_ErrNoSeat,
  1017. Collab_ErrNoSeatHidden
  1018. }
  1019. }
  1020. }[/QUOTE]
  1021.  
Advertisement
Add Comment
Please, Sign In to add comment