Major overhaul of DebugPrint
This commit is contained in:
@@ -7,6 +7,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
using namespace DebugPrint;
|
||||||
|
|
||||||
AIntegrationGameModeBase::AIntegrationGameModeBase()
|
AIntegrationGameModeBase::AIntegrationGameModeBase()
|
||||||
{
|
{
|
||||||
Thread = nullptr;
|
Thread = nullptr;
|
||||||
@@ -19,6 +21,7 @@ AIntegrationGameModeBase::AIntegrationGameModeBase()
|
|||||||
//PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong
|
//PrimaryActorTick.TickGroup = TG_PrePhysics; // Probably wrong
|
||||||
SetActorTickEnabled(true);
|
SetActorTickEnabled(true);
|
||||||
SetActorTickInterval(0.0f);
|
SetActorTickInterval(0.0f);
|
||||||
|
DebugPrintControl::EnableCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
AIntegrationGameModeBase::~AIntegrationGameModeBase()
|
AIntegrationGameModeBase::~AIntegrationGameModeBase()
|
||||||
@@ -33,11 +36,11 @@ uint32 AIntegrationGameModeBase::Run()
|
|||||||
{
|
{
|
||||||
bool triggered = ThreadEvent->Wait(3000);
|
bool triggered = ThreadEvent->Wait(3000);
|
||||||
if (ThreadStopRequested) {
|
if (ThreadStopRequested) {
|
||||||
engineutil::DPrint("Thread stopping as requested");
|
DPrint("Thread stopping as requested");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!triggered) {
|
if (!triggered) {
|
||||||
engineutil::DPrint("Thread waiting a long time...");
|
DPrint("Thread waiting a long time...");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -111,7 +114,7 @@ void AIntegrationGameModeBase::Tick(float DeltaSeconds)
|
|||||||
HandleLuprexConsoleOutput(lockedwrap);
|
HandleLuprexConsoleOutput(lockedwrap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TArray<FString> prints = engineutil::DPrintGetStored();
|
TArray<FString> prints = DebugPrintControl::GetStored();
|
||||||
for (const FString& fs : prints) {
|
for (const FString& fs : prints) {
|
||||||
ConsoleOutput.AppendLine(fs);
|
ConsoleOutput.AppendLine(fs);
|
||||||
}
|
}
|
||||||
@@ -168,7 +171,7 @@ void AIntegrationGameModeBase::BeginPlay()
|
|||||||
// If we failed to initialize the wrapper, print an error message.
|
// If we failed to initialize the wrapper, print an error message.
|
||||||
if (w->play_initialize == nullptr)
|
if (w->play_initialize == nullptr)
|
||||||
{
|
{
|
||||||
engineutil::DPrint("Luprex wrapper initialization failed");
|
DPrint("Luprex wrapper initialization failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If wrapper is initialized, try to initialize the luprex engine.
|
// If wrapper is initialized, try to initialize the luprex engine.
|
||||||
@@ -178,7 +181,7 @@ void AIntegrationGameModeBase::BeginPlay()
|
|||||||
std::string srcpakerr = drvutil::package_lua_source("c:\\Luprex", &srcpak);
|
std::string srcpakerr = drvutil::package_lua_source("c:\\Luprex", &srcpak);
|
||||||
if (!srcpakerr.empty())
|
if (!srcpakerr.empty())
|
||||||
{
|
{
|
||||||
engineutil::DPrint(srcpakerr.c_str());
|
DPrint(srcpakerr.c_str());
|
||||||
}
|
}
|
||||||
std::string_view srcpakv = srcpak.view();
|
std::string_view srcpakv = srcpak.view();
|
||||||
char* argv[1];
|
char* argv[1];
|
||||||
@@ -186,11 +189,11 @@ void AIntegrationGameModeBase::BeginPlay()
|
|||||||
w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), "");
|
w->play_initialize(w.Get(), 1, argv, srcpakv.size(), srcpakv.data(), "");
|
||||||
if (w->error[0])
|
if (w->error[0])
|
||||||
{
|
{
|
||||||
engineutil::DPrint(w->error);
|
DPrint(w->error);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
engineutil::DPrint("Luprex initialize success");
|
DPrint("Luprex initialize success");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public:
|
|||||||
FTangibleManager TangibleManager;
|
FTangibleManager TangibleManager;
|
||||||
|
|
||||||
// This stores the entire text currently visible in the console.
|
// This stores the entire text currently visible in the console.
|
||||||
engineutil::ConsoleOutput ConsoleOutput;
|
FConsoleOutput ConsoleOutput;
|
||||||
|
|
||||||
// The worker thread.
|
// The worker thread.
|
||||||
FRunnableThread *Thread;
|
FRunnableThread *Thread;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ void FLockedWrapper::InitWrapper() {
|
|||||||
InitFn init = (InitFn)FPlatformProcess::GetDllExport(DLL, TEXT("init_engine_wrapper"));
|
InitFn init = (InitFn)FPlatformProcess::GetDllExport(DLL, TEXT("init_engine_wrapper"));
|
||||||
if (init != nullptr) {
|
if (init != nullptr) {
|
||||||
init(&Lockable.Wrapper);
|
init(&Lockable.Wrapper);
|
||||||
Lockable.Wrapper.hook_dprint(engineutil::DPrintHook);
|
Lockable.Wrapper.hook_dprint(DebugPrint::DPrint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
#include "SocketSubsystem.h"
|
#include "SocketSubsystem.h"
|
||||||
#include "AddressInfoTypes.h"
|
#include "AddressInfoTypes.h"
|
||||||
|
|
||||||
|
using namespace DebugPrint;
|
||||||
|
|
||||||
#define UI UI_ST
|
#define UI UI_ST
|
||||||
THIRD_PARTY_INCLUDES_START
|
THIRD_PARTY_INCLUDES_START
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
@@ -903,7 +905,7 @@ void FLpxSocketsI::DPrintTrace()
|
|||||||
char* data;
|
char* data;
|
||||||
int ndata = BIO_get_mem_data(TraceBIO, &data);
|
int ndata = BIO_get_mem_data(TraceBIO, &data);
|
||||||
if (ndata == 0) return;
|
if (ndata == 0) return;
|
||||||
engineutil::DPrintHook(data, ndata);
|
DPrint(data, ndata);
|
||||||
BIO_reset(TraceBIO);
|
BIO_reset(TraceBIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,37 +1,150 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "engineutil.hpp"
|
#include "engineutil.hpp"
|
||||||
|
|
||||||
namespace engineutil {
|
using DPrintCallback = DebugPrintControl::DPrintCallback;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// DPrintState
|
||||||
|
//
|
||||||
|
// The global state of the DPrint routine. There is only
|
||||||
|
// ever one of these, and it is owned by DPrintAccess below.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
struct DPrintState {
|
||||||
|
// True if buffering is enabled.
|
||||||
|
bool Collect;
|
||||||
|
|
||||||
|
// The array of buffered messages.
|
||||||
|
TArray<FString> Messages;
|
||||||
|
|
||||||
|
// The array of callback functions.
|
||||||
|
TArray<DPrintCallback> Callbacks;
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// DPrintAccess
|
||||||
|
//
|
||||||
|
// This class grants you safe access to the global state
|
||||||
|
// of the DPrint routine. Constructing an object of
|
||||||
|
// class DPrintAccess will lock the global mutex and make
|
||||||
|
// sure that the DPrintState is initialized. Then it will
|
||||||
|
// give you unrestricted access to the DPrintState through
|
||||||
|
// operator right arrow.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class DPrintAccess {
|
||||||
|
private:
|
||||||
|
// The Mutex that protects the global state.
|
||||||
|
static FCriticalSection Mutex;
|
||||||
|
|
||||||
|
// The Global State. A pointer, to avoid static init issues.
|
||||||
|
static DPrintState* State;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructor. Locks mutex and initializes state if necessary.
|
||||||
|
DPrintAccess() {
|
||||||
|
Mutex.Lock();
|
||||||
|
if (State == nullptr) {
|
||||||
|
State = new DPrintState;
|
||||||
|
State->Collect = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor. Releases mutex.
|
||||||
|
~DPrintAccess() {
|
||||||
|
Mutex.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access the DPrintState.
|
||||||
|
DPrintState* operator ->() {
|
||||||
|
return State;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FCriticalSection DPrintAccess::Mutex;
|
||||||
|
DPrintState* DPrintAccess::State;
|
||||||
|
|
||||||
|
|
||||||
// The DPrint array. This stores the dprints
|
//////////////////////////////////////////////////////////////
|
||||||
// until they can be collected by the console implementation.
|
//
|
||||||
static TArray<FString> dprint_array;
|
// Namespace DebugPrint.
|
||||||
static FCriticalSection dprint_mutex;
|
//
|
||||||
|
// This contains all the various versions of the DPrint routine.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void DPrint(const FString& fs) {
|
namespace DebugPrint {
|
||||||
FScopeLock lk(&dprint_mutex);
|
|
||||||
dprint_array.Emplace(fs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DPrint(const char* msg) {
|
// DPrint. Invoke all the callbacks, and store the message.
|
||||||
FScopeLock lk(&dprint_mutex);
|
void DPrint(const FString& fs) {
|
||||||
dprint_array.Emplace(msg);
|
DPrintAccess state;
|
||||||
}
|
for (DPrintCallback cb : state->Callbacks) {
|
||||||
|
cb(fs);
|
||||||
|
}
|
||||||
|
if (state->Collect) {
|
||||||
|
state->Messages.Emplace(fs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DPrintHook(const char* msg, size_t len) {
|
// Alternative interface to the dispatcher.
|
||||||
FScopeLock lk(&dprint_mutex);
|
void DPrint(const char* msg) {
|
||||||
dprint_array.Emplace(len, (const UTF8CHAR*)msg);
|
DPrint(FString(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<FString> DPrintGetStored() {
|
// Alternative interface to the dispatcher.
|
||||||
FScopeLock lk(&dprint_mutex);
|
void DPrint(const char* msg, size_t len) {
|
||||||
TArray<FString> result = std::move(dprint_array);
|
DPrint(FString(len, (const UTF8CHAR*)msg));
|
||||||
dprint_array.Empty();
|
}
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConsoleOutput::Append(const FString& text) {
|
} // namespace DebugPrint
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Namespace DebugPrintControl.
|
||||||
|
//
|
||||||
|
// Configuration and control for the DPrint routine.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
namespace DebugPrintControl {
|
||||||
|
|
||||||
|
// Register a callback. Check for duplication.
|
||||||
|
void RegisterCallback(DPrintCallback cb) {
|
||||||
|
DPrintAccess state;
|
||||||
|
for (DPrintCallback old : state->Callbacks) {
|
||||||
|
if (cb == old) return;
|
||||||
|
}
|
||||||
|
state->Callbacks.Add(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the collection bit in the global state.
|
||||||
|
void EnableCollection() {
|
||||||
|
DPrintAccess state;
|
||||||
|
state->Collect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the array of collected messages, if any.
|
||||||
|
TArray<FString> GetStored() {
|
||||||
|
DPrintAccess state;
|
||||||
|
TArray<FString> result = std::move(state->Messages);
|
||||||
|
state->Messages.Empty();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// ConsoleOutput
|
||||||
|
//
|
||||||
|
// Storing the text that goes in the unreal console.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void FConsoleOutput::Append(const FString& text) {
|
||||||
if (!text.IsEmpty()) {
|
if (!text.IsEmpty()) {
|
||||||
Content += text;
|
Content += text;
|
||||||
Truncate();
|
Truncate();
|
||||||
@@ -39,7 +152,7 @@ void ConsoleOutput::Append(const FString& text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleOutput::AppendLine(const FString& text) {
|
void FConsoleOutput::AppendLine(const FString& text) {
|
||||||
int csize = Content.Len();
|
int csize = Content.Len();
|
||||||
if ((csize > 0) && (Content[csize - 1] != '\n')) {
|
if ((csize > 0) && (Content[csize - 1] != '\n')) {
|
||||||
Content += TEXT("\n");
|
Content += TEXT("\n");
|
||||||
@@ -50,7 +163,7 @@ void ConsoleOutput::AppendLine(const FString& text) {
|
|||||||
Dirty = true;
|
Dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleOutput::Truncate() {
|
void FConsoleOutput::Truncate() {
|
||||||
int lines = 50;
|
int lines = 50;
|
||||||
int csize = Content.Len();
|
int csize = Content.Len();
|
||||||
int total = 0;
|
int total = 0;
|
||||||
@@ -65,5 +178,3 @@ void ConsoleOutput::Truncate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace engineutil
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,119 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "enginewrapper.hpp"
|
//////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Namespace DebugPrint
|
||||||
|
//
|
||||||
|
// This namespace contains the function "DPrint",
|
||||||
|
// which is short for debugging print. This
|
||||||
|
// is what you should use if you want to print
|
||||||
|
// a debugging message.
|
||||||
|
//
|
||||||
|
// Messages printed using "DPrint" will
|
||||||
|
// be routed to several destinations. These
|
||||||
|
// might include a logfile, the unreal console,
|
||||||
|
// and the visual studio debugging log.
|
||||||
|
//
|
||||||
|
// In order for "DPrint" to work, you have to
|
||||||
|
// configure them once, at program initialization
|
||||||
|
// time. This is done using the functions in
|
||||||
|
// namespace DebugPrintConfig, below.
|
||||||
|
//
|
||||||
|
// We promise that namespace DebugPrint will
|
||||||
|
// only ever contain one function, 'DPrint.'
|
||||||
|
// that way, it's safe for you to put
|
||||||
|
// 'using namespace DebugPrint' in your code.
|
||||||
|
//
|
||||||
|
// All DPrint-related functionality is
|
||||||
|
// thread-safe. A single 'DPrint' is meant to
|
||||||
|
// be output atomically.
|
||||||
|
//
|
||||||
|
// It is fine for the content of the DPrint
|
||||||
|
// string to contain newlines.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace engineutil {
|
namespace DebugPrint {
|
||||||
// Load the DLL and initialize the wrapper, if possible.
|
// Print text into the debug log.
|
||||||
void init_wrapper(EngineWrapper* w);
|
void DPrint(const FString& fs);
|
||||||
|
|
||||||
// Print text on the console.
|
// Print text into the debug log.
|
||||||
void DPrint(const FString& fs);
|
void DPrint(const char* msg);
|
||||||
|
|
||||||
// Print text on the console.
|
// Print text into the debug log.
|
||||||
void DPrint(const char* msg);
|
void DPrint(const char* msg, size_t len);
|
||||||
|
};
|
||||||
|
|
||||||
// Print text on the console.
|
//////////////////////////////////////////////////////////////
|
||||||
void DPrintHook(const char* msg, size_t len);
|
//
|
||||||
|
// namespace DebugPrintControl
|
||||||
|
//
|
||||||
|
// This namespace contains the functions
|
||||||
|
// necessary to initialize "DPrint", and
|
||||||
|
// route its output to several destinations.
|
||||||
|
//
|
||||||
|
// "DPrint" is a callback-based design.
|
||||||
|
// Initialization might consist of: register a
|
||||||
|
// callback that sends a string to a logfile,
|
||||||
|
// register another callback that sends a string
|
||||||
|
// to visual studio's debug console, and register
|
||||||
|
// a third one that sends a string to the
|
||||||
|
// unreal console. Each time somebody calls
|
||||||
|
// 'DPrint', all three callbacks will get invoked.
|
||||||
|
//
|
||||||
|
// There is also a 'collect' option where you
|
||||||
|
// can ask "DPrint" to save the messages in a
|
||||||
|
// buffer, which you can then collect at
|
||||||
|
// your leisure. You can use buffering and
|
||||||
|
// callbacks at the same time. Note that
|
||||||
|
// buffering is inherently less than ideal for
|
||||||
|
// messages that warn of imminent program aborts.
|
||||||
|
// So it is recommended that you use at least one
|
||||||
|
// callback that sends its output without delay.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Get all the stored dprints.
|
namespace DebugPrintControl {
|
||||||
TArray<FString> DPrintGetStored();
|
// The prototype for a callback function.
|
||||||
|
//
|
||||||
|
using DPrintCallback = void (*)(const FString& fs);
|
||||||
|
|
||||||
class ConsoleOutput {
|
// Register a callback.
|
||||||
|
//
|
||||||
|
// Registering a callback that is already
|
||||||
|
// registered is a no-op.
|
||||||
|
//
|
||||||
|
void RegisterCallback(DPrintCallback f);
|
||||||
|
|
||||||
|
// Enable collection in a buffer.
|
||||||
|
//
|
||||||
|
// If collection is already enabled, this is a no-op.
|
||||||
|
//
|
||||||
|
void EnableCollection();
|
||||||
|
|
||||||
|
// Collect all the stored messages, and clear the storage.
|
||||||
|
//
|
||||||
|
TArray<FString> GetStored();
|
||||||
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// ConsoleOutput
|
||||||
|
//
|
||||||
|
// This class stores the text that's in the unreal console.
|
||||||
|
// It stores it as one great big string, which contains
|
||||||
|
// newlines to denote line breaks.
|
||||||
|
//
|
||||||
|
// This class also contains a 'dirty' bit. Each time somebody
|
||||||
|
// appends a line of text to the console, the dirty bit is
|
||||||
|
// automatically set. The bit can be checked using 'IsDirty'
|
||||||
|
// and cleared using 'ClearDirty'. This makes it so that
|
||||||
|
// you don't have to update the unreal widget unless the
|
||||||
|
// text has actually changed.
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class FConsoleOutput {
|
||||||
private:
|
private:
|
||||||
FString Content;
|
FString Content;
|
||||||
bool Dirty;
|
bool Dirty;
|
||||||
@@ -44,5 +139,3 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace engineutil
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user