using UnityEditor; using UnityEngine; using System; using System.Threading; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using Perforce.P4; namespace P4Connect { /// <summary> /// Stores and periodically updates the Perforce status of all the assets /// </summary> public class AssetStatusCache { /// <summary> /// Simple asset status struct /// Stores the file state (checked out, modified, etc...) and revision state /// </summary> public struct AssetStatus { public DateTime TimeStamp; public FileState LocalState; public FileState OtherState; public DepotState DepotState; public RevisionState RevisionState; public ResolvedState ResolvedState; public StorageType StorageType; public LockState LockState; } /// <summary> /// This delegate is triggered when the status of an asset changes /// </summary> public delegate void OnAssetStatusChangedDelegate(PerforceConnection aConnection); public static event OnAssetStatusChangedDelegate OnAssetStatusChanged { add { if (_OnAssetStatusChanged == null) { // We had no reason to get updated before, so do it now Engine.OnOperationPerformed += OnEngineOperationPerformed; } _OnAssetStatusChanged += value; } remove { _OnAssetStatusChanged -= value; if (_OnAssetStatusChanged == null) { // We no longer have any reason to be updated Engine.OnOperationPerformed -= OnEngineOperationPerformed; } } } // The internal event that is actually registered with static event OnAssetStatusChangedDelegate _OnAssetStatusChanged; // The cache static Dictionary<string, AssetStatus> _Statuses; public static void Initialize() { _Statuses = new Dictionary<string, AssetStatusCache.AssetStatus>(); } /// <summary> /// Generic method that fetches the cached asset status (checked out, out of date, etc...) /// </summary> public static AssetStatus GetAssetData(string aAssetPath) { AssetStatus ret = new AssetStatus(); if (_Statuses != null) { _Statuses.TryGetValue(GetEffectivePath(aAssetPath), out ret); } return ret; } /// <summary> /// Generic method that fetches the cached asset status (checked out, out of date, etc...) /// Faster on multiple files because it batches the P4 requests /// </summary> public static void GetAssetData(PerforceConnection aConnection, List<string> aAssetPaths, List<AssetStatus> aOutStatuses) { DateTime startTimestamp = DateTime.Now; List<FileSpec> localFiles = GetFileSpecs(aAssetPaths); GetAssetData(aConnection, localFiles, aOutStatuses); if (aAssetPaths.Count == aOutStatuses.Count) { for (int i = 0; i < aAssetPaths.Count; ++i) { _Statuses[GetEffectivePath(aAssetPaths[i])] = aOutStatuses[i]; } } if (Config.DisplayP4Timings) { double deltaInnerTime = (DateTime.Now - startTimestamp).TotalMilliseconds; aConnection.AppendTimingInfo("GetAssetData " + deltaInnerTime.ToString() + " ms for " + aAssetPaths.Count + " files (" + aOutStatuses.Count + " retrieved)"); } } /// <summary> /// Force P4Connect to update the status of a file in its status cache /// </summary> public static void UpdateAssetData(List<string> aAssetPaths) { List<FileSpec> localFiles = GetFileSpecs(aAssetPaths); List<AssetStatus> statuses = new List<AssetStatus>(); Engine.PerformConnectionOperation(con => { GetAssetData(con, localFiles, statuses); }); if (statuses.Count == aAssetPaths.Count) { for (int i = 0; i < aAssetPaths.Count; ++i) { _Statuses[GetEffectivePath(aAssetPaths[i])] = statuses[i]; } } } /// <summary> /// Given the meta data, figures out the P4Connect icon representation for the file /// There is only a subset of all the metadata that P4Connect actually displays /// </summary> static AssetStatus UpdateIconData(FileMetaData aMeta) { AssetStatus retData = new AssetStatus(); // Fill out the struct switch (aMeta.Type.BaseType) { case BaseFileType.Text: retData.StorageType = StorageType.Text; break; case BaseFileType.Binary: retData.StorageType = StorageType.Binary; break; default: retData.StorageType = StorageType.Other; break; } retData.LocalState = Engine.ParseFileAction(aMeta.Action); if (aMeta.OtherActions != null) { retData.OtherState = Engine.ParseFileAction(aMeta.OtherActions[0]); } if (aMeta.HaveRev >= aMeta.HeadRev) retData.RevisionState = RevisionState.HasLatest; else retData.RevisionState = RevisionState.OutOfDate; if (aMeta.Unresolved) retData.ResolvedState = ResolvedState.NeedsResolve; else retData.ResolvedState = ResolvedState.None; if (aMeta.HeadAction == FileAction.MoveDelete || aMeta.HeadAction == FileAction.Delete) retData.DepotState = DepotState.Deleted; else retData.DepotState = DepotState.None; retData.TimeStamp = DateTime.Now; retData.LockState = Engine.GetLockState(aMeta); return retData; } /// <summary> /// This functions gets called when P4Connect does *something* to files /// In this case we update the files status /// </summary> static void OnEngineOperationPerformed(PerforceConnection aConnection, List<FileAndMeta> aFilesAndMetas) { if (aFilesAndMetas.Count > 0) { List<FileSpec> allSpecs = new List<FileSpec>(); foreach (var fileAndMeta in aFilesAndMetas) { if (fileAndMeta.File != null) { allSpecs.Add(fileAndMeta.File); } if (fileAndMeta.Meta != null) { allSpecs.Add(fileAndMeta.Meta); } } List<AssetStatus> allStatuses = new List<AssetStatus>(); GetAssetData(aConnection, allSpecs, allStatuses); // Cache the returned values for (int i = 0; i < allSpecs.Count; ++i) { string path = null; if (allSpecs[i].LocalPath != null) Utils.LocalPathToAssetPath(allSpecs[i].LocalPath.Path); else if (allSpecs[i].ClientPath != null) Utils.LocalPathToAssetPath(Utils.ClientPathToLocalPath(allSpecs[i].ClientPath.Path)); else if (allSpecs[i].DepotPath != null) Utils.LocalPathToAssetPath(Utils.DepotPathToLocalPath(aConnection, allSpecs[i].DepotPath)); if (path != null) { _Statuses[path] = allStatuses[i]; } } if (_OnAssetStatusChanged != null) { _OnAssetStatusChanged(aConnection); } } } static void GetAssetData(PerforceConnection aConnection, List<FileSpec> aFiles, List<AssetStatus> aOutStatuses) { DateTime startTimestamp = DateTime.Now; if (aFiles.Count > 0) { Revision invalid = new Revision(-1); List<FileSpec> validSpecs = new List<FileSpec>(); for (int i = 0; i < aFiles.Count; ++i) { FileSpec spec = aFiles[i]; if (!Version.Equals(spec.Version, invalid)) { validSpecs.Add(spec); } } if (validSpecs.Count != 0) { IList<FileMetaData> allMetas = Engine.GetFileMetaData(aConnection, validSpecs, null); List<FileMetaData> matchingMetas = new List<FileMetaData>(); DateTime matchingMetaStartTime = DateTime.Now; Utils.GetMatchingMetaData(aFiles, allMetas, matchingMetas); if (Config.DisplayP4Timings) { double deltaInnerTime = (DateTime.Now - matchingMetaStartTime).TotalMilliseconds; aConnection.AppendTimingInfo("GetMatchingMetaData " + deltaInnerTime.ToString() + " ms"); } for (int i = 0; i < aFiles.Count; ++i) { if (matchingMetas[i] != null) { aOutStatuses.Add(UpdateIconData(matchingMetas[i])); } else { aOutStatuses.Add(new AssetStatus()); } } } else { for (int i = 0; i < aFiles.Count; ++i) { aOutStatuses.Add(new AssetStatus()); } } } if (Config.DisplayP4Timings) { double deltaInnerTime = (DateTime.Now - startTimestamp).TotalMilliseconds; aConnection.AppendTimingInfo("GetAssetData " + deltaInnerTime.ToString() + " ms"); } } /// <summary> /// Returns a proper list of file spec given a list of paths, accounting for directories /// </summary> static List<FileSpec> GetFileSpecs(List<string> aAssetPaths) { List<FileSpec> localFiles = new List<FileSpec>(); foreach (var path in aAssetPaths) { localFiles.Add(FileSpec.LocalSpec(Utils.AssetPathToLocalPath(GetEffectivePath(path)))); } return localFiles; } /// <summary> /// Returns the path to use when printing the name, or looking up status info /// </summary> static string GetEffectivePath(string aAssetPath) { if (Utils.IsDirectory(aAssetPath)) { return Utils.MetaFromAsset(aAssetPath); } else { return aAssetPath; } } } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#11 | 16210 | Norman Morse | Remove files from old locations | ||
#10 | 15383 | Norman Morse |
Improved Diagnostics, cleaned up unnecessary log output Moved some Dialog Initialization to OnEnable() Fixed Unity 5.1.1 incompatibilities Added Operation support for In Depot Deleted files |
||
#9 | 15244 | Norman Morse |
Better Directory support in "add" "get latest" "refresh" and other commands. Improved Project Root detection Various Bug Fixes and Clean up |
||
#8 | 15079 | Norman Morse |
Rewrote AssetStatusCache to Cache AssetStatuses and FileMetaData Fixed Edge conditions on Engine Operations Change Debug output defaults. Will now Checkout files which request to be "added" but which already exist in perforce. Output P4Connect version to log on initialization. |
||
#7 | 14232 | Norman Morse | GA.8 release | ||
#6 | 14193 | Norman Morse |
GA.7 release Refactor Pending Changes Resolve Submit issues. Fixed Menu entries. Handle mismatched file and meta states. |
||
#5 | 13824 | Norman Morse |
Changes to have fstat return true client paths. Remove versions, fixes problems with "local" paths sneaking into results. |
||
#4 | 12945 | Norman Morse | Changes from internal main GA.1 | ||
#3 | 12565 | Norman Morse |
Integrated from Dev Branch Made ChangeManager into Static Class. Improved close window behavior for when connection is invalid Fixed localOpenFiles not updating on submit |
||
#2 | 12553 | Norman Morse |
integrate from internal main Build fixes for EC. Major changes to Configuration and re-initialization code. Bug fixes |
||
#1 | 10940 | Norman Morse |
Inital Workshop release of P4Connect. Released under BSD-2 license |