using UnityEditor; using UnityEngine; using System; using System.Threading; using System.Collections; using System.Collections.Generic; using Perforce.P4; using System.Linq; using System.Text; namespace P4Connect { /// /// Small class that encompasses a perforce connection /// Always use it with the 'using' statement, so its /// Dispose() method is called and the connection to /// the server is properly closed. /// public class PerforceConnection : IDisposable { // Give read-only access to the server we're connected to public Server P4Server { get { return _P4Server; } } // Give read-only access to the current depot public Repository P4Depot { get { return _P4Depot; } } // Give read-only access to the current connection public Connection P4Connection { get { return _P4Depot.Connection; } } // Give read-only access to the current client public Client P4Client { get { return _P4Depot.Connection.Client; } } static Server _P4Server; static Repository _P4Depot; static int _ConnectionCount; static DateTime _ConnectedTimestamp; bool bDisposed; // Used if display timings is turned on DateTime _InnerStartTimestamp; DateTime _StartTimestamp; List _GetMetaDataTimingInfo; static PerforceConnection() { _ConnectionCount = 0; } static bool Disconnected { get { return _P4Server == null || _P4Depot == null || _P4Depot.Connection == null || _P4Depot.Connection.Status != ConnectionStatus.Connected; } } static bool Connected { get { return _P4Server != null && _P4Depot != null && _P4Depot.Connection != null && _P4Depot.Connection.Status == ConnectionStatus.Connected; } } static void OpenConnection() { if (Disconnected) { // Fetch the configuration settings from Config _P4Server = new Server(new ServerAddress(Config.ServerURI)); _P4Depot = new Repository(_P4Server); _P4Depot.Connection.UserName = Config.Username; _P4Depot.Connection.Client = new Client(); _P4Depot.Connection.Client.Name = Config.Workspace; if (Config.OverrideCharset) { System.Environment.SetEnvironmentVariable("P4CHARSET", Config.Charset); } if (Config.OverrideHostname) { System.Environment.SetEnvironmentVariable("P4HOST", Config.Hostname); } // Open the connection if (_P4Depot.Connection.Connect(new Options())) { // And set the credentials so that we can use secure connections _P4Depot.Connection.Trust(new TrustCmdOptions(TrustCmdFlags.AutoAccept), ""); _P4Depot.Connection.Credential = _P4Depot.Connection.Login(Config.Password); _P4Depot.Connection.CommandEcho += CommandEcho; } else { if (EditorUtility.DisplayDialog("Perforce connection Exception", "P4Connect could not connect to the server and will disable itself to prevent further slow downs\n(You'll need to \"Edit->Perforce Settings\" to turn it back on)\n\nP4Connect can attempt to cancel the current operation for you (to prevent your local files and the depot from going out of sync until you can fix the connection issue). Would you like to try?", "Yes", "No")) { Config.NeedToCheckSettings(); throw new Exception("P4Connect - Can't connect to server"); } else { Config.NeedToCheckSettings(); } } } if (_ConnectionCount == 0) { // De-register from update EditorApplication.update -= UpdateConnection; } ++_ConnectionCount; } static void CommandEcho(String data) { if (Config.DisplayP4Commands) { Debug.Log(data); } } static void UpdateConnection() { double deltaTime = (DateTime.Now - _ConnectedTimestamp).TotalSeconds; if (deltaTime > Config.ConnectionTimeOut) { ForceCloseConnection(); } } static void CloseConnection() { --_ConnectionCount; if (_ConnectionCount == 0) { EditorApplication.update += UpdateConnection; _ConnectedTimestamp = DateTime.Now; } } public static void ForceCloseConnection() { // Close the connection if (Connected) { _P4Depot.Connection.CommandEcho -= CommandEcho; _P4Depot.Connection.Disconnect(); _P4Depot = null; _P4Server = null; // No need to be updated anymore EditorApplication.update -= UpdateConnection; } } /// /// Create the Perforce connection on construction /// public PerforceConnection() { if (Config.DisplayP4Timings) { _StartTimestamp = DateTime.Now; } OpenConnection(); _P4Depot.Connection.TaggedOutputReceived += TaggedEcho; if (Config.DisplayP4Timings) { _InnerStartTimestamp = DateTime.Now; _GetMetaDataTimingInfo = new List(); } } public event Action FileEchoReceived; void TaggedEcho(int ObjId, TaggedObject Obj) { if (FileEchoReceived != null) { FileSpec fs = FileSpec.ClientSpec(Obj["clientFile"]); FileEchoReceived(fs); } } /// /// Dispose of the connection /// to be safe, use "using" instead of calling this directly /// public void Dispose() { // Simple call the dispose method Dispose(true); // Since Dispose cleans up everything, no need to call the finalizer on this GC.SuppressFinalize(this); } /// /// Dispose virtual method /// protected virtual void Dispose(bool abDisposing) { if (!bDisposed) { if (abDisposing) { _P4Depot.Connection.TaggedOutputReceived -= TaggedEcho; if (Config.DisplayP4Timings) { double deltaInnerTime = (DateTime.Now - _InnerStartTimestamp).TotalMilliseconds; CloseConnection(); double deltaTime = (DateTime.Now - _StartTimestamp).TotalMilliseconds; StringBuilder builder = new StringBuilder(); builder.AppendLine("P4Connect - Timing for the last operation: " + deltaTime + " ms (Actual operation: " + deltaInnerTime + " ms)"); if (_GetMetaDataTimingInfo != null) { foreach (string line in _GetMetaDataTimingInfo) { builder.AppendLine(line); } } Debug.Log(builder.ToString()); } else { CloseConnection(); } System.Environment.SetEnvironmentVariable("P4HOST", ""); } bDisposed = true; } } /// /// Used to track down timing issues /// public void AppendTimingInfo(string aLine) { if (_GetMetaDataTimingInfo != null) { _GetMetaDataTimingInfo.Add(aLine); } } /// /// Finalizer /// ~PerforceConnection() { Dispose(false); } } }