Files
o3de/Code/Sandbox/Plugins/PerforcePlugin/PerforceSourceControl.cpp
T
2021-03-08 14:30:57 -08:00

428 lines
11 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
#include "PerforcePlugin_precompiled.h"
#include "CryFile.h"
#include "PerforceSourceControl.h"
#include "PasswordDlg.h"
#include <QSettings>
#include <QDir>
#include <QFile>
#include <QProcess>
#include <QApplication>
#include <QTimer>
#include <Util/PathUtil.h>
#include <AzCore/base.h>
namespace
{
CryCriticalSection g_cPerforceValues;
}
void CMyClientUser::Init()
{
m_e.Clear();
}
////////////////////////////////////////////////////////////
void CMyClientApi::Run(const char* func)
{
// The "enableStreams" var has to be set prior to any Run call in order to be able to support Perforce streams
ClientApi::SetVar("enableStreams");
ClientApi::Run(func);
}
void CMyClientApi::Run(const char* func, ClientUser* clientUserUI)
{
ClientApi::SetVar("enableStreams");
ClientApi::Run(func, clientUserUI);
}
////////////////////////////////////////////////////////////
CPerforceSourceControl::CPerforceSourceControl()
: m_ref(0)
{
m_bIsWorkOffline = true;
m_bIsWorkOfflineBecauseOfConnectionLoss = true;
m_bIsFailConnectionLogged = false;
m_configurationInvalid = false;
m_dwLastAccessTime = 0;
m_client = nullptr;
m_nextHandle = 0;
m_lastWorkOfflineResult = true;
m_lastWorkOfflineBecauseOfConnectionLossResult = true;
m_clientInitialized = false;
}
ULONG STDMETHODCALLTYPE CPerforceSourceControl::Release()
{
if ((--m_ref) == 0)
{
g_cPerforceValues.Lock();
FreeData();
delete this;
g_cPerforceValues.Unlock();
return 0;
}
else
{
return m_ref;
}
}
CPerforceSourceControl::~CPerforceSourceControl()
{
delete m_client;
}
bool CPerforceSourceControl::Connect()
{
// before we call Init we must set up the connection!
// the user may have overridden the connection on a per-project basis.
// check the config file.
AUTO_LOCK(g_cPerforceValues);
FreeData();
m_client = new CMyClientApi();
// Ensure P4PORT is set before calling ClientApi::Init
const bool p4PortIsSet = nullptr != m_client->GetEnviro()->Get("P4PORT");
if (p4PortIsSet)
{
m_client->Init(&m_e);
m_clientInitialized = true;
}
if (!p4PortIsSet || m_e.Test())
{
if (!m_bIsWorkOffline)
{
m_bIsWorkOfflineBecauseOfConnectionLoss = true;
}
m_bIsWorkOffline = true;
if (!m_bIsFailConnectionLogged)
{
GetIEditor()->GetSystem()->GetILog()->Log(p4PortIsSet ? "\nPerforce plugin: Failed to connect." : "\nPerforce plugin: P4PORT must be set to connect.");
m_bIsFailConnectionLogged = true;
}
if (GetIEditor()->IsSourceControlAvailable())
{
//Only Check Connection and than Notify Listeners if SourceControl is available at that instance
CheckConnectionAndNotifyListeners();
}
return false;
}
else
{
GetIEditor()->GetSystem()->GetILog()->Log("\nPerforce plugin: Connected.");
m_bIsFailConnectionLogged = false;
if (m_bIsWorkOfflineBecauseOfConnectionLoss)
{
m_bIsWorkOffline = false;
m_bIsWorkOfflineBecauseOfConnectionLoss = false;
}
}
return true;
}
bool CPerforceSourceControl::Reconnect()
{
AUTO_LOCK(g_cPerforceValues);
if ((m_client && m_client->Dropped()) || m_bIsWorkOfflineBecauseOfConnectionLoss)
{
if (!m_bIsFailConnectionLogged)
{
GetIEditor()->GetSystem()->GetILog()->Log("\nPerforce connection dropped: attempting reconnect");
}
FreeData();
if (!Connect())
{
if (!m_bIsWorkOffline)
{
m_bIsWorkOfflineBecauseOfConnectionLoss = true;
}
m_bIsWorkOffline = true;
return false;
}
else
{
CheckConnectionAndNotifyListeners();
}
}
return true;
}
void CPerforceSourceControl::FreeData()
{
AUTO_LOCK(g_cPerforceValues);
if (m_client)
{
if (m_clientInitialized)
{
m_client->Final(&m_e);
m_clientInitialized = false;
}
delete m_client;
m_client = nullptr;
}
m_e.Clear();
}
void CPerforceSourceControl::Init()
{
Connect();
{
AzToolsFramework::SourceControlState state = AzToolsFramework::SourceControlState::Disabled;
using SCRequestBus = AzToolsFramework::SourceControlConnectionRequestBus;
SCRequestBus::BroadcastResult(state, &SCRequestBus::Events::GetSourceControlState);
SetSourceControlState(state);
}
}
void CPerforceSourceControl::ShowSettings()
{
AUTO_LOCK(g_cPerforceValues);
bool workOffline = (m_bIsWorkOffline && !m_bIsWorkOfflineBecauseOfConnectionLoss);
if (PerforceConnection::OpenPasswordDlg())
{
m_bIsWorkOfflineBecauseOfConnectionLoss = false;
bool onlineMode = false;
{
using SCRequestBus = AzToolsFramework::SourceControlConnectionRequestBus;
SCRequestBus::BroadcastResult(onlineMode, &SCRequestBus::Events::IsActive);
}
m_bIsWorkOffline = !onlineMode;
// reset the connection!
if ((!m_bIsWorkOfflineBecauseOfConnectionLoss) && onlineMode)
{
Connect();
}
CheckConnectionAndNotifyListeners();
}
}
const char* CPerforceSourceControl::GetErrorByGenericCode(int nGeneric)
{
switch (nGeneric)
{
case EV_USAGE:
return "Request is not consistent with documentation or cannot support a server version";
case EV_UNKNOWN:
return "Using unknown entity";
case EV_CONTEXT:
return "Using entity in wrong context";
case EV_ILLEGAL:
return "Trying to do something you can't";
case EV_NOTYET:
return "Something must be corrected first";
case EV_PROTECT:
return "Operation was prevented by protection level";
// No fault at all
case EV_EMPTY:
return "Action returned empty results";
// not the fault of the user
case EV_FAULT:
return "Inexplicable program fault";
case EV_CLIENT:
return "Client side program errors";
case EV_ADMIN:
return "Server administrative action required";
case EV_CONFIG:
return "Client configuration is inadequate";
case EV_UPGRADE:
return "Client or server is too old to interact";
case EV_COMM:
return "Communication error";
case EV_TOOBIG:
return "File is too big";
}
return "Undefined";
}
bool CPerforceSourceControl::Run(const char* func, int nArgs, char* argv[], bool bOnlyFatal)
{
for (int x = 0; x < nArgs; ++x)
{
if (argv[x][0] == '\0')
{
return false;
}
}
if (!Reconnect())
{
return false;
}
bool bRet = false;
AUTO_LOCK(g_cPerforceValues);
m_ui.Init();
m_client->SetArgv(nArgs, argv);
m_client->Run(func, &m_ui);
m_client->WaitTag();
#if 0 // connection debug
for (int argid = 0; argid < nArgs; argid++)
{
g_pSystem->GetILog()->Log("\n arg %d : %s", argid, argv[argid]);
}
#endif // connection debug
if (bOnlyFatal)
{
if (!m_ui.m_e.IsFatal())
{
bRet = true;
}
}
else
{
if (!m_ui.m_e.Test())
{
bRet = true;
}
}
if (m_ui.m_e.GetSeverity() == E_FAILED || m_ui.m_e.GetSeverity() == E_FATAL)
{
static int nGenericPrev = 0;
const int nGeneric = m_ui.m_e.GetGeneric();
if (IsSomeTimePassed())
{
nGenericPrev = 0;
}
if (nGenericPrev != nGeneric)
{
if ((!bOnlyFatal) || (m_ui.m_e.GetSeverity() == E_FATAL))
{
GetIEditor()->GetSystem()->GetILog()->LogError("Perforce plugin: %s", GetErrorByGenericCode(nGeneric));
StrBuf m;
m_ui.m_e.Fmt(&m);
GetIEditor()->GetSystem()->GetILog()->LogError("Perforce plugin: %s", m.Text());
}
nGenericPrev = nGeneric;
}
}
m_ui.m_e.Clear();
return bRet;
}
void CPerforceSourceControl::SetSourceControlState(SourceControlState state)
{
AUTO_LOCK(g_cPerforceValues);
switch (state)
{
case SourceControlState::Disabled:
{
m_bIsWorkOffline = true;
m_bIsWorkOfflineBecauseOfConnectionLoss = true;
m_configurationInvalid = false;
break;
}
case SourceControlState::Active:
m_bIsWorkOffline = false;
m_bIsWorkOfflineBecauseOfConnectionLoss = false;
m_configurationInvalid = false;
CheckConnectionAndNotifyListeners();
break;
case SourceControlState::ConfigurationInvalid:
m_bIsWorkOffline = false;
m_bIsWorkOfflineBecauseOfConnectionLoss = false;
m_configurationInvalid = true;
break;
default:
break;
}
}
CPerforceSourceControl::ConnectivityState CPerforceSourceControl::GetConnectivityState()
{
ConnectivityState state = ConnectivityState::Disconnected;
if (!m_bIsWorkOffline)
{
state = ConnectivityState::Connected;
}
else if (m_configurationInvalid)
{
state = ConnectivityState::BadConfiguration;
}
return state;
}
bool CPerforceSourceControl::CheckConnectionAndNotifyListeners()
{
AUTO_LOCK(g_cPerforceValues);
bool bRet = false;
bool wasWorkOffline = m_bIsWorkOffline;
if (!m_bIsWorkOffline)
{
bRet = Run("info", 0, 0);
}
if (m_bIsWorkOffline || m_configurationInvalid)
{
if (!wasWorkOffline)
{
// only log once when transitioning offline.
GetIEditor()->GetSystem()->GetILog()->LogError("Perforce plugin: Perforce is offline");
}
return false;
}
return bRet;
}
bool CPerforceSourceControl::IsSomeTimePassed()
{
AUTO_LOCK(g_cPerforceValues);
const DWORD dwSomeTime = 10000; // 10 sec
static DWORD dwLastTime = 0;
DWORD dwCurTime = GetTickCount();
if (dwCurTime - dwLastTime > dwSomeTime)
{
dwLastTime = dwCurTime;
return true;
}
return false;
}