Merge pull request #5820 from aws-lumberyard-dev/LYN-8025_PipeEditorServerLogsToEditor

Pipe Editor-Server Logs Back to the Editor
This commit is contained in:
Gene Walters
2021-12-01 07:36:02 -08:00
committed by GitHub
16 changed files with 129 additions and 46 deletions
@@ -6,16 +6,17 @@
*
*/
#include "CommunicatorTracePrinter.h"
#include "ProcessCommunicatorTracePrinter.h"
CommunicatorTracePrinter::CommunicatorTracePrinter(AzFramework::ProcessCommunicator* communicator, const char* window) :
ProcessCommunicatorTracePrinter::ProcessCommunicatorTracePrinter(AzFramework::ProcessCommunicator* communicator, const char* window) :
m_communicator(communicator),
m_window(window)
{
m_stringBeingConcatenated.reserve(1024);
}
CommunicatorTracePrinter::~CommunicatorTracePrinter()
ProcessCommunicatorTracePrinter::~ProcessCommunicatorTracePrinter()
{
// flush stdout
WriteCurrentString(false);
@@ -24,7 +25,7 @@ CommunicatorTracePrinter::~CommunicatorTracePrinter()
WriteCurrentString(true);
}
void CommunicatorTracePrinter::Pump()
void ProcessCommunicatorTracePrinter::Pump()
{
if (m_communicator->IsValid())
{
@@ -42,7 +43,7 @@ void CommunicatorTracePrinter::Pump()
}
}
void CommunicatorTracePrinter::ParseDataBuffer(AZ::u32 readSize, bool isFromStdErr)
void ProcessCommunicatorTracePrinter::ParseDataBuffer(AZ::u32 readSize, bool isFromStdErr)
{
if (readSize > AZ_ARRAY_SIZE(m_streamBuffer))
{
@@ -67,7 +68,7 @@ void CommunicatorTracePrinter::ParseDataBuffer(AZ::u32 readSize, bool isFromStdE
}
}
void CommunicatorTracePrinter::WriteCurrentString(bool isFromStdErr)
void ProcessCommunicatorTracePrinter::WriteCurrentString(bool isFromStdErr)
{
AZStd::string& bufferToUse = isFromStdErr ? m_errorStringBeingConcatenated : m_stringBeingConcatenated;
@@ -10,20 +10,21 @@
#include <AzFramework/Process/ProcessCommunicator.h>
//! CommunicatorTracePrinter listens to stderr and stdout of a running process and writes its output to the AZ_Trace system
//! ProcessCommunicatorTracePrinter listens to stderr and stdout of a running process and writes its output to the AZ_Trace system
//! Importantly, it does not do any blocking operations.
class CommunicatorTracePrinter
class ProcessCommunicatorTracePrinter
{
public:
CommunicatorTracePrinter(AzFramework::ProcessCommunicator* communicator, const char* window);
~CommunicatorTracePrinter();
ProcessCommunicatorTracePrinter(AzFramework::ProcessCommunicator* communicator, const char* window);
~ProcessCommunicatorTracePrinter();
// call this periodically to drain the buffers and write them.
//! Call this periodically to drain the buffers and write them.
void Pump();
// drains the buffer into the string thats being built, then traces the string when it hits a newline.
//! Drains the buffer into the string that's being built, then traces the string when it hits a newline.
void ParseDataBuffer(AZ::u32 readSize, bool isFromStdErr);
//! Prints the current buffer to AZ_Error or AZ_TracePrintf so that it can be picked up by AZ::Debug::Trace
void WriteCurrentString(bool isFromStdError);
private:
@@ -277,6 +277,8 @@ set(FILES
Process/ProcessWatcher.cpp
Process/ProcessWatcher.h
Process/ProcessCommon_fwd.h
Process/ProcessCommunicatorTracePrinter.cpp
Process/ProcessCommunicatorTracePrinter.h
ProjectManager/ProjectManager.h
ProjectManager/ProjectManager.cpp
Render/GameIntersectorComponent.h
+8
View File
@@ -71,6 +71,14 @@ public:
// The value of the argument as integer number.
virtual const int GetIValue() const = 0;
// </interfuscator:shuffle>
// Description:
// Retrieve the value of the argument.
// Arguments:
// cmdLineValue. The cmdline value will be filled out if a valid boolean is found.
// Return Value:
// Returns true if the cmdline arg is actually a boolean string matching "true" or "false"; otherwise return false.
virtual const bool GetBoolValue(bool& cmdLineValue) const = 0;
};
// Command line interface
+8 -6
View File
@@ -36,9 +36,10 @@ public:
Disconnect();
}
inline static void Connect()
inline static void Connect(bool suppressSystemOutput)
{
GetInstance().m_ignoredAsserts = new IgnoredAssertMap();
GetInstance().m_suppressSystemOutput = suppressSystemOutput;
GetInstance().BusConnect();
}
@@ -126,7 +127,7 @@ public:
CryLogAlways("%s", message);
}
return true; // suppress default AzCore behavior.
return m_suppressSystemOutput;
#else
AZ_UNUSED(fileName);
AZ_UNUSED(line);
@@ -146,7 +147,7 @@ public:
return false; // allow AZCore to do its default behavior.
}
gEnv->pLog->LogError("(%s) - %s", window, message);
return true; // suppress default AzCore behavior.
return m_suppressSystemOutput;
}
bool OnPreWarning(const char* window, const char* fileName, int line, const char* func, const char* message) override
@@ -161,7 +162,7 @@ public:
}
CryWarning(VALIDATOR_MODULE_UNKNOWN, VALIDATOR_WARNING, "(%s) - %s", window, message);
return true; // suppress default AzCore behavior.
return m_suppressSystemOutput;
}
bool OnOutput(const char* window, const char* message) override
@@ -179,12 +180,13 @@ public:
{
CryLog("(%s) - %s", window, message);
}
return true; // suppress default AzCore behavior.
return m_suppressSystemOutput;
}
private:
using IgnoredAssertMap = AZStd::unordered_map<AZ::Crc32, bool, AZStd::hash<AZ::Crc32>, AZStd::equal_to<AZ::Crc32>, AZ::OSStdAllocator>;
IgnoredAssertMap* m_ignoredAsserts;
bool m_suppressSystemOutput = true;
};
+17
View File
@@ -42,5 +42,22 @@ const int CCmdLineArg::GetIValue() const
{
return atoi(m_value.c_str());
}
const bool CCmdLineArg::GetBoolValue(bool& cmdLineValue) const
{
AZStd::string lowercaseValue(m_value);
AZStd::to_lower(lowercaseValue.begin(), lowercaseValue.end());
if (lowercaseValue == "true")
{
cmdLineValue = true;
return true;
}
if (lowercaseValue == "false")
{
cmdLineValue = false;
return true;
}
return false;
}
+1
View File
@@ -30,6 +30,7 @@ public:
const ECmdLineArgType GetType() const;
const float GetFValue() const;
const int GetIValue() const;
const bool GetBoolValue(bool& cmdLineValue) const;
private:
+11 -1
View File
@@ -737,7 +737,17 @@ bool CSystem::Init(const SSystemInitParams& startupParams)
m_pCmdLine = new CCmdLine(startupParams.szSystemCmdLine);
AZCoreLogSink::Connect();
// Init AZCoreLogSink. Don't suppress system output if we're running as an editor-server
bool suppressSystemOutput = true;
if (const ICmdLineArg* isEditorServerArg = m_pCmdLine->FindArg(eCLAT_Pre, "editorsv_isDedicated"))
{
bool editorsv_isDedicated = false;
if (isEditorServerArg->GetBoolValue(editorsv_isDedicated) && editorsv_isDedicated)
{
suppressSystemOutput = false;
}
}
AZCoreLogSink::Connect(suppressSystemOutput);
// Registers all AZ Console Variables functors specified within CrySystem
if (auto azConsole = AZ::Interface<AZ::IConsole>::Get(); azConsole)
@@ -92,8 +92,6 @@ set(FILES
native/utilities/BuilderManager.inl
native/utilities/ByteArrayStream.cpp
native/utilities/ByteArrayStream.h
native/utilities/CommunicatorTracePrinter.cpp
native/utilities/CommunicatorTracePrinter.h
native/utilities/IniConfiguration.cpp
native/utilities/IniConfiguration.h
native/utilities/JobDiagnosticTracker.cpp
@@ -20,7 +20,6 @@
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Process/ProcessCommunicator.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
@@ -31,7 +30,6 @@
#include "native/utilities/assetUtils.h"
#include "native/utilities/AssetBuilderInfo.h"
#include "native/utilities/CommunicatorTracePrinter.h"
#include <AssetProcessor_Traits_Platform.h>
@@ -1482,7 +1482,7 @@ bool ApplicationManagerBase::WaitForBuilderExit(AzFramework::ProcessWatcher* pro
AZ::u32 exitCode = 0;
bool finishedOK = false;
QElapsedTimer ticker;
CommunicatorTracePrinter tracer(processWatcher->GetCommunicator(), "AssetBuilder");
ProcessCommunicatorTracePrinter tracer(processWatcher->GetCommunicator(), "AssetBuilder");
ticker.start();
@@ -164,7 +164,7 @@ namespace AssetProcessor
return false;
}
m_tracePrinter = AZStd::make_unique<CommunicatorTracePrinter>(m_processWatcher->GetCommunicator(), "AssetBuilder");
m_tracePrinter = AZStd::make_unique<ProcessCommunicatorTracePrinter>(m_processWatcher->GetCommunicator(), "AssetBuilder");
return WaitForConnection();
}
@@ -10,11 +10,11 @@
#include <AzCore/std/string/string.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/Process/ProcessCommunicatorTracePrinter.h>
#include <AssetBuilderSDK/AssetBuilderSDK.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <QString>
#include <QByteArray>
#include <native/utilities/CommunicatorTracePrinter.h>
#include <native/utilities/assetUtils.h>
#include <QDir> // used in the inl file.
@@ -127,7 +127,7 @@ namespace AssetProcessor
AZStd::unique_ptr<AzFramework::ProcessWatcher> m_processWatcher = nullptr;
//! Optional communicator, only available if we have a process watcher
AZStd::unique_ptr<CommunicatorTracePrinter> m_tracePrinter = nullptr;
AZStd::unique_ptr<ProcessCommunicatorTracePrinter> m_tracePrinter = nullptr;
const AssetUtilities::QuitListener& m_quitListener;
};
@@ -14,11 +14,8 @@
#include <MultiplayerSystemComponent.h>
#include <PythonEditorEventsBus.h>
#include <Editor/MultiplayerEditorSystemComponent.h>
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Utils/Utils.h>
@@ -133,6 +130,7 @@ namespace Multiplayer
AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
AzFramework::GameEntityContextEventBus::Handler::BusDisconnect();
MultiplayerEditorServerRequestBus::Handler::BusDisconnect();
AZ::TickBus::Handler::BusDisconnect();
}
void MultiplayerEditorSystemComponent::NotifyRegisterViews()
@@ -157,12 +155,20 @@ namespace Multiplayer
[[fallthrough]];
case eNotify_OnEndGameMode:
// Kill the configured server if it's active
if (m_serverProcess)
AZ::TickBus::Handler::BusDisconnect();
if (m_serverProcessWatcher)
{
m_serverProcess->TerminateProcess(0);
m_serverProcess = nullptr;
m_serverProcessWatcher->TerminateProcess(0);
if (m_serverProcessTracePrinter)
{
m_serverProcessTracePrinter->Pump();
m_serverProcessTracePrinter->WriteCurrentString(true);
m_serverProcessTracePrinter->WriteCurrentString(false);
}
m_serverProcessWatcher = nullptr;
m_serverProcessTracePrinter = nullptr;
}
if (INetworkInterface* editorNetworkInterface = AZ::Interface<INetworking>::Get()->RetrieveNetworkInterface(AZ::Name(MpEditorInterfaceName)))
{
editorNetworkInterface->Disconnect(m_editorConnId, AzNetworking::DisconnectReason::TerminatedByClient);
@@ -181,7 +187,7 @@ namespace Multiplayer
}
}
AzFramework::ProcessWatcher* LaunchEditorServer()
void MultiplayerEditorSystemComponent::LaunchEditorServer()
{
// Assemble the server's path
AZ::CVarFixedString serverProcess = editorsv_process;
@@ -207,12 +213,22 @@ namespace Multiplayer
{
server_rhi = static_cast<AZ::CVarFixedString>(editorsv_rhi_override);
}
const auto console = AZ::Interface<AZ::IConsole>::Get();
AZ::CVarFixedString sv_defaultPlayerSpawnAsset;
if (console->GetCvarValue("sv_defaultPlayerSpawnAsset", sv_defaultPlayerSpawnAsset) != AZ::GetValueResult::Success)
{
AZ_Assert( false,
"MultiplayerEditorSystemComponent::LaunchEditorServer failed! Could not find the sv_defaultPlayerSpawnAsset cvar; the editor-server "
"will fall back to using some other default player! Please update this code to use a valid cvar!")
}
processLaunchInfo.m_commandlineParameters = AZStd::string::format(
R"("%s" --project-path "%s" --editorsv_isDedicated true --sv_defaultPlayerSpawnAsset "%s" --rhi "%s")",
serverPath.c_str(),
AZ::Utils::GetProjectPath().c_str(),
static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str(),
sv_defaultPlayerSpawnAsset.c_str(),
server_rhi.GetCStr()
);
processLaunchInfo.m_showWindow = true;
@@ -220,13 +236,21 @@ namespace Multiplayer
// Launch the Server
AzFramework::ProcessWatcher* outProcess = AzFramework::ProcessWatcher::LaunchProcess(
processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE);
processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_STDINOUT);
AZ_Error(
"MultiplayerEditor", processLaunchInfo.m_launchResult != AzFramework::ProcessLauncher::ProcessLaunchResult::PLR_MissingFile,
"LaunchEditorServer failed! The ServerLauncher binary is missing! (%s) Please build server launcher.", serverPath.c_str())
return outProcess;
// Stop the previous server if one exists
if (m_serverProcessWatcher)
{
AZ::TickBus::Handler::BusDisconnect();
m_serverProcessWatcher->TerminateProcess(0);
}
m_serverProcessWatcher.reset(outProcess);
m_serverProcessTracePrinter = AZStd::make_unique<ProcessCommunicatorTracePrinter>(m_serverProcessWatcher->GetCommunicator(), "EditorServer");
AZ::TickBus::Handler::BusConnect();
}
void MultiplayerEditorSystemComponent::OnGameEntitiesStarted()
@@ -286,7 +310,7 @@ namespace Multiplayer
editorNetworkInterface->Listen(editorsv_port);
// Launch the editor-server
m_serverProcess = LaunchEditorServer();
LaunchEditorServer();
}
else
{
@@ -393,4 +417,20 @@ namespace Multiplayer
{
return PyIsInGameMode();
}
void MultiplayerEditorSystemComponent::OnTick(float, AZ::ScriptTimePoint)
{
if (m_serverProcessTracePrinter)
{
m_serverProcessTracePrinter->Pump();
}
else
{
AZ::TickBus::Handler::BusDisconnect();
AZ_Warning(
"MultiplayerEditorSystemComponent", false,
"The server process trace printer is NULL so we won't be able to pipe server logs to the editor. Please update the code to call AZ::TickBus::Handler::BusDisconnect whenever the editor-server is terminated.")
}
}
}
@@ -13,14 +13,12 @@
#include <Multiplayer/Editor/MultiplayerPythonEditorEventsBus.h>
#include <IEditor.h>
#include <Editor/MultiplayerEditorConnection.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <AzFramework/Process/ProcessCommunicatorTracePrinter.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
namespace AzNetworking
@@ -52,6 +50,7 @@ namespace Multiplayer
, private AzToolsFramework::EditorEvents::Bus::Handler
, private IEditorNotifyListener
, private MultiplayerEditorServerRequestBus::Handler
, private AZ::TickBus::Handler
{
public:
AZ_COMPONENT(MultiplayerEditorSystemComponent, "{9F335CC0-5574-4AD3-A2D8-2FAEF356946C}");
@@ -84,7 +83,9 @@ namespace Multiplayer
bool IsInGameMode() override;
//! @}
private:
private:
void LaunchEditorServer();
//! EditorEvents::Handler overrides
//! @{
void OnEditorNotifyEvent(EEditorNotifyEvent event) override;
@@ -101,8 +102,14 @@ namespace Multiplayer
void SendEditorServerLevelDataPacket(AzNetworking::IConnection* connection) override;
//! @}
//! AZ::TickBus::Handler
//! @{
void OnTick(float, AZ::ScriptTimePoint) override;
//! @}
IEditor* m_editor = nullptr;
AzFramework::ProcessWatcher* m_serverProcess = nullptr;
AZStd::unique_ptr<AzFramework::ProcessWatcher> m_serverProcessWatcher = nullptr;
AZStd::unique_ptr<ProcessCommunicatorTracePrinter> m_serverProcessTracePrinter = nullptr;
AzNetworking::ConnectionId m_editorConnId;
ServerAcceptanceReceivedEvent::Handler m_serverAcceptanceReceivedHandler;
@@ -37,8 +37,6 @@ namespace AzNetworking
namespace Multiplayer
{
AZ_CVAR_EXTERNED(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset);
//! Multiplayer system component wraps the bridging logic between the game and transport layer.
class MultiplayerSystemComponent final
: public AZ::Component