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);
}
}
}