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
{
///
/// Stores and periodically updates the Perforce status of all the assets
///
public class AssetStatusCache
{
///
/// Simple asset status struct
/// Stores the file state (checked out, modified, etc...) and revision state
///
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;
}
///
/// This delegate is triggered when the status of an asset changes
///
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 _Statuses;
public static void Initialize()
{
_Statuses = new Dictionary();
}
///
/// Generic method that fetches the cached asset status (checked out, out of date, etc...)
///
public static AssetStatus GetAssetData(string aAssetPath)
{
AssetStatus ret = new AssetStatus();
if (_Statuses != null)
{
_Statuses.TryGetValue(GetEffectivePath(aAssetPath), out ret);
}
return ret;
}
///
/// Generic method that fetches the cached asset status (checked out, out of date, etc...)
/// Faster on multiple files because it batches the P4 requests
///
public static void GetAssetData(PerforceConnection aConnection, List aAssetPaths, List aOutStatuses)
{
if (! Config.ValidConfiguration)
return;
DateTime startTimestamp = DateTime.Now;
List 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)");
}
}
///
/// Force P4Connect to update the status of a file in its status cache
///
public static void UpdateAssetData(List aAssetPaths)
{
if (! Config.ValidConfiguration)
return;
List localFiles = GetFileSpecs(aAssetPaths);
List statuses = new List();
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];
}
}
}
///
/// 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
///
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;
}
///
/// This functions gets called when P4Connect does *something* to files
/// In this case we update the files status
///
static void OnEngineOperationPerformed(PerforceConnection aConnection, List aFilesAndMetas)
{
if (aFilesAndMetas.Count > 0)
{
List allSpecs = new List();
foreach (var fileAndMeta in aFilesAndMetas)
{
if (fileAndMeta.File != null)
{
allSpecs.Add(fileAndMeta.File);
}
if (fileAndMeta.Meta != null)
{
allSpecs.Add(fileAndMeta.Meta);
}
}
List allStatuses = new List();
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 aFiles, List aOutStatuses)
{
DateTime startTimestamp = DateTime.Now;
if (aFiles.Count > 0)
{
Revision invalid = new Revision(-1);
List validSpecs = new List();
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 allMetas = Engine.GetFileMetaData(aConnection, validSpecs, null);
List matchingMetas = new List();
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");
}
}
///
/// Returns a proper list of file spec given a list of paths, accounting for directories
///
static List GetFileSpecs(List aAssetPaths)
{
List localFiles = new List();
foreach (var path in aAssetPaths)
{
localFiles.Add(FileSpec.LocalSpec(Utils.AssetPathToLocalPath(GetEffectivePath(path))));
}
return localFiles;
}
///
/// Returns the path to use when printing the name, or looking up status info
///
static string GetEffectivePath(string aAssetPath)
{
if (Utils.IsDirectory(aAssetPath))
{
return Utils.MetaFromAsset(aAssetPath);
}
else
{
return aAssetPath;
}
}
}
}