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;
using log4net;
namespace P4Connect
{
/// <summary>
/// Stores and periodically updates the Perforce status of all the assets
/// </summary>
public class AssetStatusCache
{
private static readonly ILog log = LogManager.GetLogger(typeof(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 GetAssetStatus(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)
{
if (! Config.ValidConfiguration)
return;
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>
/// <param name="aAssetPaths">List of Linux style Project Relative Paths</param>
public static void UpdateAssetData(List<string> aAssetPaths)
{
if (! Config.ValidConfiguration)
return;
// Debug.Log("paths: " + Logger.StringArrayToString(aAssetPaths.ToArray()));
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 CreateAssetStatus(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)
path = Utils.LocalPathToAssetPath(allSpecs[i].LocalPath.Path);
else if (allSpecs[i].ClientPath != null)
path = Utils.LocalPathToAssetPath(Utils.ClientPathToLocalPath(allSpecs[i].ClientPath.Path));
else if (allSpecs[i].DepotPath != null)
path = Utils.LocalPathToAssetPath(Utils.DepotPathToLocalPath(aConnection, allSpecs[i].DepotPath));
#if DEBUG
log.Debug("_Statuses[" + Logger.ToStringNullSafe(path) + "] = " + allStatuses[i].LocalState.ToString());
#endif
if (path != null)
{
_Statuses[GetEffectivePath(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(CreateAssetStatus(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 | |
|---|---|---|---|---|---|
| #1 | 12954 | anis_sg |
Populate -o //guest/perforce_software/p4connect/... //guest/anis_sg/perforce_software/p4connect/.... |
||
| //guest/perforce_software/p4connect/src/P4Connect/P4Connect/P4Connect.AssetStatusCache.cs | |||||
| #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 |
||