diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..f92752799a4770d4eafcd885a835bdd745f7b2ce --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,94 @@ +#------------------------------------------------------------------------------- +# Copyright (c) 2020 RWTH Aachen University, Germany, +# Virtual Reality & Immersive Visualisation Group. +#------------------------------------------------------------------------------- + +# The include file can be change to either be removed or reference a specific commit. + +include: + - project: 'vr-group/unreal-development/unreal-ci' + ref: master + file: '/shared_scripts.yml' + +# In this file you are able to configure your plugins pipeline. +# If you want to customize something, either overwrite things that are defined in the shared_scripts repository, +# or remove the "extends" and write your own scripts +# +# If you want your pipeline to run on every commit, just remove the "only" blocks. Keep in mind, that a build +# can take some time. +# +# If you want to alter the unreal-building process two variables are defined for either changing the CLIENT_CONFIG or +# for adding EXTRA_ARGS to the building process +# +# For the generate stage, you can specify needed dependencies in GEN_DEPENDENCIES with [Branch@PluginFolder] as key +# Example: +# +# Generate_Project: +# only: ['web', 'schedules'] +# extends: .Generate_Project_ +# variables: +# GEN_TEMPLATE_REPO: "https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unrealprojecttemplate.git" +# GEN_TEMPLATE_BRANCH: "develop" +# GEN_DEPENDENCIES: "( +# [master@nDisplayExtensions]='https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/ndisplayextensions.git' +# [master@CaveOverlay]='https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unreal-cave-overlay.git' +# )" +# +# You can uncomment the deploy lines to deploy your project to the CAVE/VRDev. This only makes sense, if your plugin works +# with a generated project. + +stages: + - generate + - build +# - deploy + +Generate_Project: + only: ['web', 'schedules'] + extends: .Generate_Project_ + variables: + GEN_TEMPLATE_REPO: "https://devhub.vr.rwth-aachen.de/VR-Group/unreal-development/unrealprojecttemplate.git" + GEN_TEMPLATE_BRANCH: "4.26" + GEN_DEPENDENCIES: "( + )" + +Build_Windows: + only: ['web', 'schedules'] + extends: .Build_Windows_ + tags: + - windows + - unreal-4.26 + variables: + GIT_STRATEGY: none + GIT_CHECKOUT: "false" + CLIENT_CONFIG: "DebugGame" + needs: + - job: "Generate_Project" + artifacts: true + +Build_CentOS: + only: ['web', 'schedules'] + extends: .Build_CentOS_ + tags: + - centos + - unreal-4.26 + variables: + GIT_STRATEGY: none + GIT_CHECKOUT: "false" + CLIENT_CONFIG: "DebugGame" + needs: + - job: "Generate_Project" + artifacts: true + +#Deploy_CAVE: +# only: ['web', 'schedules'] +# extends: .Deploy_CAVE_ +# needs: +# - job: "Build_CentOS" +# artifacts: true +# +#Deploy_Windows: +# only: ['web', 'schedules'] +# extends: .Deploy_VRDev_ +# needs: +# - job: "Build_Windows" +# artifacts: true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c2eb444a7edecc06466b760dd3e0d2f287541903 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2020, Virtual Reality & Immersive Visualization Group at RWTH Aachen University +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index 8ba48588afa37ae9f7b9fcbf8f044ae2db776195..8d88972fd33dd65e8a7f4576e94c7cdbf01d1113 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,16 @@ UniLog.Log("Message", "StreamName"); UniLog.LogF("StreamName", "Message %s", *variable); ``` +### Send Multiple LogStreams to the Same File + +You can simply specify the same file name and file path in multiple LogStreams. The output of both streams will be written to this file. This can be useful to have separate streams for errors or warnings. To distinguish the streams, you can use + +```cpp +stream->SetPrefix("Warning: "); // No additional space will be added after the prefix, so make sure to add one if you want that +``` + +to set a prefix that will automatically be added to every message that is written to the file (this will not show up in the on screen log). + ### On Screen Logging You can also display the log messages on screen. This can even be enabled/disabled during runtime. To do this, use the ILogStream interface: diff --git a/Source/UniversalLogging/Private/LogFileManager.cpp b/Source/UniversalLogging/Private/LogFileManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d5f085bc8fcf81db5868e466bc389a33196fe4f4 --- /dev/null +++ b/Source/UniversalLogging/Private/LogFileManager.cpp @@ -0,0 +1,19 @@ +#include "LogFileManager.h" + +LogFileStream* LogFileManager::GetLogFileStream(FString FilePath, FString FileName) +{ + FString Full_Path = FPaths::Combine(FilePath, FileName); + if (Streams.Contains(Full_Path)) + return Streams[Full_Path].Get(); + else + Streams.Add(Full_Path, MakeUnique<LogFileStream>(FilePath, FileName)); + return Streams[Full_Path].Get(); +} + +LogFileManager::LogFileManager() +{ +} + +LogFileManager::~LogFileManager() +{ +} diff --git a/Source/UniversalLogging/Private/LogFileManager.h b/Source/UniversalLogging/Private/LogFileManager.h new file mode 100644 index 0000000000000000000000000000000000000000..d339d83b1343de28f0589a7c10a265d5ac53853f --- /dev/null +++ b/Source/UniversalLogging/Private/LogFileManager.h @@ -0,0 +1,17 @@ +#pragma once + +#include "LogFileStream.h" + +class LogFileManager +{ +public: + LogFileStream* GetLogFileStream(FString FilePath, FString FileName); + +private: + friend class UniversalLoggingImpl; + LogFileManager(); + virtual ~LogFileManager(); + +private: + TMap<FString, TUniquePtr<LogFileStream>> Streams; +}; \ No newline at end of file diff --git a/Source/UniversalLogging/Private/LogFileStream.cpp b/Source/UniversalLogging/Private/LogFileStream.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb48bf97636ad3127949a6d67887abca30d12b9d --- /dev/null +++ b/Source/UniversalLogging/Private/LogFileStream.cpp @@ -0,0 +1,53 @@ +#include "LogFileStream.h" + +#include "HAL/PlatformFilemanager.h" +#include "LogStream.h" +#include "Misc/Paths.h" + +LogFileStream::LogFileStream(const FString Filepath, const FString Filename) + : Filepath(Filepath) + , Filename(Filename) + , bIs_Open(false) + , File_Handle(nullptr) +{ +} + +LogFileStream::~LogFileStream() +{ + Close(); +} + +void LogFileStream::Open() +{ + IPlatformFile& platform_file = FPlatformFileManager::Get().GetPlatformFile(); + FString file_path = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir() + Filepath); + platform_file.CreateDirectoryTree(*file_path); + file_path = FPaths::Combine(file_path, Filename); + File_Handle.Reset(platform_file.OpenWrite(*file_path)); + if (!File_Handle) + { + UE_LOG(LogUni, Error, TEXT("Universal Logging: The file %s could not be opened for writing."), *file_path); + bIs_Open = false; + return; + } + bIs_Open = true; +} + +void LogFileStream::Close() +{ + File_Handle.Reset(); + bIs_Open = false; +} + +bool LogFileStream::GetIsOpen() const +{ + return bIs_Open; +} + +void LogFileStream::Write(const FString Text) +{ + if (!bIs_Open) + Open(); + const FTCHARToUTF8 StringUTF8(*Text); + File_Handle->Write(reinterpret_cast<const uint8*>(StringUTF8.Get()), StringUTF8.Length()); +} diff --git a/Source/UniversalLogging/Private/LogFileStream.h b/Source/UniversalLogging/Private/LogFileStream.h new file mode 100644 index 0000000000000000000000000000000000000000..d75f5c65b159550a8d1e32703be8bdc271f4f595 --- /dev/null +++ b/Source/UniversalLogging/Private/LogFileStream.h @@ -0,0 +1,19 @@ +#pragma once + +class LogFileStream +{ +public: + LogFileStream(const FString Filepath, const FString Filename); + virtual ~LogFileStream(); + + void Open(); + void Close(); + bool GetIsOpen() const; + void Write(const FString Text); + +private: + const FString Filepath; + const FString Filename; + bool bIs_Open; + TUniquePtr <IFileHandle> File_Handle; +}; \ No newline at end of file diff --git a/Source/UniversalLogging/Private/LogStream.cpp b/Source/UniversalLogging/Private/LogStream.cpp index 23a1778f79187321a5e753ac7fe645241f2f4c5e..a777b7b724c6320d790700e68c6ebbfa0c00fa55 100644 --- a/Source/UniversalLogging/Private/LogStream.cpp +++ b/Source/UniversalLogging/Private/LogStream.cpp @@ -1,30 +1,32 @@ -#include "UniversalLoggingPrivatePCH.h" - #include "LogStream.h" #include "UniversalLogging.h" -#include "HAL/PlatformFilemanager.h" #include "Misc/Paths.h" -#include "HAL/IPlatformFileProfilerWrapper.h" + +DEFINE_LOG_CATEGORY(LogUni); + LogStreamImpl::LogStreamImpl(const FString Filepath, const FString Filename, const bool bPer_Session, const bool bLogOnMaster, const bool bLogOnSlaves) : Filepath(Filepath) , Filename(Filename) , bPer_Session(bPer_Session) , bOnScreen(false) - , OnScreenColor(0, 0, 255, 255) + , OnScreenColor(255, 255, 255, 255) + , OnScreenBackgroundColor(0, 0, 0, 128) + , OnScreenSize(1.0) + , OnScreenDuration(5.0) + , bLogToDefaultLog(false) , bLogOnMaster(bLogOnMaster) , bLogOnSlaves(bLogOnSlaves) , bLogOnScreenOnMaster(true) , bLogOnScreenOnSlaves(false) - , bIs_Open(false) , bIs_Valid(false) - , File_Handle(nullptr) + , Log_File_Stream(nullptr) { if ((bLogOnMaster && UniversalLoggingImpl::IsClusterMaster()) || (bLogOnSlaves && !UniversalLoggingImpl::IsClusterMaster())) { Open(); - if (bIs_Open) + if (GetIsOpen()) bIs_Valid = true; Close(); } @@ -49,6 +51,16 @@ FString LogStreamImpl::GetFilename() return Filename; } +void LogStreamImpl::SetPrefix(FString Prefix) +{ + MessagePrefix = Prefix; +} + +FString LogStreamImpl::GetPrefix() const +{ + return MessagePrefix; +} + void LogStreamImpl::SetOnScreen(const bool Val) { bOnScreen = Val; @@ -69,6 +81,46 @@ FColor LogStreamImpl::GetOnScreenColor() const return OnScreenColor; } +void LogStreamImpl::SetOnScreenBackgroundColor(const FColor Color) +{ + OnScreenBackgroundColor = Color; +} + +FColor LogStreamImpl::GetOnScreenBackgroundColor() const +{ + return OnScreenBackgroundColor; +} + +void LogStreamImpl::SetOnScreenSize(const float Scale) +{ + OnScreenSize = Scale; +} + +float LogStreamImpl::GetOnScreenSize() const +{ + return OnScreenSize; +} + +void LogStreamImpl::SetOnScreenDuration(const float Seconds) +{ + OnScreenDuration = Seconds; +} + +float LogStreamImpl::GetOnScreenDuration() const +{ + return OnScreenDuration; +} + +void LogStreamImpl::SetLogToDefaultLog(const bool Val) +{ + bLogToDefaultLog = Val; +} + +bool LogStreamImpl::GetLogToDefaultLog() const +{ + return bLogToDefaultLog; +} + bool LogStreamImpl::GetLogOnMaster() const { return bLogOnMaster; @@ -81,6 +133,8 @@ bool LogStreamImpl::GetLogOnSlaves() const void LogStreamImpl::SetLogOnScreenOnMaster(const bool Val) { + if(Val) // to avoid confusion, this also enables logging + SetOnScreen(true); bLogOnScreenOnMaster = Val; } @@ -91,6 +145,8 @@ bool LogStreamImpl::GetLogOnScreenOnMaster() const void LogStreamImpl::SetLogOnScreenOnSlaves(const bool Val) { + if (Val) // to avoid confusion, this also enables logging + SetOnScreen(true); bLogOnScreenOnSlaves = Val; } @@ -106,40 +162,33 @@ bool LogStreamImpl::GetIsValid() void LogStreamImpl::Open() { - IPlatformFile& platform_file = FPlatformFileManager::Get().GetPlatformFile(); - FString file_path = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir() + Filepath); + FString File_Path = Filepath; if(bPer_Session) - file_path += "/" + UniLog.GetSessionIdentifier(); - platform_file.CreateDirectoryTree(*file_path); - file_path += "/" + Filename; - File_Handle = platform_file.OpenWrite(*file_path); - if (!File_Handle) + File_Path = FPaths::Combine(File_Path, UniLog.GetSessionIdentifier()); + Log_File_Stream = UniversalLoggingImpl::GetLogFileManager()->GetLogFileStream(File_Path, Filename); // this will not stay like this, as LogFileStreams will be managed by LogFileManager + Log_File_Stream->Open(); + if (!Log_File_Stream->GetIsOpen()) { - UE_LOG(LogTemp, Error, TEXT("Universal Logging: The file %s could not be opened for writing."), *file_path); - bIs_Open = false; bIs_Valid = false; return; } - bIs_Open = true; } void LogStreamImpl::Close() { - delete File_Handle; - File_Handle = nullptr; - bIs_Open = false; + Log_File_Stream->Close(); } bool LogStreamImpl::GetIsOpen() const { - return bIs_Open; + return Log_File_Stream->GetIsOpen(); } void LogStreamImpl::Write(const FString Text) { if (!bIs_Valid) return; - if (!bIs_Open) + if (!Log_File_Stream->GetIsOpen()) Open(); - File_Handle->Write(reinterpret_cast<const uint8*>(TCHAR_TO_ANSI(*Text)), Text.Len()); + Log_File_Stream->Write(MessagePrefix + Text); } \ No newline at end of file diff --git a/Source/UniversalLogging/Private/LogStream.h b/Source/UniversalLogging/Private/LogStream.h index 7c250a2e3523a634e6c41afc5d067fa275e20853..54969aba8b14651970c4a985a19cd22a9f582462 100644 --- a/Source/UniversalLogging/Private/LogStream.h +++ b/Source/UniversalLogging/Private/LogStream.h @@ -1,6 +1,13 @@ #pragma once + +#include "ILogStream.h" + +#include "LogFileStream.h" + #include "Math/Color.h" +DECLARE_LOG_CATEGORY_EXTERN(LogUni, Log, All); + class LogStreamImpl : public ILogStream { public: @@ -11,10 +18,22 @@ public: FString GetFilepath() override; FString GetFilename() override; + void SetPrefix(FString Prefix) override; + FString GetPrefix() const override; + void SetOnScreen(const bool Val) override; bool GetOnScreen() const override; void SetOnScreenColor(const FColor Color) override; FColor GetOnScreenColor() const override; + void SetOnScreenBackgroundColor(const FColor Color) override; + FColor GetOnScreenBackgroundColor() const override; + void SetOnScreenSize(const float Scale) override; + float GetOnScreenSize() const override; + void SetOnScreenDuration(const float Seconds) override; + float GetOnScreenDuration() const override; + + void SetLogToDefaultLog(const bool Val) override; + bool GetLogToDefaultLog() const override; bool GetLogOnMaster() const override; bool GetLogOnSlaves() const override; @@ -34,10 +53,16 @@ public: private: const FString Filepath; const FString Filename; + FString MessagePrefix; bool bPer_Session; bool bOnScreen; FColor OnScreenColor; + FColor OnScreenBackgroundColor; + float OnScreenSize; + float OnScreenDuration; + + bool bLogToDefaultLog; bool bLogOnMaster; bool bLogOnSlaves; @@ -45,8 +70,7 @@ private: bool bLogOnScreenOnMaster; bool bLogOnScreenOnSlaves; - bool bIs_Open; bool bIs_Valid; - IFileHandle* File_Handle; + LogFileStream* Log_File_Stream; }; diff --git a/Source/UniversalLogging/Private/OnScreenLog.cpp b/Source/UniversalLogging/Private/OnScreenLog.cpp index 3a20a1e3bfdeaf866bf280d97dfdc4f58439328f..249344f076a0955c05c9f937a668f118f2075345 100644 --- a/Source/UniversalLogging/Private/OnScreenLog.cpp +++ b/Source/UniversalLogging/Private/OnScreenLog.cpp @@ -12,12 +12,15 @@ AOnScreenLog::AOnScreenLog() } -void AOnScreenLog::EnqueueMessage(const FString Text, const FColor Color) +void AOnScreenLog::EnqueueMessage(const FString Text, const FColor Color, const FColor BackgroundColor, const float Scale, const float Duration) { FMessage Message; Message.Text = Text; Message.Color = Color; - Message.TimeToLive = 5.0; + Message.BackgroundColor = BackgroundColor; + Message.AlphaFactor = 1.0; + Message.Scale = Scale; + Message.TimeToLive = Duration; Message_Queue.Insert(Message, 0); } @@ -32,18 +35,17 @@ void AOnScreenLog::BeginPlay() void AOnScreenLog::Tick(float DeltaTime) { Super::Tick(DeltaTime); - for (int i = 0; i < Message_Queue.Num(); i++) + for (int i = Message_Queue.Num(); i--;) { Message_Queue[i].TimeToLive -= DeltaTime; if(Message_Queue[i].TimeToLive < 1.0) // Fade out { - Message_Queue[i].Color.A = Message_Queue[i].TimeToLive * 255; + Message_Queue[i].AlphaFactor = Message_Queue[i].TimeToLive; + } + if(Message_Queue[i].TimeToLive <= 0.0) // Remove (works because array is traversed backwards) + { + Message_Queue.RemoveAt(i); } - } - FMessage LastMessage; - while (Message_Queue.Num() > 0 && Message_Queue.Top().TimeToLive <= 0) - { - Message_Queue.Pop(); } } @@ -52,8 +54,20 @@ void AOnScreenLog::PostRenderFor(APlayerController* PC, UCanvas* Canvas, FVector float YOffset = 50; for (auto Message : Message_Queue) { - Canvas->DrawColor = Message.Color; - YOffset += Canvas->DrawText(myFont.Object, Message.Text, 10, YOffset); + int Height; + int Width; + myFont.Object->GetStringHeightAndWidth(Message.Text, Height, Width); + FVector2D BoxPosition(10, YOffset); + FVector2D BoxSize(Width * Message.Scale, Height * Message.Scale); + FLinearColor BoxColor = FLinearColor(Message.BackgroundColor); + BoxColor.A *= Message.AlphaFactor; + FCanvasTileItem Box(BoxPosition, BoxSize, BoxColor); + Box.BlendMode = ESimpleElementBlendMode::SE_BLEND_Translucent; + Canvas->DrawItem(Box); + FColor TextColor = Message.Color; + TextColor.A *= Message.AlphaFactor; + Canvas->DrawColor = TextColor; + YOffset += Canvas->DrawText(myFont.Object, Message.Text, 10, YOffset, Message.Scale, Message.Scale); } } diff --git a/Source/UniversalLogging/Private/OnScreenLog.h b/Source/UniversalLogging/Private/OnScreenLog.h index 77ce3142caac06696d028cf66e58fec6406ac040..b0b6d9556f3412dfc93d94628cc0dbb1570538d5 100644 --- a/Source/UniversalLogging/Private/OnScreenLog.h +++ b/Source/UniversalLogging/Private/OnScreenLog.h @@ -19,7 +19,7 @@ public: // Sets default values for this actor's properties AOnScreenLog(); - void EnqueueMessage(const FString Text, const FColor Color); + void EnqueueMessage(const FString Text, const FColor Color, const FColor BackgroundColor, const float Scale, const float Duration); protected: // Called when the game starts or when spawned @@ -35,6 +35,9 @@ private: { FString Text; FColor Color; + FColor BackgroundColor; + float AlphaFactor; + float Scale; float TimeToLive; }; TArray<FMessage> Message_Queue; diff --git a/Source/UniversalLogging/Private/UniLogBlueprintFunctionLibrary.cpp b/Source/UniversalLogging/Private/UniLogBlueprintFunctionLibrary.cpp index f0ab149571adde48b7d88c3297f8848f3882f030..da7f1f9aebe6aee9b6d4794ac9a881e89bfdd98c 100644 --- a/Source/UniversalLogging/Private/UniLogBlueprintFunctionLibrary.cpp +++ b/Source/UniversalLogging/Private/UniLogBlueprintFunctionLibrary.cpp @@ -26,26 +26,39 @@ FString UUniLogBlueprintFunctionLibrary::GetSessionIdentifier() } void UUniLogBlueprintFunctionLibrary::NewLogStream(const FString StreamName, const FString Filepath, - const FString Filename, bool bPer_Session, bool bOnScreen/* = false*/, + const FString Filename, FString Prefix, bool bPer_Session, bool bOnScreen/* = false*/, FColor OnScreenColor/* = FColor(0, 0, 255, 0)*/, - bool bLogOnMaster/* = true*/, bool bLogOnSlaves/* = false*/, + FColor OnScreenBackgroundColor, float OnScreenSize, + float OnScreenDuration, + bool bLogToDefaultLog, bool bLogOnMaster/* = true*/, + bool bLogOnSlaves/* = false*/, bool bLogOnScreenOnMaster/* = true*/, bool bLogOnScreenOnSlaves/* = false*/) { auto LogStream = UniLog.NewLogStream(StreamName, Filepath, Filename, bPer_Session, bLogOnMaster, bLogOnSlaves); + LogStream->SetPrefix(Prefix); LogStream->SetOnScreen(bOnScreen); LogStream->SetOnScreenColor(OnScreenColor); + LogStream->SetOnScreenBackgroundColor(OnScreenBackgroundColor); + LogStream->SetOnScreenSize(OnScreenSize); + LogStream->SetOnScreenDuration(OnScreenDuration); + LogStream->SetLogToDefaultLog(bLogToDefaultLog); LogStream->SetLogOnScreenOnMaster(bLogOnScreenOnMaster); LogStream->SetLogOnScreenOnSlaves(bLogOnScreenOnSlaves); } void UUniLogBlueprintFunctionLibrary::ModifyLogStream(const FString StreamName, bool bOnScreen, FColor OnScreenColor, - bool bLogOnScreenOnMaster/* = true*/, + FColor OnScreenBackgroundColor, + float OnScreenSize, float OnScreenDuration, bool bLogToDefaultLog, bool bLogOnScreenOnMaster/* = true*/, bool bLogOnScreenOnSlaves/* = false*/) { auto LogStream = UniLog.GetLogStream(StreamName); LogStream->SetOnScreen(bOnScreen); LogStream->SetOnScreenColor(OnScreenColor); + LogStream->SetOnScreenBackgroundColor(OnScreenBackgroundColor); + LogStream->SetOnScreenSize(OnScreenSize); + LogStream->SetOnScreenDuration(OnScreenDuration); + LogStream->SetLogToDefaultLog(bLogToDefaultLog); LogStream->SetLogOnScreenOnMaster(bLogOnScreenOnMaster); LogStream->SetLogOnScreenOnSlaves(bLogOnScreenOnSlaves); } diff --git a/Source/UniversalLogging/Private/UniversalLogging.cpp b/Source/UniversalLogging/Private/UniversalLogging.cpp index 5f2fbce9b3c331fd800bf0b4c37a83f5f57f6c85..641e190493d076cda1299fbce851fcdbc10def6c 100644 --- a/Source/UniversalLogging/Private/UniversalLogging.cpp +++ b/Source/UniversalLogging/Private/UniversalLogging.cpp @@ -1,53 +1,64 @@ #include "UniversalLogging.h" -#include "UniversalLoggingPrivatePCH.h" - - #include "LogStream.h" #include "GameFramework/PlayerController.h" #include "GameFramework/HUD.h" +#if PLATFORM_WINDOWS || PLATFORM_LINUX #include "IDisplayCluster.h" #include "Cluster/IDisplayClusterClusterManager.h" +#endif +#if WITH_EDITOR +#include "Editor.h" +#endif #include "Misc/CommandLine.h" +LogFileManager UniversalLoggingImpl::Log_File_Manager{}; + void UniversalLoggingImpl::StartupModule() { Streams.Add("", MakeUnique<LogStreamImpl>()); - On_Post_World_Initialization_Delegate.BindRaw(this, &UniversalLoggingImpl::OnSessionStart); - FWorldDelegates::OnPostWorldInitialization.Add(On_Post_World_Initialization_Delegate); + FWorldDelegates::OnPostWorldInitialization.AddRaw(this, &UniversalLoggingImpl::OnWorldStart); + FWorldDelegates::OnWorldPostActorTick.AddRaw(this, &UniversalLoggingImpl::OnPostActorTick); - On_Pre_World_Finish_Destroy_Delegate.BindRaw(this, &UniversalLoggingImpl::OnSessionEnd); - FWorldDelegates::OnPreWorldFinishDestroy.Add(On_Pre_World_Finish_Destroy_Delegate); +#if WITH_EDITOR + FEditorDelegates::BeginPIE.AddRaw(this, &UniversalLoggingImpl::OnSessionStart); + FEditorDelegates::EndPIE.AddRaw(this, &UniversalLoggingImpl::OnSessionEnd); +#endif - On_World_Post_Actor_Tick_Delegate.BindRaw(this, &UniversalLoggingImpl::OnPostActorTick); - FWorldDelegates::OnWorldPostActorTick.Add(On_World_Post_Actor_Tick_Delegate); - - Session_ID = ""; + Session_ID = ""; } void UniversalLoggingImpl::ShutdownModule() { } -void UniversalLoggingImpl::OnSessionStart(UWorld* World, const UWorld::InitializationValues) +void UniversalLoggingImpl::OnWorldStart(UWorld* World, const UWorld::InitializationValues) { if (!World->IsGameWorld()) return; - if (World->IsPlayInEditor()) + + On_Screen_Log_Actor = dynamic_cast<AOnScreenLog*>(World->SpawnActor(AOnScreenLog::StaticClass())); + + //only set Session_ID on the first world of this session + if(Session_ID != "") + return; + + if (World->IsPlayInEditor()) ResetSessionId("PlayInEditor"); else if (World->IsPlayInPreview()) ResetSessionId("PlayInPreview"); else ResetSessionId("Play"); +} - On_Screen_Log_Actor = dynamic_cast<AOnScreenLog*>(World->SpawnActor(AOnScreenLog::StaticClass())); +void UniversalLoggingImpl::OnSessionStart(const bool) +{ + Session_ID = ""; } -void UniversalLoggingImpl::OnSessionEnd(UWorld* World) +void UniversalLoggingImpl::OnSessionEnd(const bool) { - if (!World->IsGameWorld()) - return; ResetSessionId("Stopped"); for (auto& Elem : Streams) @@ -62,8 +73,11 @@ void UniversalLoggingImpl::OnPostActorTick(UWorld* World, ELevelTick LevelTick, if (!PlayerController) return; auto HUD = PlayerController->GetHUD(); - HUD->AddPostRenderedActor(On_Screen_Log_Actor); // Doing this every tick seems excessive (AddPostRenderActor checks for duplicates, though) - HUD->bShowOverlays = true; // Yuck, but necessary as otherwise the Actor's PostRenderFor method is not called. + if (HUD) + { + HUD->AddPostRenderedActor(On_Screen_Log_Actor); // Doing this every tick seems excessive (AddPostRenderActor checks for duplicates, though) + HUD->bShowOverlays = true; // Yuck, but necessary as otherwise the Actor's PostRenderFor method is not called. + } } ILogStream* UniversalLoggingImpl::NewLogStream(const FString StreamName) @@ -122,7 +136,14 @@ void UniversalLoggingImpl::Log(const FString Text, const FString Stream /*= ""*/ if(Stream_OBJ->GetOnScreen() && On_Screen_Log_Actor) { if ((Stream_OBJ->GetLogOnScreenOnMaster() && IsClusterMaster()) || (Stream_OBJ->GetLogOnScreenOnSlaves() && !IsClusterMaster())) - On_Screen_Log_Actor->EnqueueMessage(Full_Text, Stream_OBJ->GetOnScreenColor()); + On_Screen_Log_Actor->EnqueueMessage(Full_Text, Stream_OBJ->GetOnScreenColor(), + Stream_OBJ->GetOnScreenBackgroundColor(), Stream_OBJ->GetOnScreenSize(), + Stream_OBJ->GetOnScreenDuration()); + } + + if(Stream_OBJ->GetLogToDefaultLog()) + { + UE_LOG(LogUni, Log, TEXT("[%s] %s"), *Stream, *Full_Text); } } @@ -150,6 +171,7 @@ void UniversalLoggingImpl::ResetSessionId(FString Prefix) bool UniversalLoggingImpl::IsClusterMaster() { +#if PLATFORM_WINDOWS || PLATFORM_LINUX if (!IDisplayCluster::IsAvailable()) { return true; @@ -159,15 +181,27 @@ bool UniversalLoggingImpl::IsClusterMaster() { return true; // if we are not in cluster mode, we are always the master } - return Manager->IsMaster(); + return Manager->IsMaster() || !Manager->IsSlave(); +#else + return true; +#endif } FString UniversalLoggingImpl::GetNodeName() { +#if PLATFORM_WINDOWS || PLATFORM_LINUX if (IDisplayCluster::Get().GetOperationMode() == EDisplayClusterOperationMode::Cluster) return IDisplayCluster::Get().GetClusterMgr()->GetNodeId(); else return FString(TEXT("Localhost")); +#else + return FString(TEXT("Localhost")); +#endif +} + +LogFileManager* UniversalLoggingImpl::GetLogFileManager() +{ + return &Log_File_Manager; } IMPLEMENT_MODULE(UniversalLoggingImpl, UniversalLogging) diff --git a/Source/UniversalLogging/Private/UniversalLogging.h b/Source/UniversalLogging/Private/UniversalLogging.h index ce83479536c40a8cfc1764bd12a1ed533daf15a6..1f0c4bd59d71151a7e7ae4a7628c5295f3061e77 100644 --- a/Source/UniversalLogging/Private/UniversalLogging.h +++ b/Source/UniversalLogging/Private/UniversalLogging.h @@ -2,6 +2,8 @@ #include "LogStream.h" #include "OnScreenLog.h" +#include "IUniversalLogging.h" +#include "LogFileManager.h" #include "Engine/World.h" @@ -12,8 +14,9 @@ public: void StartupModule() override; void ShutdownModule() override; - void OnSessionStart(UWorld*, const UWorld::InitializationValues); - void OnSessionEnd(UWorld*); + void OnWorldStart(UWorld*, const UWorld::InitializationValues); + void OnSessionStart(const bool); + void OnSessionEnd(const bool); void OnPostActorTick(UWorld*, ELevelTick, float); @@ -31,11 +34,11 @@ public: static bool IsClusterMaster(); static FString GetNodeName(); + static LogFileManager* GetLogFileManager(); + private: + static LogFileManager Log_File_Manager; TMap<FString, TUniquePtr<LogStreamImpl>> Streams; FString Session_ID; - TBaseDelegate<void, UWorld*, const UWorld::InitializationValues> On_Post_World_Initialization_Delegate; - TBaseDelegate<void, UWorld*> On_Pre_World_Finish_Destroy_Delegate; - TBaseDelegate<void, UWorld*, ELevelTick, float> On_World_Post_Actor_Tick_Delegate; AOnScreenLog* On_Screen_Log_Actor; }; \ No newline at end of file diff --git a/Source/UniversalLogging/Public/ILogStream.h b/Source/UniversalLogging/Public/ILogStream.h index c7889e30da34a16ce642c8869b2915f54a25612a..49ef4d25ee7f7a764ea7569720728815d38f5c8c 100644 --- a/Source/UniversalLogging/Public/ILogStream.h +++ b/Source/UniversalLogging/Public/ILogStream.h @@ -7,10 +7,22 @@ public: virtual FString GetFilename() = 0; virtual bool GetIsValid() = 0; + virtual void SetPrefix(FString Prefix) = 0; + virtual FString GetPrefix() const = 0; + virtual void SetOnScreen(const bool Val) = 0; virtual bool GetOnScreen() const = 0; virtual void SetOnScreenColor(const FColor Color) = 0; virtual FColor GetOnScreenColor() const = 0; + virtual void SetOnScreenBackgroundColor(const FColor Color) = 0; + virtual FColor GetOnScreenBackgroundColor() const = 0; + virtual void SetOnScreenSize(const float Scale) = 0; + virtual float GetOnScreenSize() const = 0; + virtual void SetOnScreenDuration(const float Seconds) = 0; + virtual float GetOnScreenDuration() const = 0; + + virtual void SetLogToDefaultLog(const bool Val) = 0; + virtual bool GetLogToDefaultLog() const = 0; virtual bool GetLogOnMaster() const = 0; virtual bool GetLogOnSlaves() const = 0; diff --git a/Source/UniversalLogging/Public/IUniversalLogging.h b/Source/UniversalLogging/Public/IUniversalLogging.h index c6d052a3036284a17712e53cfa44ec7e1d8ed328..b76c7ec2ebc693c33a0700873d3e7c1d244ee217 100644 --- a/Source/UniversalLogging/Public/IUniversalLogging.h +++ b/Source/UniversalLogging/Public/IUniversalLogging.h @@ -1,6 +1,9 @@ #pragma once +#if PLATFORM_WINDOWS || PLATFORM_LINUX #include "IDisplayCluster.h" +#endif + #include "Modules/ModuleManager.h" #include "ILogStream.h" @@ -72,10 +75,12 @@ public: * Advanced logging with printf like syntax. * Note: Does not automatically add newline! */ - template<typename... TArgs> - void LogF(const FString Stream, const FString Format, TArgs&&... Args) + template<typename FmtType, typename... TArgs> + void LogF(const FString Stream, const FmtType& Format, TArgs&&... Args) { - const FString Out = FString::Printf(*Format, Args...); + static_assert(TIsArrayOrRefOfType<FmtType, TCHAR>::Value, "Format must be a TCHAR string literal, i.e., only ever call this function with TEXT(\"...\") as the second parameter."); + + const FString Out = FString::Printf(Format, Args...); Log(Out, Stream, true); } }; diff --git a/Source/UniversalLogging/Public/UniLogBlueprintFunctionLibrary.h b/Source/UniversalLogging/Public/UniLogBlueprintFunctionLibrary.h index 7889f9242409c53f148e6b62c8d9937e8440bafd..ce5d6e4dcaa57c820574343828c664436f45c299 100644 --- a/Source/UniversalLogging/Public/UniLogBlueprintFunctionLibrary.h +++ b/Source/UniversalLogging/Public/UniLogBlueprintFunctionLibrary.h @@ -23,13 +23,18 @@ class UUniLogBlueprintFunctionLibrary : public UBlueprintFunctionLibrary static FString GetSessionIdentifier(); UFUNCTION(BlueprintCallable, Category = "UniLog") - static void NewLogStream(const FString StreamName, const FString Filepath, const FString Filename, + static void NewLogStream(const FString StreamName, const FString Filepath, const FString Filename, FString Prefix, bool bPer_Session = false, bool bOnScreen = false, - FColor OnScreenColor = FColor(0, 0, 255, 0), bool bLogOnMaster = true, - bool bLogOnSlaves = false, bool bLogOnScreenOnMaster = true, bool bLogOnScreenOnSlaves = false); + FColor OnScreenColor = FColor(255, 255, 255, 255), + FColor OnScreenBackgroundColor = FColor(0, 0, 0, 128), + float OnScreenSize = 1.0, float OnScreenDuration = 5.0, bool bLogToDefaultLog = false, bool bLogOnMaster = true, + bool bLogOnSlaves = false, bool bLogOnScreenOnMaster = true, + bool bLogOnScreenOnSlaves = false); UFUNCTION(BlueprintCallable, Category = "UniLog") static void ModifyLogStream(const FString StreamName, bool bOnScreen = false, - FColor OnScreenColor = FColor(0, 0, 255, 0), bool bLogOnScreenOnMaster = true, + FColor OnScreenColor = FColor(255, 255, 255, 255), + FColor OnScreenBackgroundColor = FColor(0, 0, 0, 128), float OnScreenSize = 1.0, + float OnScreenDuration = 5.0, bool bLogToDefaultLog = false, bool bLogOnScreenOnMaster = true, bool bLogOnScreenOnSlaves = false); }; diff --git a/Source/UniversalLogging/UniversalLogging.Build.cs b/Source/UniversalLogging/UniversalLogging.Build.cs index cdf41288b47bb353a5a736d6f3f83a0fff75c2f8..d79a3f9f626c194ca2440a75fefac77c81bae9ba 100644 --- a/Source/UniversalLogging/UniversalLogging.Build.cs +++ b/Source/UniversalLogging/UniversalLogging.Build.cs @@ -5,9 +5,24 @@ public class UniversalLogging : ModuleRules { public UniversalLogging(ReadOnlyTargetRules Target) : base(Target) { - PrivateIncludePaths.AddRange(new string[] { "UniversalLogging/Private" }); - PublicIncludePaths.AddRange(new string[] { "UniversalLogging/Public" }); + bLegacyPublicIncludePaths = false; - PublicDependencyModuleNames.AddRange(new string[] { "CoreUObject", "Engine", "Core", "DisplayCluster" }); + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PrivateIncludePaths.AddRange(new string[] { }); + PublicIncludePaths.AddRange(new string[] { }); + + PublicDependencyModuleNames.AddRange(new string[] { "CoreUObject", "Engine", "Core" }); + + if (Target.Platform == UnrealTargetPlatform.Linux || Target.Platform == UnrealTargetPlatform.Win64) + { + PublicDependencyModuleNames.AddRange(new string[] { "DisplayCluster" }); + } + + //this is needed to register on Editor delegates, i.e., BeginPIE and EndPIE, but only in Editor builds + if (Target.Type == TargetRules.TargetType.Editor) + { + PrivateDependencyModuleNames.AddRange(new string[] { "UnrealEd" }); + } } } \ No newline at end of file diff --git a/UniversalLogging.uplugin b/UniversalLogging.uplugin index 467331ba643824cc3835896174cfcafbbc87c157..1a06157f1a411190fef66082a8bace1b46200d61 100644 --- a/UniversalLogging.uplugin +++ b/UniversalLogging.uplugin @@ -1,21 +1,30 @@ { - "FileVersion" : 3, + "FileVersion": 3, - "FriendlyName" : "Universal Logging Plugin", - "Version" : 1, - "VersionName": "1.0", - "Description" : "This plugin adds universal logging functionality which can be used also in shipping builds.", - "Category" : "Logging", - "CreatedBy" : "Martin Bellgardt", - "CreatedByURL" : "https://bird.lol", - "CanContainContent" : "true", + "FriendlyName": "Universal Logging Plugin", + "Version": 1, + "VersionName": "1.0", + "Description": "This plugin adds universal logging functionality which can be used also in shipping builds.", + "Category": "Logging", + "CreatedBy": "Martin Bellgardt", + "CreatedByURL": "https://bird.lol", + "CanContainContent": "true", - "Modules" : - [ - { - "Name" : "UniversalLogging", - "Type" : "Runtime", - "LoadingPhase" : "PreDefault" - } - ] + "Modules": [ + { + "Name": "UniversalLogging", + "Type": "Runtime", + "LoadingPhase": "PreDefault" + } + ], + "Plugins": [ + { + "Name": "nDisplay", + "Enabled": true, + "WhitelistPlatforms": [ + "Win64", + "Linux" + ] + } + ] } \ No newline at end of file