diff --git a/Content/FRM_Icon.uasset b/Content/FRM_Icon.uasset new file mode 100644 index 00000000..9dba1b3b Binary files /dev/null and b/Content/FRM_Icon.uasset differ diff --git a/Content/GameInstance.uasset b/Content/GameInstance.uasset index 013b20a8..98f5cde7 100644 Binary files a/Content/GameInstance.uasset and b/Content/GameInstance.uasset differ diff --git a/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring.uasset b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring.uasset new file mode 100644 index 00000000..78353713 Binary files /dev/null and b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring.uasset differ diff --git a/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_Debug.uasset b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_Debug.uasset new file mode 100644 index 00000000..8cf439a5 Binary files /dev/null and b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_Debug.uasset differ diff --git a/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_DiscIT.uasset b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_DiscIT.uasset new file mode 100644 index 00000000..dd7823a5 Binary files /dev/null and b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_DiscIT.uasset differ diff --git a/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_General.uasset b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_General.uasset new file mode 100644 index 00000000..a209c58e Binary files /dev/null and b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_General.uasset differ diff --git a/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_Serial.uasset b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_Serial.uasset new file mode 100644 index 00000000..7c2baace Binary files /dev/null and b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_Serial.uasset differ diff --git a/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_uWS.uasset b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_uWS.uasset new file mode 100644 index 00000000..b8cce391 Binary files /dev/null and b/Content/SessionSettings/Categories/Client/FicsitRemoteMonitoring_uWS.uasset differ diff --git a/Content/SessionSettings/Categories/FicsitRemoteMonitoring.uasset b/Content/SessionSettings/Categories/FicsitRemoteMonitoring.uasset deleted file mode 100644 index d2f11eb5..00000000 Binary files a/Content/SessionSettings/Categories/FicsitRemoteMonitoring.uasset and /dev/null differ diff --git a/Content/SessionSettings/Categories/FicsitRemoteMonitoring_General.uasset b/Content/SessionSettings/Categories/FicsitRemoteMonitoring_General.uasset new file mode 100644 index 00000000..dbef5a71 Binary files /dev/null and b/Content/SessionSettings/Categories/FicsitRemoteMonitoring_General.uasset differ diff --git a/Content/SessionSettings/Categories/FicsiteRemoteMonitoring_General.uasset b/Content/SessionSettings/Categories/FicsiteRemoteMonitoring_General.uasset deleted file mode 100644 index ffd8810d..00000000 Binary files a/Content/SessionSettings/Categories/FicsiteRemoteMonitoring_General.uasset and /dev/null differ diff --git a/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_Debug.uasset b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_Debug.uasset new file mode 100644 index 00000000..fc4f234d Binary files /dev/null and b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_Debug.uasset differ diff --git a/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_DiscIT.uasset b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_DiscIT.uasset new file mode 100644 index 00000000..a84d33e3 Binary files /dev/null and b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_DiscIT.uasset differ diff --git a/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_General.uasset b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_General.uasset new file mode 100644 index 00000000..2f7bf14e Binary files /dev/null and b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_General.uasset differ diff --git a/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_Serial.uasset b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_Serial.uasset new file mode 100644 index 00000000..0c944827 Binary files /dev/null and b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_Serial.uasset differ diff --git a/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_uWS.uasset b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_uWS.uasset new file mode 100644 index 00000000..27938ea6 Binary files /dev/null and b/Content/SessionSettings/Categories/Server/FicsitRemoteMonitoring_Server_uWS.uasset differ diff --git a/Content/SessionSettings/DediUI/Debug/FRM_JSONDebug.uasset b/Content/SessionSettings/DediUI/Debug/FRM_JSONDebug.uasset new file mode 100644 index 00000000..47e86578 Binary files /dev/null and b/Content/SessionSettings/DediUI/Debug/FRM_JSONDebug.uasset differ diff --git a/Content/SessionSettings/DediUI/General/FRM_SplineSampleSetting.uasset b/Content/SessionSettings/DediUI/General/FRM_SplineSampleSetting.uasset new file mode 100644 index 00000000..fc684b0d Binary files /dev/null and b/Content/SessionSettings/DediUI/General/FRM_SplineSampleSetting.uasset differ diff --git a/Content/SessionSettings/DediUI/Serial/FRM_Baud_Rate.uasset b/Content/SessionSettings/DediUI/Serial/FRM_Baud_Rate.uasset new file mode 100644 index 00000000..3a2ef323 Binary files /dev/null and b/Content/SessionSettings/DediUI/Serial/FRM_Baud_Rate.uasset differ diff --git a/Content/SessionSettings/DediUI/Serial/FRM_COM_Autostart.uasset b/Content/SessionSettings/DediUI/Serial/FRM_COM_Autostart.uasset new file mode 100644 index 00000000..6a8030bf Binary files /dev/null and b/Content/SessionSettings/DediUI/Serial/FRM_COM_Autostart.uasset differ diff --git a/Content/SessionSettings/DediUI/Serial/FRM_COM_Port.uasset b/Content/SessionSettings/DediUI/Serial/FRM_COM_Port.uasset new file mode 100644 index 00000000..e00ead76 Binary files /dev/null and b/Content/SessionSettings/DediUI/Serial/FRM_COM_Port.uasset differ diff --git a/Content/SessionSettings/DediUI/Serial/FRM_SerialStackSize.uasset b/Content/SessionSettings/DediUI/Serial/FRM_SerialStackSize.uasset new file mode 100644 index 00000000..81da521b Binary files /dev/null and b/Content/SessionSettings/DediUI/Serial/FRM_SerialStackSize.uasset differ diff --git a/Content/SessionSettings/DediUI/uWebSockets/FRM_Authentication_Token.uasset b/Content/SessionSettings/DediUI/uWebSockets/FRM_Authentication_Token.uasset new file mode 100644 index 00000000..b0849c35 Binary files /dev/null and b/Content/SessionSettings/DediUI/uWebSockets/FRM_Authentication_Token.uasset differ diff --git a/Content/SessionSettings/DediUI/uWebSockets/FRM_HTTP_Port.uasset b/Content/SessionSettings/DediUI/uWebSockets/FRM_HTTP_Port.uasset new file mode 100644 index 00000000..98a8013d Binary files /dev/null and b/Content/SessionSettings/DediUI/uWebSockets/FRM_HTTP_Port.uasset differ diff --git a/Content/SessionSettings/DediUI/uWebSockets/FRM_WebSocketPushCycle.uasset b/Content/SessionSettings/DediUI/uWebSockets/FRM_WebSocketPushCycle.uasset new file mode 100644 index 00000000..fb59b662 Binary files /dev/null and b/Content/SessionSettings/DediUI/uWebSockets/FRM_WebSocketPushCycle.uasset differ diff --git a/Content/SessionSettings/DediUI/uWebSockets/FRM_Web_Autostart.uasset b/Content/SessionSettings/DediUI/uWebSockets/FRM_Web_Autostart.uasset new file mode 100644 index 00000000..1e4b76b3 Binary files /dev/null and b/Content/SessionSettings/DediUI/uWebSockets/FRM_Web_Autostart.uasset differ diff --git a/Content/SessionSettings/DediUI/uWebSockets/FRM_Web_Root.uasset b/Content/SessionSettings/DediUI/uWebSockets/FRM_Web_Root.uasset new file mode 100644 index 00000000..70c11b50 Binary files /dev/null and b/Content/SessionSettings/DediUI/uWebSockets/FRM_Web_Root.uasset differ diff --git a/Content/SessionSettings/FicsitRemoteMonitoring_Base.uasset b/Content/SessionSettings/FicsitRemoteMonitoring_Base.uasset index dc970045..b09c1287 100644 Binary files a/Content/SessionSettings/FicsitRemoteMonitoring_Base.uasset and b/Content/SessionSettings/FicsitRemoteMonitoring_Base.uasset differ diff --git a/Content/SessionSettings/General/SplineSampleSetting.uasset b/Content/SessionSettings/General/SplineSampleSetting.uasset deleted file mode 100644 index c261f154..00000000 Binary files a/Content/SessionSettings/General/SplineSampleSetting.uasset and /dev/null differ diff --git a/Content/SessionSettings/Session/Debug/FRM_JSONDebug.uasset b/Content/SessionSettings/Session/Debug/FRM_JSONDebug.uasset new file mode 100644 index 00000000..39dd4f87 Binary files /dev/null and b/Content/SessionSettings/Session/Debug/FRM_JSONDebug.uasset differ diff --git a/Content/SessionSettings/Session/General/FRM_SplineSampleSetting.uasset b/Content/SessionSettings/Session/General/FRM_SplineSampleSetting.uasset new file mode 100644 index 00000000..a12e6312 Binary files /dev/null and b/Content/SessionSettings/Session/General/FRM_SplineSampleSetting.uasset differ diff --git a/Content/SessionSettings/Session/Serial/FRM_Baud_Rate.uasset b/Content/SessionSettings/Session/Serial/FRM_Baud_Rate.uasset new file mode 100644 index 00000000..9a4be154 Binary files /dev/null and b/Content/SessionSettings/Session/Serial/FRM_Baud_Rate.uasset differ diff --git a/Content/SessionSettings/Session/Serial/FRM_COM_Autostart.uasset b/Content/SessionSettings/Session/Serial/FRM_COM_Autostart.uasset new file mode 100644 index 00000000..234d1464 Binary files /dev/null and b/Content/SessionSettings/Session/Serial/FRM_COM_Autostart.uasset differ diff --git a/Content/SessionSettings/Session/Serial/FRM_COM_Port.uasset b/Content/SessionSettings/Session/Serial/FRM_COM_Port.uasset new file mode 100644 index 00000000..8cf197e9 Binary files /dev/null and b/Content/SessionSettings/Session/Serial/FRM_COM_Port.uasset differ diff --git a/Content/SessionSettings/Session/Serial/FRM_SerialStackSize.uasset b/Content/SessionSettings/Session/Serial/FRM_SerialStackSize.uasset new file mode 100644 index 00000000..73141615 Binary files /dev/null and b/Content/SessionSettings/Session/Serial/FRM_SerialStackSize.uasset differ diff --git a/Content/SessionSettings/Session/uWebSockets/FRM_Authentication_Token.uasset b/Content/SessionSettings/Session/uWebSockets/FRM_Authentication_Token.uasset new file mode 100644 index 00000000..4664db9c Binary files /dev/null and b/Content/SessionSettings/Session/uWebSockets/FRM_Authentication_Token.uasset differ diff --git a/Content/SessionSettings/Session/uWebSockets/FRM_HTTP_Port.uasset b/Content/SessionSettings/Session/uWebSockets/FRM_HTTP_Port.uasset new file mode 100644 index 00000000..882fb10c Binary files /dev/null and b/Content/SessionSettings/Session/uWebSockets/FRM_HTTP_Port.uasset differ diff --git a/Content/SessionSettings/Session/uWebSockets/FRM_WebSocketPushCycle.uasset b/Content/SessionSettings/Session/uWebSockets/FRM_WebSocketPushCycle.uasset new file mode 100644 index 00000000..73051f54 Binary files /dev/null and b/Content/SessionSettings/Session/uWebSockets/FRM_WebSocketPushCycle.uasset differ diff --git a/Content/SessionSettings/Session/uWebSockets/FRM_Web_Autostart.uasset b/Content/SessionSettings/Session/uWebSockets/FRM_Web_Autostart.uasset new file mode 100644 index 00000000..c466d6ae Binary files /dev/null and b/Content/SessionSettings/Session/uWebSockets/FRM_Web_Autostart.uasset differ diff --git a/Content/SessionSettings/Session/uWebSockets/FRM_Web_Root.uasset b/Content/SessionSettings/Session/uWebSockets/FRM_Web_Root.uasset new file mode 100644 index 00000000..f5b31d4c Binary files /dev/null and b/Content/SessionSettings/Session/uWebSockets/FRM_Web_Root.uasset differ diff --git a/Content/Subsystems/FicsitRemoteMonitoring_BP.uasset b/Content/Subsystems/FicsitRemoteMonitoring_BP.uasset index 078d1d97..269dc2d5 100644 Binary files a/Content/Subsystems/FicsitRemoteMonitoring_BP.uasset and b/Content/Subsystems/FicsitRemoteMonitoring_BP.uasset differ diff --git a/Source/FicsitRemoteMonitoring/FicsitRemoteMonitoring.build.cs b/Source/FicsitRemoteMonitoring/FicsitRemoteMonitoring.build.cs index 8ab4880f..f5fc0251 100644 --- a/Source/FicsitRemoteMonitoring/FicsitRemoteMonitoring.build.cs +++ b/Source/FicsitRemoteMonitoring/FicsitRemoteMonitoring.build.cs @@ -44,12 +44,15 @@ public FicsitRemoteMonitoring(ReadOnlyTargetRules Target) : base(Target) "InputCore", "Json", "JsonUtilities", + "Sockets", + "Networking", "FactoryGame", "SML", "Chaos", "ChaosVehiclesCore", "ChaosVehicles", "ChaosSolverEngine", "HTTP", - "FicsitRemoteMonitoring" + "FicsitRemoteMonitoring", + "FactoryDedicatedServer" } ); diff --git a/Source/FicsitRemoteMonitoring/Private/Commands/multi.cpp b/Source/FicsitRemoteMonitoring/Private/Commands/multi.cpp index 126135ce..f42286ae 100644 --- a/Source/FicsitRemoteMonitoring/Private/Commands/multi.cpp +++ b/Source/FicsitRemoteMonitoring/Private/Commands/multi.cpp @@ -7,6 +7,7 @@ #include "FRM_Request.h" #include "NotificationLoader.h" #include "StructuredLog.h" +#include "Libraries/Validation.h" #include "Kismet/KismetSystemLibrary.h" FChatReturn AFRMCommand::RemoteMonitoringCommand(UObject* WorldContext, UCommandSender* Sender, TArray Arguments) { @@ -76,7 +77,7 @@ FChatReturn AFRMCommand::RemoteMonitoringCommand(UObject* WorldContext, UCommand return ChatReturn; } - if (command == "http") { + if (command == "http" || command == "uws") { ChatReturn.Chat = TEXT("Usage: /frm http "); if (argumentsNum < 2) { @@ -85,9 +86,19 @@ FChatReturn AFRMCommand::RemoteMonitoringCommand(UObject* WorldContext, UCommand FString arg1 = Arguments[1].ToLower(); + const int32 Port = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.Port"), 8080); + + FString Reason; + if (!UFRMValidation::IsTcpPortAvailable(Port, Reason)) + { + UE_LOG(LogHttpServer, Error, TEXT("Port %d unavailable: %s"), Port, *Reason); + + ChatReturn.Chat = FString(TEXT("Port " + FString::FromInt(Port) + "Unavailable. Reason: " + *Reason)); + ChatReturn.Color = FLinearColor::Red; + ChatReturn.Status = EExecutionStatus::COMPLETED; + } + if (arg1 == "start") { - auto config = FConfig_HTTPStruct::GetActiveConfig(WorldContext); - int32 Port = config.HTTP_Port; UE_LOG(LogHttpServer, Log, TEXT("Chat Command: Starting HTTP Service. Port: %d"), Port); ModSubsystem->StartWebSocketServer(true); @@ -118,11 +129,10 @@ FChatReturn AFRMCommand::RemoteMonitoringCommand(UObject* WorldContext, UCommand FString arg1 = Arguments[1].ToLower(); if (arg1 == "start") { - auto config = FConfig_SerialStruct::GetActiveConfig(WorldContext); - FString Port = config.COM_Port; - - ModSubsystem->StartWebSocketServer(); + ModSubsystem->InitSerialDevice(); + const FString Port = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("Serial.Port"), "COM3").TrimStartAndEnd(); + ChatReturn.Chat = FString(TEXT("Serial/RS232 Service Initiated on Port: " + Port)); ChatReturn.Color = FLinearColor::Green; ChatReturn.Status = EExecutionStatus::COMPLETED; @@ -130,7 +140,7 @@ FChatReturn AFRMCommand::RemoteMonitoringCommand(UObject* WorldContext, UCommand UE_LOGFMT(LogHttpServer, Log, "Serial/RS232 Service started. Port: {Port}"); } else if (arg1 == "stop") { - ModSubsystem->StopWebSocketServer(); + ModSubsystem->StopSerialDevice(); UE_LOG(LogHttpServer, Log, TEXT("Stopping Serial/RS232 Service.")); ChatReturn.Chat = TEXT("Stopping Serial/RS232 Service."); diff --git a/Source/FicsitRemoteMonitoring/Private/Endpoints/Factory/Logistics.cpp b/Source/FicsitRemoteMonitoring/Private/Endpoints/Factory/Logistics.cpp index e1540d4e..7805dbf2 100644 --- a/Source/FicsitRemoteMonitoring/Private/Endpoints/Factory/Logistics.cpp +++ b/Source/FicsitRemoteMonitoring/Private/Endpoints/Factory/Logistics.cpp @@ -22,8 +22,8 @@ class UFGFactoryConnectionComponent; TArray> ULogistics::getBelts_Helper(UObject* WorldContext, bool IsBelt) { AFGBuildableSubsystem* BuildableSubsystem = AFGBuildableSubsystem::Get(WorldContext->GetWorld()); - USessionSettingsManager* SessionSettings = WorldContext->GetWorld()->GetSubsystem(); - float SampleDistance = SessionSettings->GetFloatOptionValue("FicsitRemoteMonitoring.General.SplineSampleDistance"); + + const float SampleDistance = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("General.SplineSampleDistance"), 75.0f); TArray Conveyors; BuildableSubsystem->GetTypedBuildable(Conveyors); @@ -179,8 +179,7 @@ void ULogistics::getSplitterMerger(UObject* WorldContext, FRequestData RequestDa void ULogistics::getPipes(UObject* WorldContext, FRequestData RequestData, TArray>& OutJsonArray) { AFGBuildableSubsystem* BuildableSubsystem = AFGBuildableSubsystem::Get(WorldContext->GetWorld()); - USessionSettingsManager* SessionSettings = WorldContext->GetWorld()->GetSubsystem(); - float SampleDistance = SessionSettings->GetFloatOptionValue("FicsitRemoteMonitoring.General.SplineSampleDistance"); + const float SampleDistance = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("General.SplineSampleDistance"), 75.0f); TArray Pipes; BuildableSubsystem->GetTypedBuildable(Pipes); diff --git a/Source/FicsitRemoteMonitoring/Private/Endpoints/Travel/Hypertubes.cpp b/Source/FicsitRemoteMonitoring/Private/Endpoints/Travel/Hypertubes.cpp index 6526d3c7..0efd1e36 100644 --- a/Source/FicsitRemoteMonitoring/Private/Endpoints/Travel/Hypertubes.cpp +++ b/Source/FicsitRemoteMonitoring/Private/Endpoints/Travel/Hypertubes.cpp @@ -31,8 +31,7 @@ void UHypertubes::getHyperEntrance(UObject* WorldContext, FRequestData RequestDa void UHypertubes::getHypertube(UObject* WorldContext, FRequestData RequestData, TArray>& OutJsonArray) { AFGBuildableSubsystem* BuildableSubsystem = AFGBuildableSubsystem::Get(WorldContext->GetWorld()); - USessionSettingsManager* SessionSettings = WorldContext->GetWorld()->GetSubsystem(); - float SampleDistance = SessionSettings->GetFloatOptionValue("FicsitRemoteMonitoring.General.SplineSampleDistance"); + const float SampleDistance = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("General.SplineSampleDistance"), 75.0f); TArray Hypertubes; BuildableSubsystem->GetTypedBuildable(Hypertubes); diff --git a/Source/FicsitRemoteMonitoring/Private/Endpoints/Travel/Trains.cpp b/Source/FicsitRemoteMonitoring/Private/Endpoints/Travel/Trains.cpp index 15689502..ae02fa97 100644 --- a/Source/FicsitRemoteMonitoring/Private/Endpoints/Travel/Trains.cpp +++ b/Source/FicsitRemoteMonitoring/Private/Endpoints/Travel/Trains.cpp @@ -287,8 +287,8 @@ void UTrains::getTrainStation(UObject* WorldContext, FRequestData RequestData, T void UTrains::getTrainRails(UObject* WorldContext, FRequestData RequestData, TArray>& OutJsonArray) { AFGBuildableSubsystem* BuildableSubsystem = AFGBuildableSubsystem::Get(WorldContext->GetWorld()); - USessionSettingsManager* SessionSettings = WorldContext->GetWorld()->GetSubsystem(); - float SampleDistance = SessionSettings->GetFloatOptionValue("FicsitRemoteMonitoring.General.SplineSampleDistance"); + + const float SampleDistance = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("General.SplineSampleDistance"), 75.0f); TArray RailroadTracks; BuildableSubsystem->GetTypedBuildable(RailroadTracks); diff --git a/Source/FicsitRemoteMonitoring/Private/Endpoints/World/Communication.cpp b/Source/FicsitRemoteMonitoring/Private/Endpoints/World/Communication.cpp index 78d6cda6..9ec9bd3c 100644 --- a/Source/FicsitRemoteMonitoring/Private/Endpoints/World/Communication.cpp +++ b/Source/FicsitRemoteMonitoring/Private/Endpoints/World/Communication.cpp @@ -270,12 +270,9 @@ void UCommunication::setModSetting(UObject* WorldContext, FRequestData RequestDa continue; } - UE_LOG(LogTemp, Warning, TEXT("SplineSampleDistance: %f"), SplineSampleDistance); + UE_LOG(LogFRMAPI, Warning, TEXT("SplineSampleDistance: %f"), SplineSampleDistance); - const FString& cvar = "FicsitRemoteMonitoring.General.SplineSampleDistance"; - - USessionSettingsManager* SessionSettings = WorldContext->GetWorld()->GetSubsystem(); - SessionSettings->SetFloatOptionValue(cvar, SplineSampleDistance); + UFRMConfigManager::FRM_SetConfigFromInput(TEXT("General.SplineSampleDistance"), FString::SanitizeFloat(SplineSampleDistance), false); TSharedPtr JChatMessage = MakeShared(); JChatMessage->Values.Add("IsSent", MakeShared(true)); diff --git a/Source/FicsitRemoteMonitoring/Private/FRMConfigInitSubsystem.cpp b/Source/FicsitRemoteMonitoring/Private/FRMConfigInitSubsystem.cpp index 88f01760..3be5756d 100644 --- a/Source/FicsitRemoteMonitoring/Private/FRMConfigInitSubsystem.cpp +++ b/Source/FicsitRemoteMonitoring/Private/FRMConfigInitSubsystem.cpp @@ -1,7 +1,11 @@ #include "FRMConfigInitSubsystem.h" #include "Configuration/ConfigManager.h" #include "ConfigPropertyString.h" +#include "FGGameUserSettings.h" +#include "SessionSettingsManager.h" +#include "SMLOptionsLibrary.h" #include "Engine/Engine.h" +#include "Libraries/FRMConfigManager.h" DEFINE_LOG_CATEGORY_STATIC(LogFRMConfigInitSubsystem, Log, All); @@ -9,52 +13,32 @@ void UFRMConfigInitSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); - UConfigManager* ConfigManager = GetGameInstance()->GetSubsystem(); - if (!ConfigManager) + USessionSettingsManager* SessionSettings = GetWorld()->GetSubsystem(); + if (!SessionSettings) { - UE_LOG(LogFRMConfigInitSubsystem, Error, TEXT("ConfigManager missing.")); + UE_LOG(LogFRMConfigInitSubsystem, Error, TEXT("SessionSettingsManager missing.")); return; } - ConfigManager->ReloadModConfigurations(); + const FString AuthToken = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.Root"), ""); - // Now config is loaded and safe to read/write - HttpConfig = FConfig_HTTPStruct::GetActiveConfig(this); - SerialConfig = FConfig_SerialStruct::GetActiveConfig(this); - FactoryConfig = FConfig_FactoryStruct::GetActiveConfig(this); - - if (HttpConfig.Authentication_Token.IsEmpty()) + if (AuthToken.IsEmpty()) { - HttpConfig.Authentication_Token = GenerateAuthToken(32); - SaveHttpAuthToken(ConfigManager); + if (!UFRMConfigManager::FRM_SetConfigFromInput(TEXT("uWS.AuthenticationToken"), GenerateAuthToken(32), false)) + { + UE_LOG(LogFRMConfigInitSubsystem, Warning, TEXT("Failed to apply setting")); + return; + } - UE_LOG(LogFRMConfigInitSubsystem, Log, TEXT("Generated and saved new token: %s"), *HttpConfig.Authentication_Token); + UE_LOG(LogFRMConfigInitSubsystem, Log, TEXT("Generated and saved new token: %s"), *AuthToken); } else { UE_LOG(LogFRMConfigInitSubsystem, Log, TEXT("Token already exists.")); } - AuthenticationToken = HttpConfig.Authentication_Token; -} - -void UFRMConfigInitSubsystem::SaveHttpAuthToken(UConfigManager* ConfigManager) -{ - FConfigId ConfigId{ "FicsitRemoteMonitoring", "WebServer" }; - - UConfigPropertySection* ConfigurationRootSection = ConfigManager->GetConfigurationRootSection(ConfigId); - if (!ConfigurationRootSection) - { - UE_LOG(LogFRMConfigInitSubsystem, Warning, TEXT("ConfigurationRootSection is null.")); - return; - } - - if (UConfigPropertyString* AuthTokenProperty = Cast(ConfigurationRootSection->SectionProperties.FindRef("Authentication_Token"))) - { - AuthTokenProperty->Value = HttpConfig.Authentication_Token; - } - - ConfigManager->MarkConfigurationDirty(ConfigId); + AuthenticationToken = AuthToken; + } FString UFRMConfigInitSubsystem::GenerateAuthToken(const int32 Length) diff --git a/Source/FicsitRemoteMonitoring/Private/FicsitRemoteMonitoring.cpp b/Source/FicsitRemoteMonitoring/Private/FicsitRemoteMonitoring.cpp index 51e840f3..8f0cc789 100644 --- a/Source/FicsitRemoteMonitoring/Private/FicsitRemoteMonitoring.cpp +++ b/Source/FicsitRemoteMonitoring/Private/FicsitRemoteMonitoring.cpp @@ -22,6 +22,7 @@ #include "Research.h" #include "Resources.h" #include "Session.h" +#include "SMLOptionsLibrary.h" #include "StructuredLog.h" #include "SubsystemActorManager.h" #include "Support.h" @@ -31,6 +32,7 @@ #include "Trains.h" #include "Vehicles.h" #include "Engine/World.h" +#include "Libraries/Validation.h" us_listen_socket_t* SocketListener; bool SocketRunning = false; @@ -63,41 +65,21 @@ void AFicsitRemoteMonitoring::BeginPlay() // Load FRM's API Endpoints InitAPIRegistry(); - // Get our config subsystem - auto ConfigSubsystem = GetGameInstance()->GetSubsystem(); - if (ConfigSubsystem) - { - SetAuthToken(ConfigSubsystem->GetAuthenticationToken()); - } - - if (!ConfigSubsystem) - { - UE_LOG(LogTemp, Error, TEXT("[AFicsitRemoteMonitoring] Config subsystem missing!")); - return; - } - - // Use cached config values from the subsystem - const auto& HttpConfig = ConfigSubsystem->GetHttpConfig(); - const auto& SerialConfig = ConfigSubsystem->GetSerialConfig(); - const auto& FactoryConfig = ConfigSubsystem->GetFactoryConfig(); - - // Save locally - JSONDebugMode = FactoryConfig.JSONDebugMode; + const FString AuthToken = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.AuthenticationToken"), ""); + + // Store token for use in auth checks + SetAuthToken(AuthToken); - // Start services based on config - if (HttpConfig.Web_Autostart) + if (UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.Autostart"), false)) { StartWebSocketServer(); } - - if (SerialConfig.COM_Autostart) + + if (UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("Serial.Autostart"), false)) { InitSerialDevice(); } - // Store token for use in auth checks - SetAuthToken(ConfigSubsystem->GetAuthenticationToken()); - // Register the callback to ensure WebSocket is stopped on crash/exit FCoreDelegates::OnExit.AddUObject(this, &AFicsitRemoteMonitoring::StopWebSocketServer); } @@ -106,16 +88,16 @@ void AFicsitRemoteMonitoring::StartWebSocketPushDataLoop() { if (bHasRunningPushDataLoop) return; - FConfig_HTTPStruct HttpConfig = FConfig_HTTPStruct::GetActiveConfig(GetWorld()); - - Async(EAsyncExecution::Thread, [this, HttpConfig]() + Async(EAsyncExecution::Thread, [this]() { bHasRunningPushDataLoop = true; UE_LOGFMT(LogHttpServer, Log, "Starting PushUpdatedData loop"); while (SocketRunning && !bShouldStop) { + const float PushCycle = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.PushCycle"), 5.0f); + PushUpdatedData(); - FPlatformProcess::Sleep(HttpConfig.WebSocketPushCycle); + FPlatformProcess::Sleep(PushCycle); } UE_LOGFMT(LogHttpServer, Log, "Stopped PushUpdatedData loop"); bHasRunningPushDataLoop = false; @@ -192,22 +174,22 @@ void AFicsitRemoteMonitoring::StartWebSocketServer(bool bSkipIfRunning) try { auto app = uWS::App(); auto World = GetWorld(); - auto config = FConfig_HTTPStruct::GetActiveConfig(World); + + const int32 port = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.Port"), 8080); + const FString Root = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.Root"), ""); FString ModPath = FPaths::ProjectModsDir() + "FicsitRemoteMonitoring/"; FString IconsPath = ModPath + "Icons"; FString UIPath; - if (config.Web_Root.IsEmpty()) { + if (Root.IsEmpty()) { UIPath = ModPath + "www"; } else { - UIPath = config.Web_Root; + UIPath = Root; }; - int port = config.HTTP_Port; - // Define WebSocket behavior uWS::App::WebSocketBehavior wsBehavior; @@ -279,15 +261,17 @@ void AFicsitRemoteMonitoring::StartWebSocketServer(bool bSkipIfRunning) UFRM_RequestLibrary::SendErrorJson(res, "404 Not Found", ""); }); - app.get("/api/:APIEndpoint", [this, World, config](auto* res, auto* req) { + app.get("/api/:APIEndpoint", [this, World](auto* res, auto* req) { std::string url(req->getParameter("APIEndpoint")); FString Endpoint = FString(url.c_str()); // Log the request URL //UE_LOGFMT(LogHttpServer, Log, "Request URL: {0}", Endpoint); + const FString AuthToken = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.AuthenticationToken"), ""); + FRequestData RequestData; - RequestData.bIsAuthorized = IsAuthorizedRequest(req, config.Authentication_Token); + RequestData.bIsAuthorized = IsAuthorizedRequest(req, AuthToken); HandleApiRequest(World, res, req, Endpoint, RequestData); }); @@ -299,12 +283,12 @@ void AFicsitRemoteMonitoring::StartWebSocketServer(bool bSkipIfRunning) res->end(); }); - app.post("/*", [this, World, config](auto* res, uWS::HttpRequest* req) + app.post("/*", [this, World](auto* res, uWS::HttpRequest* req) { const std::string URL(req->getUrl().begin(), req->getUrl().end()); FString RelativePath = FString(URL.c_str()).Mid(1); - res->onData([this, res, req, World, RelativePath, config](const std::string_view data, bool) + res->onData([this, res, req, World, RelativePath](const std::string_view data, bool) { try { @@ -318,9 +302,11 @@ void AFicsitRemoteMonitoring::StartWebSocketServer(bool bSkipIfRunning) return UFRM_RequestLibrary::SendErrorMessage(res, "400 Bad Request", FString("Invalid Request Body")); } + const FString AuthToken = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.AuthenticationToken"), ""); + FRequestData RequestData; RequestData.Method = "POST"; - RequestData.bIsAuthorized = IsAuthorizedRequest(req, config.Authentication_Token); + RequestData.bIsAuthorized = IsAuthorizedRequest(req, AuthToken); if (JsonValue->Type == EJson::Array) { @@ -348,10 +334,12 @@ void AFicsitRemoteMonitoring::StartWebSocketServer(bool bSkipIfRunning) res->onAborted([]() {}); }); - app.get("/*", [this, UIPath, World, config](auto* res, uWS::HttpRequest* req) { + app.get("/*", [this, UIPath, World](auto* res, uWS::HttpRequest* req) { if (!res) return; std::string url(req->getUrl().begin(), req->getUrl().end()); + + const FString AuthToken = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.AuthenticationToken"), ""); bool bFileExists = false; // Remove initial '/' @@ -376,7 +364,7 @@ void AFicsitRemoteMonitoring::StartWebSocketServer(bool bSkipIfRunning) } else { FRequestData RequestData; - RequestData.bIsAuthorized = IsAuthorizedRequest(req, config.Authentication_Token); + RequestData.bIsAuthorized = IsAuthorizedRequest(req, AuthToken); HandleApiRequest(World, res, req, RelativePath, RequestData); } }); @@ -387,7 +375,12 @@ void AFicsitRemoteMonitoring::StartWebSocketServer(bool bSkipIfRunning) UE_LOG(LogHttpServer, Warning, TEXT("Attempting to listen on port %d"), port); - if (token) { + FString Reason; + if (!UFRMValidation::IsTcpPortAvailable(port, Reason)) + { + UE_LOG(LogHttpServer, Error, TEXT("Port %d unavailable: %s"), port, *Reason); + } + else if (token) { SocketListener = token; UE_LOGFMT(LogHttpServer, Warning, "Listening on port {port}", port); @@ -819,7 +812,6 @@ FString AFicsitRemoteMonitoring::FlavorTextRandomizer(EFlavorType FlavorType) { auto config = FConfig_DiscITStruct::GetActiveConfig(World); - JsonPath = config.OutageJSON; if (config.OutageJSON.IsEmpty()) { diff --git a/Source/FicsitRemoteMonitoring/Private/Libraries/FRMConfigManager.cpp b/Source/FicsitRemoteMonitoring/Private/Libraries/FRMConfigManager.cpp new file mode 100644 index 00000000..9c92215f --- /dev/null +++ b/Source/FicsitRemoteMonitoring/Private/Libraries/FRMConfigManager.cpp @@ -0,0 +1,289 @@ +#include "FRMConfigManager.h" + +#include "Misc/Variant.h" + +class UFGGameUserSettings; + +bool UFRMConfigManager::FRM_GetStoredConfigType(const FString& StrID, EVariantTypes& OutType) +{ + UFGGameUserSettings* UserSettings = UFGGameUserSettings::GetFGGameUserSettings(); + if (!UserSettings) + { + UE_LOG(LogTemp, Error, TEXT("FRM_GetStoredConfigType: UserSettings is null")); + return false; + } + + const FString FullSettingName = TEXT("FicsitRemoteMonitoring.") + StrID; + const FVariant ConfigVariant = UserSettings->GetOptionValue(FullSettingName); + OutType = ConfigVariant.GetType(); + + if (OutType == EVariantTypes::Empty) + { + UE_LOG(LogTemp, Warning, TEXT("FRM_GetStoredConfigType: Setting '%s' is empty or invalid"), *FullSettingName); + return false; + } + + return true; +} + +bool UFRMConfigManager::FRM_GetExpectedConfigType(const FString& StrID, EVariantTypes& OutType) +{ + // Explicit schema for known settings. + // This avoids being trapped by previously mis-saved string values like "9090". + if (StrID == TEXT("uWS.Port")) + { + OutType = EVariantTypes::Int32; + return true; + } + + if (StrID == TEXT("uWS.PushCycle")) + { + OutType = EVariantTypes::Float; + return true; + } + + if (StrID == TEXT("uWS.Enabled")) + { + OutType = EVariantTypes::Bool; + return true; + } + + // Fallback to whatever is currently stored. + return FRM_GetStoredConfigType(StrID, OutType); +} + +bool UFRMConfigManager::FRM_ValidateConfigValue(const FString& StrID, const FVariant& InValue, FString& OutReason) +{ + OutReason.Empty(); + + const EVariantTypes VariantType = InValue.GetType(); + + if (StrID == TEXT("uWS.Port")) + { + int32 Port = 0; + + if (VariantType == EVariantTypes::Int32) + { + Port = InValue.GetValue(); + } + else if (VariantType == EVariantTypes::String) + { + const FString StringValue = InValue.GetValue(); + if (!LexTryParseString(Port, *StringValue)) + { + OutReason = TEXT("Port must be a valid integer."); + return false; + } + } + else + { + OutReason = TEXT("Port must be an integer."); + return false; + } + + if (Port < 1 || Port > 65535) + { + OutReason = TEXT("Port must be between 1 and 65535."); + return false; + } + + return true; + } + + if (StrID == TEXT("uWS.PushCycle")) + { + float PushCycle = 0.0f; + + if (VariantType == EVariantTypes::Float) + { + PushCycle = InValue.GetValue(); + } + else if (VariantType == EVariantTypes::Int32) + { + PushCycle = static_cast(InValue.GetValue()); + } + else if (VariantType == EVariantTypes::String) + { + const FString StringValue = InValue.GetValue(); + if (!LexTryParseString(PushCycle, *StringValue)) + { + OutReason = TEXT("PushCycle must be a valid number."); + return false; + } + } + else + { + OutReason = TEXT("PushCycle must be a number."); + return false; + } + + if (PushCycle < 0.05f) + { + OutReason = TEXT("PushCycle must be at least 0.05."); + return false; + } + + return true; + } + + return true; +} + +bool UFRMConfigManager::FRM_ParseConfigInputToVariant( + const FString& StrID, + const FString& RawTextValue, + bool bCheckboxValue, + FVariant& OutVariant, + FString& OutReason +) +{ + OutReason.Empty(); + + EVariantTypes ExpectedType; + if (!FRM_GetExpectedConfigType(StrID, ExpectedType)) + { + OutReason = TEXT("Could not determine expected setting type."); + return false; + } + + switch (ExpectedType) + { + case EVariantTypes::Bool: + { + OutVariant = FVariant(bCheckboxValue); + return true; + } + + case EVariantTypes::Int32: + { + int32 ParsedValue = 0; + if (!LexTryParseString(ParsedValue, *RawTextValue)) + { + OutReason = FString::Printf(TEXT("'%s' is not a valid integer."), *RawTextValue); + UE_LOG( + LogTemp, + Warning, + TEXT("FRM_ParseConfigInputToVariant: Failed to parse int32 from '%s' for setting '%s'"), + *RawTextValue, + *StrID + ); + return false; + } + + OutVariant = FVariant(ParsedValue); + return true; + } + + case EVariantTypes::Float: + { + float ParsedValue = 0.0f; + if (!LexTryParseString(ParsedValue, *RawTextValue)) + { + OutReason = FString::Printf(TEXT("'%s' is not a valid float."), *RawTextValue); + UE_LOG( + LogTemp, + Warning, + TEXT("FRM_ParseConfigInputToVariant: Failed to parse float from '%s' for setting '%s'"), + *RawTextValue, + *StrID + ); + return false; + } + + OutVariant = FVariant(ParsedValue); + return true; + } + + case EVariantTypes::String: + { + OutVariant = FVariant(RawTextValue); + return true; + } + + default: + { + OutReason = FString::Printf(TEXT("Unsupported variant type %d."), static_cast(ExpectedType)); + UE_LOG( + LogTemp, + Warning, + TEXT("FRM_ParseConfigInputToVariant: Unsupported variant type %d for setting '%s'"), + static_cast(ExpectedType), + *StrID + ); + return false; + } + } +} + +bool UFRMConfigManager::FRM_SetConfigFromInput( + const FString& StrID, + const FString& RawTextValue, + bool bCheckboxValue +) +{ + UFGGameUserSettings* UserSettings = UFGGameUserSettings::GetFGGameUserSettings(); + if (!UserSettings) + { + UE_LOG(LogTemp, Error, TEXT("FRM_SetConfigFromInput: UserSettings is null")); + return false; + } + + FVariant ParsedVariant; + FString ParseReason; + if (!FRM_ParseConfigInputToVariant(StrID, RawTextValue, bCheckboxValue, ParsedVariant, ParseReason)) + { + UE_LOG( + LogTemp, + Warning, + TEXT("FRM_SetConfigFromInput: Failed to parse setting '%s': %s"), + *StrID, + *ParseReason + ); + return false; + } + + FString ValidationReason; + if (!FRM_ValidateConfigValue(StrID, ParsedVariant, ValidationReason)) + { + UE_LOG( + LogTemp, + Warning, + TEXT("FRM_SetConfigFromInput: Validation failed for setting '%s': %s"), + *StrID, + *ValidationReason + ); + return false; + } + + const FString FullSettingName = TEXT("FicsitRemoteMonitoring.") + StrID; + UserSettings->SetOptionValue(FullSettingName, ParsedVariant); + + UE_LOG( + LogTemp, + Log, + TEXT("FRM_SetConfigFromInput: Applied setting '%s'"), + *FullSettingName + ); + + return true; +} + +bool UFRMConfigManager::FRM_SetConfigFromInput(const FString& StrID, float Value) +{ + return FRM_SetConfigFromInput(StrID, FString::SanitizeFloat(Value), false); +} + +bool UFRMConfigManager::FRM_SetConfigFromInput(const FString& StrID, int32 Value) +{ + return FRM_SetConfigFromInput(StrID, FString::FromInt(Value), false); +} + +bool UFRMConfigManager::FRM_SetConfigFromInput(const FString& StrID, bool bValue) +{ + return FRM_SetConfigFromInput(StrID, TEXT(""), bValue); +} + +bool UFRMConfigManager::FRM_SetConfigFromInput(const FString& StrID, const FString& Value) +{ + return FRM_SetConfigFromInput(StrID, Value, false); +} \ No newline at end of file diff --git a/Source/FicsitRemoteMonitoring/Private/Libraries/Validation.cpp b/Source/FicsitRemoteMonitoring/Private/Libraries/Validation.cpp new file mode 100644 index 00000000..7e6a209a --- /dev/null +++ b/Source/FicsitRemoteMonitoring/Private/Libraries/Validation.cpp @@ -0,0 +1,47 @@ +#include "Validation.h" + +#include "Runtime/Sockets/Public/SocketSubsystem.h" +#include "Runtime/TraceLog/standalone_prologue.h" +#include "Runtime/Sockets/Public/Sockets.h" +#include "UObject/NameTypes.h" + +bool UFRMValidation::IsTcpPortAvailable(int32 Port, FString& OutReason) +{ + OutReason.Empty(); + + if (Port < 1 || Port > 65535) + { + OutReason = TEXT("Port is outside valid range 1-65535."); + return false; + } + + ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); + if (!SocketSubsystem) + { + OutReason = TEXT("Socket subsystem is unavailable."); + return false; + } + + TSharedRef Addr = SocketSubsystem->CreateInternetAddr(); + Addr->SetAnyAddress(); + Addr->SetPort(Port); + + FSocket* TestSocket = SocketSubsystem->CreateSocket(NAME_Stream, TEXT("PortAvailabilityTest"), false); + if (!TestSocket) + { + OutReason = TEXT("Failed to create test socket."); + return false; + } + + const bool bBindSucceeded = TestSocket->Bind(*Addr); + + if (!bBindSucceeded) + { + OutReason = TEXT("Bind failed. Port is likely already in use or not permitted."); + } + + TestSocket->Close(); + SocketSubsystem->DestroySocket(TestSocket); + + return bBindSucceeded; +} diff --git a/Source/FicsitRemoteMonitoring/Private/RemoteMonitoringLibrary.cpp b/Source/FicsitRemoteMonitoring/Private/RemoteMonitoringLibrary.cpp index 2543ba91..457c0fd8 100644 --- a/Source/FicsitRemoteMonitoring/Private/RemoteMonitoringLibrary.cpp +++ b/Source/FicsitRemoteMonitoring/Private/RemoteMonitoringLibrary.cpp @@ -19,6 +19,7 @@ #include "FicsitRemoteMonitoring.h" #include "FicsitRemoteMonitoringModule.h" #include "Research.h" +#include "SMLOptionsLibrary.h" #include "StructuredLog.h" #include "Components/SplineComponent.h" #include "GameFramework/PlayerState.h" @@ -299,10 +300,9 @@ void URemoteMonitoringLibrary::GetOverclockingItemsFromInventory(const UFGInvent FString URemoteMonitoringLibrary::APItoJSON(TArray> JSONArray, UObject* WorldContext) { - FString Write; - auto config = FConfig_FactoryStruct::GetActiveConfig(WorldContext); - - if (config.JSONDebugMode) { + FString Write{}; + + if (UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("Debug.JSONDebug"), false)) { const TSharedRef>> JsonWriter = TJsonWriterFactory>::Create(&Write); FJsonSerializer::Serialize(JSONArray, JsonWriter); } else { diff --git a/Source/FicsitRemoteMonitoring/Public/FRMConfigInitSubsystem.h b/Source/FicsitRemoteMonitoring/Public/FRMConfigInitSubsystem.h index d709776c..beeb57d2 100644 --- a/Source/FicsitRemoteMonitoring/Public/FRMConfigInitSubsystem.h +++ b/Source/FicsitRemoteMonitoring/Public/FRMConfigInitSubsystem.h @@ -32,5 +32,4 @@ class FICSITREMOTEMONITORING_API UFRMConfigInitSubsystem : public UGameInstanceS FConfig_FactoryStruct FactoryConfig{}; FString GenerateAuthToken(int32 Length); - void SaveHttpAuthToken(UConfigManager* ConfigManager); }; diff --git a/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h b/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h index 7e095d31..86eea4ab 100644 --- a/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h +++ b/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h @@ -164,6 +164,9 @@ class FICSITREMOTEMONITORING_API AFicsitRemoteMonitoring : public AModSubsystem UFUNCTION(BlueprintImplementableEvent, Category = "Ficsit Remote Monitoring") void InitSerialDevice(); + UFUNCTION(BlueprintImplementableEvent, Category = "Ficsit Remote Monitoring") + void StopSerialDevice(); + void HandleApiRequest(UObject* World, uWS::HttpResponse* res, uWS::HttpRequest* req, FString Endpoint, FRequestData RequestData); void InitAPIRegistry(); diff --git a/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoringModule.h b/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoringModule.h index a8c3c27d..314ef7a7 100644 --- a/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoringModule.h +++ b/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoringModule.h @@ -9,6 +9,7 @@ DECLARE_LOG_CATEGORY_EXTERN(LogArduino, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogFRMAPI, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogFRMNotification, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogFRMDebug, Log, All); +DECLARE_LOG_CATEGORY_EXTERN(LogFRMConfig, Log, All); DECLARE_LOG_CATEGORY_EXTERN(LogWebSocketServer, Log, All); diff --git a/Source/FicsitRemoteMonitoring/Public/Libraries/FRMConfigManager.h b/Source/FicsitRemoteMonitoring/Public/Libraries/FRMConfigManager.h new file mode 100644 index 00000000..04ff32d7 --- /dev/null +++ b/Source/FicsitRemoteMonitoring/Public/Libraries/FRMConfigManager.h @@ -0,0 +1,203 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Misc/Variant.h" +#include +#include "FGGameUserSettings.h" +#include "../FicsitRemoteMonitoringModule.h" +#include "FRMConfigManager.generated.h" + +class UFGGameUserSettings; + +UCLASS() +class FICSITREMOTEMONITORING_API UFRMConfigManager : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + + /** Gets a config value and converts it to the requested type if possible. */ + template + static bool FRM_GetConfig(const FString& StrID, T& OutValue) + { + UFGGameUserSettings* UserSettings = UFGGameUserSettings::GetFGGameUserSettings(); + if (!UserSettings) + { + UE_LOG(LogTemp, Error, TEXT("FRM_GetConfig: UserSettings is null")); + return false; + } + + const FString FullSettingName = TEXT("FicsitRemoteMonitoring.") + StrID; + const FVariant ConfigVariant = UserSettings->GetOptionValue(FullSettingName); + const EVariantTypes VariantType = ConfigVariant.GetType(); + + if constexpr (std::is_same_v) + { + if (VariantType == EVariantTypes::Float) + { + OutValue = ConfigVariant.GetValue(); + return true; + } + + if (VariantType == EVariantTypes::Int32) + { + OutValue = static_cast(ConfigVariant.GetValue()); + return true; + } + + if (VariantType == EVariantTypes::String) + { + float ParsedValue = 0.0f; + const FString StringValue = ConfigVariant.GetValue(); + if (LexTryParseString(ParsedValue, *StringValue)) + { + OutValue = ParsedValue; + return true; + } + } + } + else if constexpr (std::is_same_v) + { + if (VariantType == EVariantTypes::Int32) + { + OutValue = ConfigVariant.GetValue(); + return true; + } + + if (VariantType == EVariantTypes::Float) + { + OutValue = static_cast(ConfigVariant.GetValue()); + return true; + } + + if (VariantType == EVariantTypes::String) + { + int32 ParsedValue = 0; + const FString StringValue = ConfigVariant.GetValue(); + if (LexTryParseString(ParsedValue, *StringValue)) + { + OutValue = ParsedValue; + return true; + } + } + } + else if constexpr (std::is_same_v) + { + if (VariantType == EVariantTypes::Bool) + { + OutValue = ConfigVariant.GetValue(); + return true; + } + + if (VariantType == EVariantTypes::String) + { + const FString StringValue = ConfigVariant.GetValue(); + + if (StringValue.Equals(TEXT("true"), ESearchCase::IgnoreCase) || + StringValue.Equals(TEXT("1"), ESearchCase::IgnoreCase)) + { + OutValue = true; + return true; + } + + if (StringValue.Equals(TEXT("false"), ESearchCase::IgnoreCase) || + StringValue.Equals(TEXT("0"), ESearchCase::IgnoreCase)) + { + OutValue = false; + return true; + } + } + } + else if constexpr (std::is_same_v) + { + if (VariantType == EVariantTypes::String) + { + OutValue = ConfigVariant.GetValue(); + return true; + } + + if (VariantType == EVariantTypes::Int32) + { + OutValue = FString::FromInt(ConfigVariant.GetValue()); + return true; + } + + if (VariantType == EVariantTypes::Float) + { + OutValue = FString::SanitizeFloat(ConfigVariant.GetValue()); + return true; + } + + if (VariantType == EVariantTypes::Bool) + { + OutValue = ConfigVariant.GetValue() ? TEXT("true") : TEXT("false"); + return true; + } + } + else + { + static_assert( + std::is_same_v || + std::is_same_v || + std::is_same_v || + std::is_same_v, + "FRM_GetConfig only supports float, int32, bool, and FString." + ); + } + + UE_LOG( + LogTemp, + Warning, + TEXT("FRM_GetConfig: Invalid type for setting '%s'. Variant type: %d"), + *FullSettingName, + static_cast(VariantType) + ); + + return false; + } + + /** Gets a config value or returns a fallback default. */ + template + static T FRM_GetConfigOrDefault(const FString& StrID, const T& DefaultValue) + { + T Value = DefaultValue; + FRM_GetConfig(StrID, Value); + return Value; + } + + /** Gets the raw current variant type of a setting. */ + static bool FRM_GetStoredConfigType(const FString& StrID, EVariantTypes& OutType); + + /** Gets the expected variant type for a known setting. Falls back to the stored type if not explicitly mapped. */ + static bool FRM_GetExpectedConfigType(const FString& StrID, EVariantTypes& OutType); + + /** Validates a parsed variant value for a specific setting. */ + static bool FRM_ValidateConfigValue(const FString& StrID, const FVariant& InValue, FString& OutReason); + + /** + * Converts UI input into a correctly typed FVariant for the target setting. + * RawTextValue is used for String / Int32 / Float settings. + * bCheckboxValue is used for Bool settings. + */ + static bool FRM_ParseConfigInputToVariant( + const FString& StrID, + const FString& RawTextValue, + bool bCheckboxValue, + FVariant& OutVariant, + FString& OutReason + ); + + /** Applies a parsed value to the setting if the input is valid. */ + static bool FRM_SetConfigFromInput( + const FString& StrID, + const FString& RawTextValue, + bool bCheckboxValue + ); + + /** Convenience overloads */ + static bool FRM_SetConfigFromInput(const FString& StrID, float Value); + static bool FRM_SetConfigFromInput(const FString& StrID, int32 Value); + static bool FRM_SetConfigFromInput(const FString& StrID, bool bValue); + static bool FRM_SetConfigFromInput(const FString& StrID, const FString& Value); +}; \ No newline at end of file diff --git a/Source/FicsitRemoteMonitoring/Public/Libraries/Validation.h b/Source/FicsitRemoteMonitoring/Public/Libraries/Validation.h new file mode 100644 index 00000000..d083621d --- /dev/null +++ b/Source/FicsitRemoteMonitoring/Public/Libraries/Validation.h @@ -0,0 +1,11 @@ +#pragma once +#include "Kismet/BlueprintFunctionLibrary.h" +#include "Validation.generated.h" + +UCLASS() +class FICSITREMOTEMONITORING_API UFRMValidation : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() +public: + static bool IsTcpPortAvailable(int32 Port, FString& OutReason); +}; diff --git a/Source/FicsitRemoteMonitoring/Public/RemoteMonitoringLibrary.h b/Source/FicsitRemoteMonitoring/Public/RemoteMonitoringLibrary.h index 6b606dfd..33e977f8 100644 --- a/Source/FicsitRemoteMonitoring/Public/RemoteMonitoringLibrary.h +++ b/Source/FicsitRemoteMonitoring/Public/RemoteMonitoringLibrary.h @@ -5,8 +5,8 @@ #include "Dom/JsonObject.h" #include "Dom/JsonValue.h" #include "FRM_RequestData.h" -#include "SessionSettingsManager.h" #include "Components/SplineComponent.h" +#include "Libraries/FRMConfigManager.h" #include "RemoteMonitoringLibrary.generated.h" struct FSplinePointData; diff --git a/Source/FicsitRemoteMonitoringServer/FicsitRemoteMonitoringServer.build.cs b/Source/FicsitRemoteMonitoringServer/FicsitRemoteMonitoringServer.build.cs index 5976b616..baae7573 100644 --- a/Source/FicsitRemoteMonitoringServer/FicsitRemoteMonitoringServer.build.cs +++ b/Source/FicsitRemoteMonitoringServer/FicsitRemoteMonitoringServer.build.cs @@ -19,6 +19,8 @@ public FicsitRemoteMonitoringServer(ReadOnlyTargetRules Target) : base(Target) "Json", "JsonUtilities", "FactoryGame", + "Sockets", + "Networking", "SML", "Chaos", "ChaosVehiclesCore", "ChaosVehicles", "ChaosSolverEngine", @@ -30,7 +32,8 @@ public FicsitRemoteMonitoringServer(ReadOnlyTargetRules Target) : base(Target) PrivateDependencyModuleNames.AddRange(new string[] { "HTTP", - "HTTPServer" + "HTTPServer", + "FicsitRemoteMonitoring" }); // Enable exception handling diff --git a/Source/FicsitRemoteMonitoringServer/Private/FicsitRemoteMonitoringServer.cpp b/Source/FicsitRemoteMonitoringServer/Private/FicsitRemoteMonitoringServer.cpp index c1a4be0a..ecbfd8ba 100644 --- a/Source/FicsitRemoteMonitoringServer/Private/FicsitRemoteMonitoringServer.cpp +++ b/Source/FicsitRemoteMonitoringServer/Private/FicsitRemoteMonitoringServer.cpp @@ -2,7 +2,7 @@ #include "FGServerAPIManager.h" #include "FGServerSubsystem.h" -#include "../../FicsitRemoteMonitoring/Public/Configs/Config_HTTPStruct.h" +#include "../../FicsitRemoteMonitoring/Public/Libraries/FRMConfigManager.h" #include "../../FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h" class UFGServerSubsystem; @@ -15,9 +15,13 @@ void AFicsitRemoteMonitoringServer::BeginPlay() const auto World = this->GetWorld(); this->Controller = NewObject(); this->Controller->World = World; + AFicsitRemoteMonitoring* ModSubsystem = AFicsitRemoteMonitoring::Get(World); this->Controller->ModSubsystem = ModSubsystem; - this->Controller->AuthToken = FConfig_HTTPStruct::GetActiveConfig(World).Authentication_Token; + + const FString AuthToken = UFRMConfigManager::FRM_GetConfigOrDefault(TEXT("uWS.AuthenticationToken"), ""); + + this->Controller->AuthToken = AuthToken; if (World == nullptr) return;