diff --git a/Handbook/src/appendix/changelog.md b/Handbook/src/appendix/changelog.md index c64a66de..1c37839e 100644 --- a/Handbook/src/appendix/changelog.md +++ b/Handbook/src/appendix/changelog.md @@ -17,6 +17,7 @@ This minor release introduces a major IPC overhaul, significant inter-process co - Added support for the new SenseGlove inter-process communication module `SGFakeShm`, enabling communication with SenseCom via `IPC` (named pipes on Microsoft Windows and Unix Domain Sockets on GNU/Linux) and `TCP over loopback/localhost` mechanisms, in addition to the previously supported `Boost::SharedMem`. This release also reduces `Boost::SharedMem` latency and increases data throughput, which should help improve frame rate and overall performance. Please note that communication over SharedMem is still blocking (non-async) in nature, so in enterprise environments where security software typically restricts `SharedMem` access, or in cases of conflicts with other applications, we recommend using either `IPC` or `TCP`. These transports have been implemented from scratch using an asynchronous, non-blocking architecture, although the new SenseCom also supports optional blocking communication modes and additional configuration tweaks if desired. `IPC` and `TCP over loopback` are also typically less restricted than `SharedMem` in enterprise environments and generally provide significantly lower latency. This helps maintain smooth frame rates by decoupling rendering and data exchange with SenseCom. Furthermore, the new SenseGlove UE plugin and SenseCom remain backward compatible with older releases of SenseCom and the UE plugin without requiring any specific configuration. They can automatically detect and fall back to the legacy `SharedMem` transport. - Added [nanomsg/nng](https://github.com/nanomsg/nng) third-party library binaries for Microsoft Windows and GNU/Linux. This is a requirement for the `SGFakeShm` module. - Added [google/flatbuffers](https://github.com/google/flatbuffers) third-party library binaries for Microsoft Windows and GNU/Linux. This is a requirement for the `SGFakeShm` module. +- Added `SGCore`'s `Library::IsInitialized()`, `Library::Initialize()`, and `Library::Terminate()` wrapper functions to SenseGlove UE C++ and Blueprint API that are required for the underlying `SGCore` and `SGFakeShm` initialization and termination and automatically initialize or terminate the library at the relevant module's startup or shutdown, eliminating the need for manual initialization or termination by plugin users. - Added third-party module `SGFakeShmThirdPartyLibs`. - Added third-party module `SGFlatBuffersThirdPartyLibs`. - Added third-party module `SGNngThirdPartyLibs`. diff --git a/Source/SenseGloveCore/Private/SGCore/SGLibrary.cpp b/Source/SenseGloveCore/Private/SGCore/SGLibrary.cpp index 0a605e21..64af5e1d 100644 --- a/Source/SenseGloveCore/Private/SGCore/SGLibrary.cpp +++ b/Source/SenseGloveCore/Private/SGCore/SGLibrary.cpp @@ -39,6 +39,27 @@ #include "SGInterop/SGIC_ESGBackendType.h" #include "SGInterop/SGIC_FString.h" +bool FSGLibrary::IsInitialized() +{ + return SGCoreImpl_FSGLibraryImpl_IsInitialized(); +} + +bool FSGLibrary::Initialize(FString& OutError) +{ + FSGIC_FString Container; + const bool bResult = SGCoreImpl_FSGLibraryImpl_Initialize(&Container); + OutError = MoveTemp(Container.String); + return bResult; +} + +bool FSGLibrary::Terminate(FString& OutError) +{ + FSGIC_FString Container; + const bool bResult = SGCoreImpl_FSGLibraryImpl_Terminate(&Container); + OutError = MoveTemp(Container.String); + return bResult; +} + FString FSGLibrary::GetVersion() { FSGIC_FString Container; diff --git a/Source/SenseGloveCore/Private/SenseGloveCoreModule.cpp b/Source/SenseGloveCore/Private/SenseGloveCoreModule.cpp index 3162ac8d..218e86b2 100644 --- a/Source/SenseGloveCore/Private/SenseGloveCoreModule.cpp +++ b/Source/SenseGloveCore/Private/SenseGloveCoreModule.cpp @@ -43,8 +43,34 @@ void FSenseGloveCoreModule::StartupModule() { SGLOG("Starting up the SenseGloveCore module..."); - SGLOG(FString::Printf(TEXT("The Unreal Engine implementation of '%s' has been loaded successfully!"), + + SGLOG(FString::Printf(TEXT("Initializing '%s'..."), *FSGLibrary::GetVersion())); + + const bool bInitializedAlready = FSGLibrary::IsInitialized(); + if (!ensureAlwaysMsgf(!bInitializedAlready, TEXT("Failed to initialize SGCore!"))) + { + SGLOG_FATAL(FString::Printf(TEXT("Failed to initialize '%s': it has already been initialized!"), + *FSGLibrary::GetVersion())); + } + else + { + FString Error; + const bool bInitialized = FSGLibrary::Initialize(Error); + if (bInitialized) + { + SGLOG(FString::Printf(TEXT("'%s' has been initialized successfully!"), + *FSGLibrary::GetVersion())); + + SGLOG(FString::Printf(TEXT("The Unreal Engine implementation of '%s' has been loaded successfully!"), + *FSGLibrary::GetVersion())); + } + else + { + SGLOG_FATAL(FString::Printf(TEXT("Failed to initialize '%s'!"), + *FSGLibrary::GetVersion())); + } + } } void FSenseGloveCoreModule::PreUnloadCallback() @@ -60,6 +86,34 @@ void FSenseGloveCoreModule::PostLoadCallback() void FSenseGloveCoreModule::ShutdownModule() { SGLOG("Shutting down the SenseGloveCore module..."); + + SGLOG(FString::Printf(TEXT("Terminating '%s'..."), + *FSGLibrary::GetVersion())); + + const bool bInitialized = FSGLibrary::IsInitialized(); + if (!ensureAlwaysMsgf(bInitialized, TEXT("Failed to terminate SGCore!"))) + { + SGLOG_FATAL(FString::Printf(TEXT("Failed to terminate '%s': it has not been initialized yet!"), + *FSGLibrary::GetVersion())); + } + else + { + FString Error; + const bool bTerminated = FSGLibrary::Terminate(Error); + if (bTerminated) + { + SGLOG(FString::Printf(TEXT("'%s' has been terminated successfully!"), + *FSGLibrary::GetVersion())); + + SGLOG(FString::Printf(TEXT("The Unreal Engine implementation of '%s' has been shutdown successfully!"), + *FSGLibrary::GetVersion())); + } + else + { + SGLOG_FATAL(FString::Printf(TEXT("Failed to terminate '%s'!"), + *FSGLibrary::GetVersion())); + } + } } #undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Source/SenseGloveCore/Public/SGCore/SGLibrary.h b/Source/SenseGloveCore/Public/SGCore/SGLibrary.h index 2a10aa54..59e90908 100644 --- a/Source/SenseGloveCore/Public/SGCore/SGLibrary.h +++ b/Source/SenseGloveCore/Public/SGCore/SGLibrary.h @@ -51,6 +51,10 @@ struct SENSEGLOVECORE_API FSGLibrary GENERATED_BODY() public: + static bool IsInitialized(); + static bool Initialize(FString& OutError); + static bool Terminate(FString& OutError); + /** * The version of this C++ Library in string representation. */ diff --git a/Source/SenseGloveCoreImpl/Private/SGCoreImpl/SGExportedFunctions.cpp b/Source/SenseGloveCoreImpl/Private/SGCoreImpl/SGExportedFunctions.cpp index ffee3bd8..d6d9154a 100644 --- a/Source/SenseGloveCoreImpl/Private/SGCoreImpl/SGExportedFunctions.cpp +++ b/Source/SenseGloveCoreImpl/Private/SGCoreImpl/SGExportedFunctions.cpp @@ -2776,6 +2776,25 @@ bool SGCoreImpl_FSGJointKinematicsImpl_ForwardKinematics_Profile_Finger_JointAng * SGLibraryImpl.h ******************************************************************************/ +bool SGCoreImpl_FSGLibraryImpl_IsInitialized() +{ + return FSGLibraryImpl::IsInitialized(); +} + +bool SGCoreImpl_FSGLibraryImpl_Initialize( + void* OutError) +{ + return FSGLibraryImpl::Initialize( + StaticCast(OutError)->String); +} + +bool SGCoreImpl_FSGLibraryImpl_Terminate( + void* OutError) +{ + return FSGLibraryImpl::Terminate( + StaticCast(OutError)->String); +} + void SGCoreImpl_FSGLibraryImpl_Version( void* OutVersion) { diff --git a/Source/SenseGloveCoreImpl/Private/SGCoreImpl/SGLibraryImpl.cpp b/Source/SenseGloveCoreImpl/Private/SGCoreImpl/SGLibraryImpl.cpp index 18862db7..3657e076 100644 --- a/Source/SenseGloveCoreImpl/Private/SGCoreImpl/SGLibraryImpl.cpp +++ b/Source/SenseGloveCoreImpl/Private/SGCoreImpl/SGLibraryImpl.cpp @@ -35,11 +35,34 @@ #include "SGCoreImpl/SGLibraryImpl.h" +#include + #include "Containers/StringConv.h" #include "SGBuildHacks/SGInclude_Core_Library.h" #include "SGCoreImpl/SGCoreEnumCast.h" +bool FSGLibraryImpl::IsInitialized() +{ + return FLibraryType::IsInitialized(); +} + +bool FSGLibraryImpl::Initialize(FString& OutError) +{ + std::string Error; + const bool bResult = FLibraryType::Initialize(Error); + OutError = UTF8_TO_TCHAR(Error.c_str()); + return bResult; +} + +bool FSGLibraryImpl::Terminate(FString& OutError) +{ + std::string Error; + const bool bResult = FLibraryType::Terminate(Error); + OutError = UTF8_TO_TCHAR(Error.c_str()); + return bResult; +} + FString FSGLibraryImpl::Version() { const FString Version(UTF8_TO_TCHAR(FLibraryType::Version().c_str())); diff --git a/Source/SenseGloveCoreImpl/Public/SGCoreImpl/SGExportedFunctions.h b/Source/SenseGloveCoreImpl/Public/SGCoreImpl/SGExportedFunctions.h index 8fe71d6e..0202e621 100644 --- a/Source/SenseGloveCoreImpl/Public/SGCoreImpl/SGExportedFunctions.h +++ b/Source/SenseGloveCoreImpl/Public/SGCoreImpl/SGExportedFunctions.h @@ -1409,6 +1409,14 @@ SGCoreImpl_FSGJointKinematicsImpl_ForwardKinematics_Profile_Finger_JointAngles_O * SGLibraryImpl.h ******************************************************************************/ +SENSEGLOVECOREIMPL_PUBLIC_API bool SGCoreImpl_FSGLibraryImpl_IsInitialized(); + +SENSEGLOVECOREIMPL_PUBLIC_API bool SGCoreImpl_FSGLibraryImpl_Initialize( + void* OutError); + +SENSEGLOVECOREIMPL_PUBLIC_API bool SGCoreImpl_FSGLibraryImpl_Terminate( + void* OutError); + SENSEGLOVECOREIMPL_PUBLIC_API void SGCoreImpl_FSGLibraryImpl_Version( void* OutVersion); diff --git a/Source/SenseGloveCoreImpl/Public/SGCoreImpl/SGLibraryImpl.h b/Source/SenseGloveCoreImpl/Public/SGCoreImpl/SGLibraryImpl.h index 9abbeab8..08054dba 100644 --- a/Source/SenseGloveCoreImpl/Public/SGCoreImpl/SGLibraryImpl.h +++ b/Source/SenseGloveCoreImpl/Public/SGCoreImpl/SGLibraryImpl.h @@ -50,6 +50,10 @@ public: typedef SGCore::Library FLibraryType; public: + static bool IsInitialized(); + static bool Initialize(FString& OutError); + static bool Terminate(FString& OutError); + static FString Version(); static ESGBackendType GetBackendType(); diff --git a/Source/SenseGloveCoreKismet/Private/SGCoreKismet/SGLibraryKismetLibrary.cpp b/Source/SenseGloveCoreKismet/Private/SGCoreKismet/SGLibraryKismetLibrary.cpp index cba0f12c..ab9a22cc 100644 --- a/Source/SenseGloveCoreKismet/Private/SGCoreKismet/SGLibraryKismetLibrary.cpp +++ b/Source/SenseGloveCoreKismet/Private/SGCoreKismet/SGLibraryKismetLibrary.cpp @@ -37,6 +37,21 @@ #include "SGCore/SGLibrary.h" +bool USGLibraryKismetLibrary::IsInitialized() +{ + return FSGLibrary::IsInitialized(); +} + +bool USGLibraryKismetLibrary::Initialize(FString& OutError) +{ + return FSGLibrary::Initialize(OutError); +} + +bool USGLibraryKismetLibrary::Terminate(FString& OutError) +{ + return FSGLibrary::Terminate(OutError); +} + FString USGLibraryKismetLibrary::GetVersion() { return FSGLibrary::GetVersion(); diff --git a/Source/SenseGloveCoreKismet/Public/SGCoreKismet/SGLibraryKismetLibrary.h b/Source/SenseGloveCoreKismet/Public/SGCoreKismet/SGLibraryKismetLibrary.h index 5c92c88c..9dc2953e 100644 --- a/Source/SenseGloveCoreKismet/Public/SGCoreKismet/SGLibraryKismetLibrary.h +++ b/Source/SenseGloveCoreKismet/Public/SGCoreKismet/SGLibraryKismetLibrary.h @@ -51,6 +51,15 @@ class SENSEGLOVECOREKISMET_API USGLibraryKismetLibrary final : public USGBluepri GENERATED_BODY() public: + UFUNCTION(BlueprintCallable, Category="SenseGlove | Core | Library") + static bool IsInitialized(); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Core | Library") + static bool Initialize(FString& OutError); + + UFUNCTION(BlueprintCallable, Category="SenseGlove | Core | Library") + static bool Terminate(FString& OutError); + /** * The version of this C++ Library in string representation. */