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)) { System.Environment.SetEnvironmentVariable("P4CHARSET", Config.Charset); } if (!String.IsNullOrEmpty(Config.Hostname)) { System.Environment.SetEnvironmentVariable("P4HOST", Config.Hostname); } if (!String.IsNullOrEmpty(Config.IgnoreName)) { System.Environment.SetEnvironmentVariable("P4IGNORE", Config.IgnoreName); } // Open the connection, give its log entries a programname and version Options opts = new Options(); opts.Add("ProgramName", "p4connect"); opts.Add("ProgramVersion", Version.PerforceReleaseString); if (_P4Depot.Connection.Connect(opts)) { // 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 { 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; } static void CommandEcho(String data) { #if DEBUG log.Debug(data); #endif 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(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); } } }