using UnityEditor; using UnityEngine; using System; using System.Collections.Generic; using System.Linq; using System.Text; using log4net; namespace P4Connect { public class PostProcessorRequest { public DateTime Created; public HashSet Imported; public HashSet Deleted; public string[] MovedTo; public string[] MovedFrom; public PostProcessorRequest( String[] importedAssets, String[] deletedAssets, String[] movedToAssets, String[] movedFromAssets) { Imported = new HashSet(importedAssets); Deleted = new HashSet(deletedAssets); MovedTo = (string[]) movedToAssets.Clone(); MovedFrom = (string[]) movedFromAssets.Clone(); Created = DateTime.Now; } public new string ToString() { return string.Format("time: {0} import: {1} delete: {2} moveTo: {3} moveFrom: {4}", Created.ToShortTimeString(), Logger.StringArrayToString(Imported.ToArray()), Logger.StringArrayToString(Deleted.ToArray()), Logger.StringArrayToString(MovedTo), Logger.StringArrayToString(MovedFrom)); } } /// /// This class hooks onto the Asset Save/Delete/Move process and makes sure that /// Perforce is updated accordingly. It uses the perforce connection class /// (which internally uses Config to retrieve the connection settings) /// to open a connection to the server and add the required changes (add/checkout/delete/move). /// public class AssetPostProcessor : UnityEditor.AssetPostprocessor { private static readonly ILog log = LogManager.GetLogger(typeof(AssetPostProcessor)); public AssetPostProcessor() { EditorApplication.update += OnUpdate; // enable timer. // Initial setting of PostProcessorRequestQueue if (Config.PostProcessorRequestQueue == null) Config.PostProcessorRequestQueue = new Queue(); } /// /// This function allows us to process the queue when we want to. /// /// Count of Pending PostProcessorRequests public static int DoPendingPostProcessorRequest() { if (!Config.ValidConfiguration) return -1; var cnt = Config.PostProcessorRequestQueue.Count; if (cnt > 0) { PostProcessorRequest request = Config.PostProcessorRequestQueue.Dequeue(); ProcessRequest(request); } return cnt; } /// /// Called from the Update() function /// public void OnUpdate() { // Periodically look for pending work. if (Config.ValidConfiguration) { DoPendingPostProcessorRequest(); } } [MenuItem("Assets/Perforce/DoPostprocessorWork", false, 100)] public static void DoWorkMenu() { if (DoPendingPostProcessorRequest() <= 0) Debug.Log("Nothing Pending"); } /// /// Actually process the Post Processor Event /// The Event is stored in the req structure /// /// Unity OnPostProcess Request public static void ProcessRequest(PostProcessorRequest req) { #if DEBUG // Debug.Log("Processing: " + req.ToString()); #endif // Find all files which are both deleted and imported. These are "replaced" - They should be Checked Out. var replaced = req.Deleted.Intersect(req.Imported).ToList(); if (replaced.Count > 0) { req.Deleted.ExceptWith(replaced); // remove replaced files from the delete list req.Imported.ExceptWith(replaced); // remove replaced files from the imported list Engine.CheckoutAssets(replaced.ToArray()); } if (req.Deleted.Any()) { Engine.DeleteAssets(req.Deleted.ToArray()); } req.Imported.RemoveWhere(Utils.IsDirectoryAsset); // Perforce doesn't support directories, file paths create directories as side effects if (req.Imported.Any()) { Engine.CreateAssets(req.Imported.ToArray()); } if (req.MovedTo.Length > 0) { Engine.MoveAssets(req.MovedTo, req.MovedFrom); } } public static void OnPostprocessAllAssets( String[] aImportedAssets, String[] aDeletedAssets, String[] aMovedAssets, String[] aMovedFromAssetPaths) { // set up PostProcessorRequestQueue if needed if (Config.PostProcessorRequestQueue == null) Config.PostProcessorRequestQueue = new Queue(); var req = new PostProcessorRequest(aImportedAssets, aDeletedAssets, aMovedAssets, aMovedFromAssetPaths); #if DEBUG // Debug.Log("Queuing: " + req.ToString()); #endif Config.PostProcessorRequestQueue.Enqueue(req); } } }