using UnityEngine; using UnityEditor; using System; 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(); static float poll_rate = 3 * 60; static DateTime last_refresh; public static void Initialize() { last_refresh = DateTime.Now; Engine.OnOperationPerformed += OnEngineOperationPerformed; } /// /// This functions gets called when P4Connect does *something* to files /// In this case we refresh the involved change list /// static void OnEngineOperationPerformed(PerforceConnection aConnection, List aFilesAndMetas) { // ToDo: Check the changelist associated to the files DEFAULT_CHANGE for now.... // ToDo: Refresh the involved changelist in prep for a localOpenedFiles() call later RefreshChange(aConnection, DEFAULT_CHANGE); } /// /// Periodic Update method, triggers refreshes, not used /// static void Update() { if (Config.ValidConfiguration) { DateTime now = DateTime.Now; TimeSpan delta = now - last_refresh; if (delta.TotalSeconds > poll_rate) { last_refresh = now; log.Debug("ChangeManager Tick"); RefreshAll(); } } } // Update the list of change lists public static void Refresh() { if (Config.ValidConfiguration) { using (PerforceConnection con = new PerforceConnection()) { RefreshChangeLists(con); } } } // Refresh the files in a specific change lists public static void Refresh(int changeId) { if (Config.ValidConfiguration) { if (_changes.Count == 0 || (!_changes.ContainsKey(changeId))) { RefreshAll(); } using (PerforceConnection con = new PerforceConnection()) { RefreshChange(con, changeId); } } } // Get all changelists and all files in them public static void RefreshAll() { if (Config.ValidConfiguration) { using (PerforceConnection con = new PerforceConnection()) { RefreshChangeLists(con, true); } } } public static IList Changelists { get { if (_changes.Count == 0 ) { RefreshAll(); } return (_changes.Values.ToList()); } } public static Changelist GetChangelist(int id) { if (_changes.ContainsKey(id)) { return (_changes[id]); } return null; } public static string ChangeIdToString(int id) { if (id <= DEFAULT_CHANGE) return "default"; return id.ToString(); } public static List LocalOpenedFiles { get { List retList = new List(); // If no changes have been found, look for some if (_changes.Count == 0 || (!_changes.ContainsKey(DEFAULT_CHANGE))) { Refresh(); Refresh(DEFAULT_CHANGE); } // The Files array should be instantiated now foreach (var md in _changes[DEFAULT_CHANGE].Files) { if (md == null) log.DebugFormat("Null element in Files"); else retList.Add(Utils.LocalPathToAssetPath(md.LocalPath.Path)); } // log.DebugFormat("LocalOpened {0}", Logger.StringArrayToString(retList.ToArray())); return retList; } } /// /// Helper method to check if a file is in the depot list /// /// /// /// public static bool Contains(Predicate aMatch) { foreach (FileMetaData fmd in _changes[DEFAULT_CHANGE].Files) { if (aMatch(fmd)) return true; } return false; } // Collect all the Changelists and put them in the _changes dictionary static void RefreshChangeLists(PerforceConnection p4conn, bool all = false) { 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); if (all) { RefreshChange(p4conn, DEFAULT_CHANGE); } //// 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); // } // } //} } } // Collect all the files and shelves for a specific changelist 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); // 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); } } } 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); if (!_changes.ContainsKey(DEFAULT_CHANGE)) { RefreshChangeLists(p4conn, false); // Changelists dictionary needs to be constructed first } _changes[DEFAULT_CHANGE].Files.Clear(); // Clear out old list of files if (allOpenedFiles != null) { List fromMetas = allOpenedFiles.Cast().ToList(); // Strip the version from each entry foreach (var m in fromMetas) m.Version = null; List allOpenedFileSpecs = fromMetas.ToList(); //log.Debug("opened : " + Logger.FileSpecListToString(allOpenedFileSpecs)); Options fsopts = new Options(); fsopts["-Op"] = null; // Return "real" clientpaths // issue an fstat to get a proper FileMetaData for each file. We need lastAction and other fields List allFilesMeta = Engine.GetFileMetaData(p4conn, allOpenedFileSpecs, fsopts).ToList(); // log.Debug("opened metas: " + Logger.FileMetaDataListToString(allFilesMeta)); foreach (FileMetaData fmd in allFilesMeta) { _changes[DEFAULT_CHANGE].Files.Add(fmd); } } } } } }