using UnityEngine; using UnityEditor; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using Perforce.P4; using log4net; namespace P4Connect { public static class ChangeManager { private static readonly ILog log = LogManager.GetLogger(typeof(ChangeManager)); public static int DEFAULT_CHANGE = -1; static Dictionary _changes = new Dictionary(); public static void Initialize() { } /// /// This callback is working and is called whenever an Operation is completed with a list of the modifed assetPaths /// /// Enumeration of asset paths public static void OnUpdatedFiles(IEnumerable assetPaths) { // ToDo: This routine should check all pending changes to see if they contain any of the assets. log.Debug("OnUpdatedFiles " + Logger.StringEnumerationToString(assetPaths)); HashSet changeContents = ChangeManager.GetHashMap(DEFAULT_CHANGE); changeContents.IntersectWith(assetPaths); foreach (var f in changeContents) { ReplaceChangeMetadata(ChangeManager.DEFAULT_CHANGE, f); } } /// /// Find an existing FileMetaData in an existing Change and Update it. /// /// Change Id of Change to update /// AssetPath static void ReplaceChangeMetadata(int changeId, string path) { PrepareChanges(); var files = _changes[changeId].Files.ToList(); int j = files.FindIndex(e => e.ToAssetPath().Equals(path)); if (j > 0) { files[j] = AssetStatusCache.GetCachedMetaData(path); } else { //Debug.Log("No Match found for " + path + " in change " + changeId); _changes[changeId].Files.Add(AssetStatusCache.GetCachedMetaData(path)); } } /// /// See if this Change has already been retrieved from server, /// If not, retrieve it. /// /// Change ID of changelist /// optional flag to force reinitialization public static void PrepareChange(int changeId, bool force = false) { if (Config.ValidConfiguration) { PrepareChanges(); if (force || !_changes[changeId].Initialized) { using (PerforceConnection con = new PerforceConnection()) { RefreshChange(con, changeId); } } } } /// /// Get a list of ChangeLists /// /// List of Changelists public static IList Changelists { get { PrepareChanges(); return (_changes.Values.ToList()); } } /// /// Get a specific changelist by ID /// /// /// Changelist public static Changelist GetChangelist(int id) { PrepareChanges(); if (_changes.ContainsKey(id)) { return (_changes[id]); } Debug.Log("GetChangelist() can't find " + id.ToString()); return null; } /// /// Get a HashSet containing all paths in the selected change ID /// /// change ID (-1 for DefaultChange) /// A hashset public static HashSet GetHashMap(int id) { var change = GetChangelist(id); if (change != null) { return change.Files.ToAssetPaths().ToHashSet(); } return new HashSet(); } /// /// Return the string associated with a change id /// /// /// string id public static string ChangeIdToString(int id) { if (id <= DEFAULT_CHANGE) return "default"; return id.ToString(); } /// /// Get a list of all local opened files from the DEFAULT_CHANGE /// public static IEnumerable LocalOpenedFiles { get { PrepareChanges(); PrepareChange(DEFAULT_CHANGE); foreach (string fname in _changes[DEFAULT_CHANGE].Files.ToAssetPaths()) { yield return fname; } } } /// /// If the ChangeLists have not been initialized, do it. /// If not initialized, call "p4 changes" /// /// number of changelists public static int PrepareChanges() { // If no changes have been stored, look for some if (_changes.Count == 0 || (!_changes.ContainsKey(DEFAULT_CHANGE))) { using (PerforceConnection con = new PerforceConnection()) { RefreshChangeLists(con); } } return _changes.Count; } // Collect all the Changelists and put them in the _changes dictionary static void RefreshChangeLists(PerforceConnection p4conn) { if (Config.ValidConfiguration) { //log.DebugFormat("RefreshChangeLists {0}", all.ToString()); _changes.Clear(); // Create a placeholder for the default change Changelist dchange = new Changelist() { Description = "Default", OwnerName = Config.Username, ClientId = Config.Workspace }; dchange.initialize(p4conn.P4Connection); _changes.Add(DEFAULT_CHANGE, dchange); //// Now go get the numbered change lists //ChangesCmdOptions opts = new ChangesCmdOptions(ChangesCmdFlags.FullDescription, null, 0, ChangeListStatus.Pending, null); //IList allChanges = p4conn.P4Depot.GetChangelists(opts, null); //if (allChanges != null) //{ // foreach (var change in allChanges) // { // change.initialize(p4conn.P4Connection); // _changes.Add(change.Id, change); // if (all) // { // RefreshChange(p4conn, change.Id); // } // } //} } } /// /// Go to the Server and retrieve information about a specific change /// Collect all the files and shelves for a specific changelist /// /// Perforce Connection /// Change ID static void RefreshChange( PerforceConnection p4Conn, int changeId) { if (Config.ValidConfiguration) { //log.DebugFormat("RefreshChange {0}", changeId); if (changeId <= DEFAULT_CHANGE) { RefreshDefaultChange(p4Conn); return; } // Get files for the specified changeId _changes[changeId].initialize(p4Conn.P4Connection); // runs "p4 change -o" to get file lists // Get shelves too P4Command cmd = new P4Command(p4Conn.P4Connection, "describe", true, "-S", changeId.ToString()); P4CommandResult results = cmd.Run(); if ((results.Success) && (results.TaggedOutput != null) && (results.TaggedOutput.Count > 0)) { _changes[changeId].FromChangeCmdTaggedOutput(results.TaggedOutput[0], true, string.Empty, false); // FIXME: the time zone is wrong. } // Verify that all the files returned are rooted in this project var filesToRemove = _changes[changeId].Files.Where(entry => entry.ClientPath != null && entry.ClientPath.Path != null && ! entry.ClientPath.Path.StartsWith(Config.ClientProjectRootMatch) == false); foreach(var itemToRemove in filesToRemove) { log.Debug("Removing file from change " + changeId.ToString() + ", not in project: " + itemToRemove.ClientPath.Path); _changes[changeId].Files.Remove(itemToRemove); } } } /// /// Force a refresh of the Default Change /// /// static void RefreshDefaultChange(PerforceConnection p4Conn) { if (Config.ValidConfiguration) { // Use "p4 opened -c default" to get the opened files in the default changelist Options opts = new Options(); opts["-c"] = "default"; // The default change opts["-C"] = Config.Workspace; // workspace must match opts["-u"] = Config.Username; // username must match also IList allOpenedFiles = p4Conn.P4Depot.GetOpenedFiles(Config.ProjectFileSpec, opts); PrepareChanges(); _changes[DEFAULT_CHANGE].Files.Clear(); // Clear out old list of files if (allOpenedFiles != null) { //AssetStatusCache.GetAssetMetaData() IEnumerable fromMetas = allOpenedFiles.Cast().UnversionedSpecs(); // we need to break this into bite sized chunks var fromMetasBatches = fromMetas.Split(100); foreach (var batch in fromMetasBatches) { // Go to the server to get all the metadata var files = AssetStatusCache.GetAssetMetaData(p4Conn, batch.ToAssetPaths()); // Update the Default Change with the new MetaData foreach (var file in files) { _changes[DEFAULT_CHANGE].Files.Add(AssetStatusCache.GetCachedMetaData(file)); } } } } } } }