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; using System.IO; using log4net; 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 { private static readonly ILog log = LogManager.GetLogger(typeof(PerforceConnection)); // Give read-only access to the server we're connected to public Server P4Server { get { return _p4Server; } set { _p4Server = value; } } // Give read-only access to the current depot public Repository P4Depot { get { return _p4Depot; } set { _p4Depot = value; } } // 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 (!String.IsNullOrEmpty(Config.Charset)) { Environment.SetEnvironmentVariable("P4CHARSET", Config.Charset); } if (!String.IsNullOrEmpty(Config.Hostname)) { Environment.SetEnvironmentVariable("P4HOST", Config.Hostname); } if (!String.IsNullOrEmpty(Config.IgnoreName)) { Environment.SetEnvironmentVariable("P4IGNORE", Config.IgnoreName); } if (_p4Depot.Connection.Connect(Version.ConnectOptions)) { // And set the credentials so that we can use secure connections _p4Depot.Connection.Credential = _p4Depot.Connection.Login(Config.Password, true); _p4Depot.Connection.CommandEcho += CommandEcho; } else { EditorUtility.DisplayDialog("Perforce connection Exception", "P4Connect could not connect to the server", "OK"); throw new Exception("P4Connect - Can't connect to server"); } } if (_connectionCount == 0) { // De-register from update EditorApplication.update -= UpdateConnection; } ++_connectionCount; } // Delegate Echos Perforce commands to log static void CommandEcho(String data) { if (Config.EchoP4Commands) { log.Debug(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(uint cmdId, 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); } } }