diff --git a/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp b/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp index 5d552abe4380c30bce9b717b4659481ecdb6e30e..256a7f7cea10ef46568a20b58c4a72824e073b3d 100644 --- a/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp +++ b/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp @@ -1,195 +1,265 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -#include "NDisplayLaunchButton.h" - -#include "NDisplayLaunchButtonStyle.h" -#include "NDisplayLaunchButtonSettings.h" -#include "NDisplayLaunchButtonCommands.h" -#include "Misc/MessageDialog.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" -#include "Interfaces/IPluginManager.h" -#include "Misc/EngineVersion.h" -#include "Networking.h" -#include "SocketHelper.h" -#include "LevelEditor.h" -#include "FileHelpers.h" - -DEFINE_LOG_CATEGORY(LogNDisplayLaunchButton); - -void FNDisplayLaunchButtonModule::StartupModule() -{ - const UNDisplayLaunchButtonSettings* Settings = GetDefault<UNDisplayLaunchButtonSettings>(); - FNDisplayLaunchButtonStyle::Initialize(); - FNDisplayLaunchButtonStyle::ReloadTextures(); - FNDisplayLaunchButtonCommands::Register(); - - TSharedPtr<class FUICommandList> PluginCommands = MakeShareable(new FUICommandList); - PluginCommands->MapAction( - FNDisplayLaunchButtonCommands::Get().PluginAction, - FExecuteAction::CreateRaw(this, &FNDisplayLaunchButtonModule::PluginButtonClicked), - FCanExecuteAction()); - - FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor"); - TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender); - ToolbarExtender->AddToolBarExtension("Game", EExtensionHook::First, PluginCommands, FToolBarExtensionDelegate::CreateLambda([](FToolBarBuilder& Builder) - { - Builder.AddToolBarButton(FNDisplayLaunchButtonCommands::Get().PluginAction); - })); - LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); -} - -void FNDisplayLaunchButtonModule::ShutdownModule() -{ - FNDisplayLaunchButtonStyle::Shutdown(); - FNDisplayLaunchButtonCommands::Unregister(); -} - -void FNDisplayLaunchButtonModule::PluginButtonClicked() -{ - const UNDisplayLaunchButtonSettings* Settings = GetDefault<UNDisplayLaunchButtonSettings>(); - if (Settings->LaunchType == ButtonLaunchType_NONE) - { - GEngine->AddOnScreenDebugMessage(-1, 3, FColor::White, TEXT("The Button is set to do nothing.")); - return; - } - - if(FModuleManager::Get().IsModuleLoaded("SteamVR") || FModuleManager::Get().IsModuleLoaded("OculusHMD")) - { - FText Title = FText::FromString("VR Plugin enabled."); - FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("You have either SteamVR or OculusVR enabled in your project. This prevents NDisplay from working. You can switch them off in the 'Plugin' menu."), &Title); - return; - } - - if (!UEditorLoadingAndSavingUtils::SaveDirtyPackagesWithDialog(true, true)) return; - - // minimize the root window to provide max performance for the preview. - TSharedPtr<SWindow> RootWindow = FGlobalTabmanager::Get()->GetRootWindow(); - if (RootWindow.IsValid()) RootWindow->Minimize(); - - if (Settings->LaunchType == ButtonLaunchType_ROLV) - { - FString EditorExecutable = "UE4Editor.exe"; - FString Parameters = "\"" + FPaths::GetProjectFilePath() + "\" -game dc_cfg=\"" + Settings->RolvConfig.FilePath + "\"" + ((Settings->ROLVLogToProjectDir) ? (" -log ABSLOG=" + FPaths::ProjectDir() + "\\ROLV_Launch.log") : ""); - - FProcHandle VRPN; - ProjectorDisplayType ModeFromBefore = DisplayType_Error; - if (Settings->StartVRPN) VRPN = FPlatformProcess::CreateProc(*Settings->VRPNPath.FilePath, *FString("-f \"" + Settings->VRPNConfigPath.FilePath + "\" -millisleep 0"), false, false, false, nullptr, 0, nullptr, nullptr); - if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking start\0"); - if (Settings->SwitchProjector) ModeFromBefore = SwitchProjectorToState(Settings->ProjectorIP, Settings->ProjectorPort, Settings->ProjectorType); - - FProcHandle Instance = FPlatformProcess::CreateProc(*EditorExecutable, *(Parameters + " " + Settings->ROLVLaunchParameters), true, false, false, nullptr, 0, nullptr, nullptr); - FPlatformProcess::WaitForProc(Instance); - - if (Settings->StartVRPN) FPlatformProcess::TerminateProc(VRPN); - FPlatformProcess::CloseProc(VRPN); - if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking stop\0"); - if (Settings->SwitchProjector && ModeFromBefore != DisplayType_Error) SwitchProjectorToState(Settings->ProjectorIP, Settings->ProjectorPort, ModeFromBefore); - } - - if (Settings->LaunchType == ButtonLaunchType_MiniCAVE) - { - FString Config = IPluginManager::Get().FindPlugin("NDisplayLaunchButton")->GetBaseDir() + "/LaunchConfig/minicave.cfg"; - FString EditorExecutable = "UE4Editor.exe"; - FString Parameters = "\"" + FPaths::GetProjectFilePath() + "\" -game dc_cfg=\"" + Config + "\" " + Settings->MiniCAVELaunchParameters; - - const int Num_Nodes = 5; - FString Windows_Node_Specific_Commands[Num_Nodes] = { - "dc_node=node_floor WinX=720 WinY=300 ResX=480 ResY=480" + FString((Settings->MiniCAVELogMasterWindow) ? " -log" : "") + ((Settings->MiniCAVELogToProjectDirFloor) ? (" ABSLOG=" + FPaths::ProjectDir() + "\\MiniCave_Floor_Master.log") : "") + " " + Settings->MiniCAVEAdditionalLaunchParametersMaster, - "dc_node=node_front WinX=720 WinY=0 ResX=480 ResY=300" + ((Settings->MiniCAVELogToProjectDirFront) ? (" ABSLOG=" + FPaths::ProjectDir() + "\\MiniCave_Front.log") : ""), - "dc_node=node_left WinX=420 WinY=300 ResX=300 ResY=480" + ((Settings->MiniCAVELogToProjectDirLeft) ? (" ABSLOG=" + FPaths::ProjectDir() + "\\MiniCave_Left.log") : ""), - "dc_node=node_right WinX=1200 WinY=300 ResX=300 ResY=480" + ((Settings->MiniCAVELogToProjectDirRight) ? (" ABSLOG=" + FPaths::ProjectDir() + "\\MiniCave_Right.log") : ""), - "dc_node=node_back WinX=720 WinY=780 ResX=480 ResY=300" + ((Settings->MiniCAVELogToProjectDirBack) ? (" ABSLOG=" + FPaths::ProjectDir() + "\\MiniCave_Back.log") : "") - }; - - FProcHandle Processes[Num_Nodes]; - for (int i = 0; i < Num_Nodes; i++) - { - Processes[i] = FPlatformProcess::CreateProc(*EditorExecutable, *(Parameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); - } - FPlatformProcess::WaitForProc(Processes[0]); //wait for only one of them - } - - if (Settings->LaunchType == ButtonLaunchType_TWO_SCREEN) - { - FString Config = IPluginManager::Get().FindPlugin("NDisplayLaunchButton")->GetBaseDir() + "/LaunchConfig/twoscreen.cfg"; - FString EditorExecutable = "UE4Editor.exe"; - FString Parameters = "\"" + FPaths::GetProjectFilePath() + "\" -game dc_cfg=\"" + Config + "\" " + Settings->TwoScreenLaunchParameters; - - const int Num_Nodes = 2; - FString Windows_Node_Specific_Commands[Num_Nodes] = { - "dc_node=node_left WinX=200 WinY=200 ResX=480 ResY=480" + FString((Settings->TwoScreenLogMasterWindow) ? " -log" : "") + ((Settings->TwoScreenLogToProjectDirLeft) ? (" ABSLOG=" + FPaths::ProjectDir() + "\\TwoScreen_Left_Master.log") : "") + " " + Settings->TwoScreenAdditionalLaunchParametersMaster, - "dc_node=node_right WinX=682 WinY=200 ResX=480 ResY=480" + ((Settings->TwoScreenLogToProjectDirRight) ? (" ABSLOG = " + FPaths::ProjectDir() + "\\TwoScreen_Right.log") : "") - }; - - FProcHandle Processes[Num_Nodes]; - for (int i = 0; i < Num_Nodes; i++) - { - Processes[i] = FPlatformProcess::CreateProc(*EditorExecutable, *(Parameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); - } - FPlatformProcess::WaitForProc(Processes[0]); //wait for only one of them - } - - if (Settings->LaunchType == ButtonLaunchType_CAVE) - { - FProcHandle Instance = FPlatformProcess::CreateProc( - *Settings->CAVELaunchScriptPath.FilePath, - *("\"" + (FPaths::ConvertRelativePathToFull(".") + "/UE4Editor\"") - + " " + FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath()) - + " " + FString::FromInt(FEngineVersion::Current().GetMajor()) - + FString::FromInt(FEngineVersion::Current().GetMinor())), - true, false, false, nullptr, 0, nullptr, nullptr); - FPlatformProcess::WaitForProc(Instance); - } - - if (RootWindow.IsValid()) RootWindow->Maximize(); -} - -void FNDisplayLaunchButtonModule::SendToDTrack(FString Address, int Port, FString Message) -{ - FSocket* Socket = USocketHelper::OpenSocket(Address, Port, "DTrackSocket"); - if (!Socket) return; - if (USocketHelper::SendSocket(Socket, Message) <= 0) return; - FString Response = USocketHelper::ReceiveSocket<100>(Socket); - if (Response.Compare("dtrack2 ok") != 0) - { - UE_LOG(LogTemp, Error, TEXT("DTrack Command Failed. Response: '%s'"), *Response); - } - Socket->Shutdown(ESocketShutdownMode::ReadWrite); - Socket->Close(); -} - -//Returns old state -ProjectorDisplayType FNDisplayLaunchButtonModule::SwitchProjectorToState(FString Address, int Port, ProjectorDisplayType State) -{ - ProjectorDisplayType ModeBefore = ProjectorDisplayType::DisplayType_Error; - FSocket* Socket = USocketHelper::OpenSocket(Address, Port, "ProjectorSocket"); - if (!Socket) return ModeBefore; - - //Get mode from before - if (USocketHelper::SendSocket(Socket, ":TDSM ?\r") <= 0) return ModeBefore; - FString Response = USocketHelper::ReceiveSocket<100>(Socket); //Response looks like: %001 TDSM 000000 - - if (!Response.IsEmpty()) - { - int32 Position = 0; - Response.FindLastChar(' ', Position); - ModeBefore = static_cast<ProjectorDisplayType>(FCString::Atoi(*Response.RightChop(Position))); - } - - if (Response.IsEmpty() || USocketHelper::SendSocket(Socket, ":TDSM " + FString::FromInt(State) + "\r") <= 0) - { - Socket->Shutdown(ESocketShutdownMode::ReadWrite); - Socket->Close(); - return ModeBefore; - } - Response = USocketHelper::ReceiveSocket<100>(Socket); - if (Response.EndsWith(")")) - { - //Errors are marked like this - UE_LOG(LogTemp, Error, TEXT("Projector Type Change Failed. Response: '%s'"), *Response); - } - Socket->Shutdown(ESocketShutdownMode::ReadWrite); - Socket->Close(); - return ModeBefore; -} - -IMPLEMENT_MODULE(FNDisplayLaunchButtonModule, NDisplayLaunchButton) +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +#include "NDisplayLaunchButton.h" + +#include "NDisplayLaunchButtonStyle.h" +#include "NDisplayLaunchButtonSettings.h" +#include "NDisplayLaunchButtonCommands.h" +#include "Misc/MessageDialog.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" +#include "Interfaces/IPluginManager.h" +#include "Misc/EngineVersion.h" +#include "Networking.h" +#include "SocketHelper.h" +#include "LevelEditor.h" +#include "FileHelpers.h" +#include "GameProjectGeneration/Public/GameProjectGenerationModule.h" +#include "Interfaces/IProjectManager.h" + +DEFINE_LOG_CATEGORY(LogNDisplayLaunchButton); + +void FNDisplayLaunchButtonModule::StartupModule() +{ + const UNDisplayLaunchButtonSettings* Settings = GetDefault<UNDisplayLaunchButtonSettings>(); + FNDisplayLaunchButtonStyle::Initialize(); + FNDisplayLaunchButtonStyle::ReloadTextures(); + FNDisplayLaunchButtonCommands::Register(); + + TSharedPtr<class FUICommandList> PluginCommands = MakeShareable(new FUICommandList); + PluginCommands->MapAction( + FNDisplayLaunchButtonCommands::Get().PluginAction, + FExecuteAction::CreateRaw(this, &FNDisplayLaunchButtonModule::PluginButtonClicked), + FCanExecuteAction()); + + FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor"); + TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender); + ToolbarExtender->AddToolBarExtension("Game", EExtensionHook::First, PluginCommands, FToolBarExtensionDelegate::CreateLambda([](FToolBarBuilder& Builder) + { + Builder.AddToolBarButton(FNDisplayLaunchButtonCommands::Get().PluginAction); + })); + LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); +} + +void FNDisplayLaunchButtonModule::ShutdownModule() +{ + FNDisplayLaunchButtonStyle::Shutdown(); + FNDisplayLaunchButtonCommands::Unregister(); +} + +/** + * Since some plugins interfere with nDisplay execution, this function can disable them + * @param PluginName - Name of the plugin to enable/disable + * @param NewState - The state to switch the plugin to + * @param OldState - Here the old state is stored AFTER the change + * @return true on Success + */ +bool FNDisplayLaunchButtonModule::ChangePluginStateAndStoreConfig(const FString PluginName, const bool NewState, bool& OldState) const +{ + const bool PreviousState = IPluginManager::Get().FindPlugin(PluginName)->IsEnabled(); + + FText FailMessage; + FGameProjectGenerationModule::Get().TryMakeProjectFileWriteable(FPaths::GetProjectFilePath()); + if (!IProjectManager::Get().SetPluginEnabled(PluginName, NewState, FailMessage)) + { + FMessageDialog::Open(EAppMsgType::Ok, FailMessage); + return false; + }else + { + if (!IProjectManager::Get().SaveCurrentProjectToDisk(FailMessage)) + { + FMessageDialog::Open(EAppMsgType::Ok, FailMessage); + return false; + } + } + + OldState = PreviousState; + return true; +} + +/** + * Get filename of the editor executable + */ +FString FNDisplayLaunchButtonModule::GetEditorExecutableName() +{ + return "UE4Editor.exe"; +} + +/** + * Get filename in the root dir of the project + */ +FString FNDisplayLaunchButtonModule::GetFilePathInProject(FString FileName) +{ + return FPaths::Combine(FPaths::ProjectDir(), FileName); +} + +/** + * Get the path to one of the bundled configs + * @param ConfigName - Name of the config without extension + * @return Path as FString + */ +FString FNDisplayLaunchButtonModule::GetConfigPath(FString ConfigName) +{ + return FPaths::Combine(IPluginManager::Get().FindPlugin("NDisplayLaunchButton")->GetBaseDir(), "LaunchConfig", ConfigName + ".cfg"); +} + +/** + * Executed on click of the button in the toolbar + */ +void FNDisplayLaunchButtonModule::PluginButtonClicked() +{ + const UNDisplayLaunchButtonSettings* Settings = GetDefault<UNDisplayLaunchButtonSettings>(); + /* Check if we need to do something */ + if (Settings->LaunchType == ButtonLaunchType_NONE) + { + GEngine->AddOnScreenDebugMessage(-1, 3, FColor::White, TEXT("The Button is set to do nothing.")); + return; + } + + /* Disable Plugins temporarily */ + if(!ChangePluginStateAndStoreConfig("SteamVR", false, SteamVRState)) return; + if(!ChangePluginStateAndStoreConfig("OculusHMD", false, OculusVRState)) return; + + /* Trigger Editor save. Needed, else old version will be launched every time */ + if (!UEditorLoadingAndSavingUtils::SaveDirtyPackagesWithDialog(true, true)) return; + + /* minimize the root window to provide max performance for the previews. */ + TSharedPtr<SWindow> RootWindow = FGlobalTabmanager::Get()->GetRootWindow(); + if (RootWindow.IsValid()) RootWindow->Minimize(); + + /* Different Launch Types */ + if (Settings->LaunchType == ButtonLaunchType_ROLV) + { + FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *Settings->RolvConfig.FilePath, *((Settings->ROLVLogToProjectDir) ? ("-log ABSLOG=" + GetFilePathInProject("ROLV_Launch.log")) : "")); + + FProcHandle VRPN; + ProjectorDisplayType ModeFromBefore = DisplayType_Error; + if (Settings->StartVRPN) VRPN = FPlatformProcess::CreateProc(*Settings->VRPNPath.FilePath, *FString::Printf(TEXT("-f \"%s\" -millisleep 0"), *Settings->VRPNConfigPath.FilePath), false, false, false, nullptr, 0, nullptr, nullptr); + if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking start\0"); + if (Settings->SwitchProjector) ModeFromBefore = SwitchProjectorToState(Settings->ProjectorIP, Settings->ProjectorPort, Settings->ProjectorType); + + FProcHandle Instance = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + Settings->ROLVLaunchParameters), true, false, false, nullptr, 0, nullptr, nullptr); + FPlatformProcess::WaitForProc(Instance); + + if (Settings->StartVRPN) FPlatformProcess::TerminateProc(VRPN); + FPlatformProcess::CloseProc(VRPN); + if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking stop\0"); + if (Settings->SwitchProjector && ModeFromBefore != DisplayType_Error) SwitchProjectorToState(Settings->ProjectorIP, Settings->ProjectorPort, ModeFromBefore); + } + else if (Settings->LaunchType == ButtonLaunchType_MiniCAVE) + { + const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("minicave"), *Settings->MiniCAVELaunchParameters); + + const int Num_Nodes = 5; + FString Windows_Node_Specific_Commands[Num_Nodes] = { + "dc_node=node_floor WinX=720 WinY=300 ResX=480 ResY=480" + FString((Settings->MiniCAVELogMasterWindow) ? " -log" : "") + ((Settings->MiniCAVELogToProjectDirFloor) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Floor_Master.log")) : "") + " " + Settings->MiniCAVEAdditionalLaunchParametersMaster, + "dc_node=node_front WinX=720 WinY=0 ResX=480 ResY=300" + ((Settings->MiniCAVELogToProjectDirFront) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Front.log")) : ""), + "dc_node=node_left WinX=420 WinY=300 ResX=300 ResY=480" + ((Settings->MiniCAVELogToProjectDirLeft) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Left.log" )) : ""), + "dc_node=node_right WinX=1200 WinY=300 ResX=300 ResY=480" + ((Settings->MiniCAVELogToProjectDirRight) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Right.log")) : ""), + "dc_node=node_back WinX=720 WinY=780 ResX=480 ResY=300" + ((Settings->MiniCAVELogToProjectDirBack) ? (" ABSLOG=" + GetFilePathInProject("MiniCave_Back.log" )) : "") + }; + + FProcHandle Processes[Num_Nodes]; + for (int i = 0; i < Num_Nodes; i++) + { + Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); + } + FPlatformProcess::WaitForProc(Processes[0]); //wait for only one of them + } + else if (Settings->LaunchType == ButtonLaunchType_TWO_SCREEN) + { + const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("twoscreen"), *Settings->TwoScreenLaunchParameters); + + const int Num_Nodes = 2; + FString Windows_Node_Specific_Commands[Num_Nodes] = { + "dc_node=node_left WinX=200 WinY=200 ResX=480 ResY=480" + FString((Settings->TwoScreenLogMasterWindow) ? " -log" : "") + ((Settings->TwoScreenLogToProjectDirLeft) ? (" ABSLOG=" + GetFilePathInProject("TwoScreen_Left_Master.log")) : "") + " " + Settings->TwoScreenAdditionalLaunchParametersMaster, + "dc_node=node_right WinX=682 WinY=200 ResX=480 ResY=480" + ((Settings->TwoScreenLogToProjectDirRight) ? (" ABSLOG = " + GetFilePathInProject("TwoScreen_Right.log")) : "") + }; + + FProcHandle Processes[Num_Nodes]; + for (int i = 0; i < Num_Nodes; i++) + { + Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr); + } + FPlatformProcess::WaitForProc(Processes[0]); //wait for only one of them + } + else if (Settings->LaunchType == ButtonLaunchType_CAVE) + { + FProcHandle Instance = FPlatformProcess::CreateProc( + *Settings->CAVELaunchScriptPath.FilePath, + *("\"" + (FPaths::ConvertRelativePathToFull(".") + "/UE4Editor\"") + + " " + FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath()) + + " " + FString::FromInt(FEngineVersion::Current().GetMajor()) + + FString::FromInt(FEngineVersion::Current().GetMinor())), + true, false, false, nullptr, 0, nullptr, nullptr); + FPlatformProcess::WaitForProc(Instance); + } + + /* Re-maximize Editor Window */ + if (RootWindow.IsValid()) RootWindow->Maximize(); + + /* Restore Plugin States */ + ChangePluginStateAndStoreConfig("SteamVR", SteamVRState, SteamVRState); + ChangePluginStateAndStoreConfig("OculusHMD", OculusVRState, OculusVRState); +} + +/** + * Send a command to the DTrack controller + * @param Address - IP address of DTrack controller + * @param Port - Port to contact DTrack controller + * @param Message - Message to send + */ +void FNDisplayLaunchButtonModule::SendToDTrack(FString Address, int Port, FString Message) +{ + FSocket* Socket = USocketHelper::OpenSocket(Address, Port, "DTrackSocket"); + if (!Socket) return; + if (USocketHelper::SendSocket(Socket, Message) <= 0) return; + FString Response = USocketHelper::ReceiveSocket<100>(Socket); + if (Response.Compare("dtrack2 ok") != 0) + { + UE_LOG(LogTemp, Error, TEXT("DTrack Command Failed. Response: '%s'"), *Response); + } + Socket->Shutdown(ESocketShutdownMode::ReadWrite); + Socket->Close(); +} + +/** + * Switch the display mode of the projector a 3D display mode or back + * @param Address - IP address of projector + * @param Port - Port to contact projector + * @param State - Switch to this state + * @return state of the projector before the change + */ +ProjectorDisplayType FNDisplayLaunchButtonModule::SwitchProjectorToState(FString Address, int Port, ProjectorDisplayType State) +{ + ProjectorDisplayType ModeBefore = ProjectorDisplayType::DisplayType_Error; + FSocket* Socket = USocketHelper::OpenSocket(Address, Port, "ProjectorSocket"); + if (!Socket) return ModeBefore; + + //Get mode from before + if (USocketHelper::SendSocket(Socket, ":TDSM ?\r") <= 0) return ModeBefore; + FString Response = USocketHelper::ReceiveSocket<100>(Socket); //Response looks like: %001 TDSM 000000 + + if (!Response.IsEmpty()) + { + int32 Position = 0; + Response.FindLastChar(' ', Position); + ModeBefore = static_cast<ProjectorDisplayType>(FCString::Atoi(*Response.RightChop(Position))); + } + + if (Response.IsEmpty() || USocketHelper::SendSocket(Socket, ":TDSM " + FString::FromInt(State) + "\r") <= 0) + { + Socket->Shutdown(ESocketShutdownMode::ReadWrite); + Socket->Close(); + return ModeBefore; + } + Response = USocketHelper::ReceiveSocket<100>(Socket); + if (Response.EndsWith(")")) + { + //Errors are marked like this + UE_LOG(LogTemp, Error, TEXT("Projector Type Change Failed. Response: '%s'"), *Response); + } + Socket->Shutdown(ESocketShutdownMode::ReadWrite); + Socket->Close(); + return ModeBefore; +} + +IMPLEMENT_MODULE(FNDisplayLaunchButtonModule, NDisplayLaunchButton) diff --git a/Source/NDisplayLaunchButton/Public/NDisplayLaunchButton.h b/Source/NDisplayLaunchButton/Public/NDisplayLaunchButton.h index f23187186584483a8b8659954c52891ecfc35c9f..a0be9e65472caf84d7a54ba31d4a71e1a79b763f 100644 --- a/Source/NDisplayLaunchButton/Public/NDisplayLaunchButton.h +++ b/Source/NDisplayLaunchButton/Public/NDisplayLaunchButton.h @@ -1,29 +1,36 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Modules/ModuleManager.h" -#include "NDisplayLaunchButtonSettings.h" -#include "Logging/LogMacros.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" - -class FToolBarBuilder; -class FMenuBuilder; - -DECLARE_LOG_CATEGORY_EXTERN(LogNDisplayLaunchButton, Log, All); - -class FNDisplayLaunchButtonModule : public IModuleInterface -{ -public: - - /** IModuleInterface implementation */ - virtual void StartupModule() override; - virtual void ShutdownModule() override; - - /** This function will be bound to Command. */ - void PluginButtonClicked(); - - void SendToDTrack(FString Address, int Port, FString Message); - ProjectorDisplayType SwitchProjectorToState(FString Address, int Port, ProjectorDisplayType State); -}; +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" +#include "NDisplayLaunchButtonSettings.h" +#include "Logging/LogMacros.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" + +class FToolBarBuilder; +class FMenuBuilder; + +DECLARE_LOG_CATEGORY_EXTERN(LogNDisplayLaunchButton, Log, All); + +class FNDisplayLaunchButtonModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + bool ChangePluginStateAndStoreConfig(FString PluginName, bool NewState, bool& OldState) const; + static FString GetEditorExecutableName(); + static FString GetFilePathInProject(FString FileName); + static FString GetConfigPath(FString ConfigName); + + /** This function will be bound to Command. */ + void PluginButtonClicked(); + + void SendToDTrack(FString Address, int Port, FString Message); + ProjectorDisplayType SwitchProjectorToState(FString Address, int Port, ProjectorDisplayType State); +private: + bool SteamVRState = false; + bool OculusVRState = false; +}; diff --git a/Source/NDisplayLaunchButton/Public/NDisplayLaunchButtonSettings.h b/Source/NDisplayLaunchButton/Public/NDisplayLaunchButtonSettings.h index 3c298512948f5e6245e5f1e7aa18442739472377..fd45cae835cdbe1caafde73eaf6928dd1a2058da 100644 --- a/Source/NDisplayLaunchButton/Public/NDisplayLaunchButtonSettings.h +++ b/Source/NDisplayLaunchButton/Public/NDisplayLaunchButtonSettings.h @@ -1,153 +1,109 @@ -#pragma once -#include "CoreMinimal.h" -#include "Engine/EngineTypes.h" -#include "Engine/DeveloperSettings.h" -#include "NDisplayLaunchButtonSettings.generated.h" - -UENUM(BlueprintType) -enum ProjectorDisplayType -{ - DisplayType_Off = 0 UMETA(DisplayName = "Off"), - DisplayType_Frame_Sequential = 1 UMETA(DisplayName = "Frame Sequential"), - DisplayType_Side_By_Side = 2 UMETA(DisplayName = "Side By Side"), - DisplayType_DualHead = 3 UMETA(DisplayName = "Dual Head"), - DisplayType_Error = 4 UMETA(Hidden) -}; - -UENUM(BlueprintType) -enum ButtonLaunchType -{ - ButtonLaunchType_NONE UMETA(DisplayName = "Nothing"), - ButtonLaunchType_MiniCAVE UMETA(DisplayName = "MiniCAVE"), - ButtonLaunchType_TWO_SCREEN UMETA(DisplayName = "Two Screen"), - ButtonLaunchType_CAVE UMETA(DisplayName = "CAVE"), - ButtonLaunchType_ROLV UMETA(DisplayName = "ROLV") -}; - -UCLASS(config=Engine, defaultconfig, meta=(DisplayName="nDisplay Launch Button")) -class UNDisplayLaunchButtonSettings : public UDeveloperSettings -{ - GENERATED_BODY() - -public: - UPROPERTY(EditAnywhere, config, Category = "General", meta = (DisplayName = "Start ")) - TEnumAsByte<ButtonLaunchType> LaunchType = ButtonLaunchType_MiniCAVE; - - /* - * TwoScreen Options - */ - UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen", meta = (DisplayName = "Launch Parameters")) - FString TwoScreenLaunchParameters = "-dc_cluster -dc_dev_mono -windowed -fixedseed -notexturestreaming"; - UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen", meta = (DisplayName = "Additioanl Launch Parameters for Master")) - FString TwoScreenAdditionalLaunchParametersMaster = ""; - UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen|Log", meta = (DisplayName = "Open Log Window for Master Node")) - bool TwoScreenLogMasterWindow = true; - UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen|Log", meta = (DisplayName = "Write Log for Floor-Left to Project Directory")) - bool TwoScreenLogToProjectDirLeft = true; - UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen|Log", meta = (DisplayName = "Write Log for Front-Right to Project Directory")) - bool TwoScreenLogToProjectDirRight = false; - - /* - * Mini CAVE Options - */ - UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE", meta = (DisplayName = "Launch Parameters")) - FString MiniCAVELaunchParameters = "-dc_cluster -dc_dev_mono -windowed -fixedseed -notexturestreaming"; - UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE", meta = (DisplayName = "Additioanl Launch Parameters for Master")) - FString MiniCAVEAdditionalLaunchParametersMaster = "-log"; - - UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Open Log Window for Master Node")) - bool MiniCAVELogMasterWindow = true; - UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Floor-Node to Project Directory")) - bool MiniCAVELogToProjectDirFloor = true; - UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Front-Node to Project Directory")) - bool MiniCAVELogToProjectDirFront = false; - UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Left-Node to Project Directory")) - bool MiniCAVELogToProjectDirLeft = false; - UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Right-Node to Project Directory")) - bool MiniCAVELogToProjectDirRight = false; - UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Back-Node to Project Directory")) - bool MiniCAVELogToProjectDirBack = false; - - /* - * CAVE Options - */ - UPROPERTY(EditAnywhere, config, Category = "General|CAVE", meta = (DisplayName = "Path to Launch Script")) - FFilePath CAVELaunchScriptPath; - - /* - * ROLV Options - */ - UPROPERTY(EditAnywhere, config, Category = "General|ROLV", meta = (DisplayName = "Path to the ROLV nDisplay Config")) - FFilePath RolvConfig; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV", meta = (DisplayName = "Launch Parameters")) - FString ROLVLaunchParameters = "-dc_cluster -nosplash -fixedseed -dx11 -dc_dev_side_by_side -notexturestreaming -fullscreen dc_node=node_main"; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV", meta = (DisplayName = "Write Log to Project Directory")) - bool ROLVLogToProjectDir = true; - - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|Projector", meta = (DisplayName = "Change Projector Mode on Startup")) - bool SwitchProjector = true; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|Projector", meta = (DisplayName = "Switch to ")) - TEnumAsByte<ProjectorDisplayType> ProjectorType; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|Projector", meta = (DisplayName = "Projector IP")) - FString ProjectorIP; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|Projector", meta = (DisplayName = "Projector Port")) - int ProjectorPort = 1025; - - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|VRPN", meta = (DisplayName = "Start VRPN in the Background")) - bool StartVRPN = true; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|VRPN", meta = (DisplayName = "Path to VRPN Executable")) - FFilePath VRPNPath; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|VRPN", meta = (DisplayName = "Path to VRPN Config")) - FFilePath VRPNConfigPath; - - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|DTRACK", meta = (DisplayName = "Start DTrack in the Background")) - bool StartDTrack = true; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|DTRACK", meta = (DisplayName = "DTrack IP")) - FString DTrackIP; - UPROPERTY(EditAnywhere, config, Category = "General|ROLV|DTRACK", meta = (DisplayName = "DTrack Port")) - int DTrackPort = 50105; - - //Macro used to shorten code -#define PROPERTY_CONDITION_CHECK(Variable, Condition) if (InProperty->GetFName() == GET_MEMBER_NAME_CHECKED(UNDisplayLaunchButtonSettings, Variable)) return Condition; - - bool CanEditChange(const FProperty* InProperty) const override - { - const bool ParentVal = Super::CanEditChange(InProperty); - - //TODO: This Code is not executed for all FFilePath Properties. Check if this works in 4.23 with the EditCondition Parser and remove this method - - PROPERTY_CONDITION_CHECK(TwoScreenLaunchParameters, LaunchType == ButtonLaunchType_TWO_SCREEN) - PROPERTY_CONDITION_CHECK(TwoScreenAdditionalLaunchParametersMaster, LaunchType == ButtonLaunchType_TWO_SCREEN) - PROPERTY_CONDITION_CHECK(TwoScreenLogToProjectDirRight, LaunchType == ButtonLaunchType_TWO_SCREEN) - PROPERTY_CONDITION_CHECK(TwoScreenLogToProjectDirLeft, LaunchType == ButtonLaunchType_TWO_SCREEN) - PROPERTY_CONDITION_CHECK(TwoScreenLogMasterWindow, LaunchType == ButtonLaunchType_TWO_SCREEN) - - PROPERTY_CONDITION_CHECK(MiniCAVELaunchParameters, LaunchType == ButtonLaunchType_MiniCAVE) - PROPERTY_CONDITION_CHECK(MiniCAVEAdditionalLaunchParametersMaster, LaunchType == ButtonLaunchType_MiniCAVE) - PROPERTY_CONDITION_CHECK(MiniCAVELogToProjectDirFloor, LaunchType == ButtonLaunchType_MiniCAVE) - PROPERTY_CONDITION_CHECK(MiniCAVELogToProjectDirFront, LaunchType == ButtonLaunchType_MiniCAVE) - PROPERTY_CONDITION_CHECK(MiniCAVELogToProjectDirLeft, LaunchType == ButtonLaunchType_MiniCAVE) - PROPERTY_CONDITION_CHECK(MiniCAVELogToProjectDirRight, LaunchType == ButtonLaunchType_MiniCAVE) - PROPERTY_CONDITION_CHECK(MiniCAVELogToProjectDirBack, LaunchType == ButtonLaunchType_MiniCAVE) - PROPERTY_CONDITION_CHECK(MiniCAVELogMasterWindow, LaunchType == ButtonLaunchType_MiniCAVE) - - PROPERTY_CONDITION_CHECK(CAVELaunchScriptPath, LaunchType == ButtonLaunchType_CAVE) - - PROPERTY_CONDITION_CHECK(RolvConfig, LaunchType == ButtonLaunchType_ROLV) - PROPERTY_CONDITION_CHECK(ROLVLaunchParameters, LaunchType == ButtonLaunchType_ROLV) - PROPERTY_CONDITION_CHECK(ROLVLogToProjectDir, LaunchType == ButtonLaunchType_ROLV) - PROPERTY_CONDITION_CHECK(SwitchProjector, LaunchType == ButtonLaunchType_ROLV) - PROPERTY_CONDITION_CHECK(ProjectorType, LaunchType == ButtonLaunchType_ROLV && SwitchProjector) - PROPERTY_CONDITION_CHECK(ProjectorIP, LaunchType == ButtonLaunchType_ROLV && SwitchProjector) - PROPERTY_CONDITION_CHECK(ProjectorPort, LaunchType == ButtonLaunchType_ROLV && SwitchProjector) - PROPERTY_CONDITION_CHECK(VRPNConfigPath, LaunchType == ButtonLaunchType_ROLV && StartVRPN) - PROPERTY_CONDITION_CHECK(StartVRPN, LaunchType == ButtonLaunchType_ROLV) - PROPERTY_CONDITION_CHECK(VRPNPath, LaunchType == ButtonLaunchType_ROLV && StartVRPN) - PROPERTY_CONDITION_CHECK(VRPNConfigPath, LaunchType == ButtonLaunchType_ROLV && StartVRPN) - PROPERTY_CONDITION_CHECK(StartDTrack, LaunchType == ButtonLaunchType_ROLV) - PROPERTY_CONDITION_CHECK(DTrackIP, LaunchType == ButtonLaunchType_ROLV && StartDTrack) - PROPERTY_CONDITION_CHECK(DTrackPort, LaunchType == ButtonLaunchType_ROLV && StartDTrack) - - return ParentVal; - } -}; +#pragma once +#include "CoreMinimal.h" +#include "Engine/EngineTypes.h" +#include "Engine/DeveloperSettings.h" +#include "NDisplayLaunchButtonSettings.generated.h" + +UENUM(BlueprintType) +enum ProjectorDisplayType +{ + DisplayType_Off = 0 UMETA(DisplayName = "Off"), + DisplayType_Frame_Sequential = 1 UMETA(DisplayName = "Frame Sequential"), + DisplayType_Side_By_Side = 2 UMETA(DisplayName = "Side By Side"), + DisplayType_DualHead = 3 UMETA(DisplayName = "Dual Head"), + DisplayType_Error = 4 UMETA(Hidden) +}; + +UENUM(BlueprintType) +enum ButtonLaunchType +{ + ButtonLaunchType_NONE UMETA(DisplayName = "Nothing"), + ButtonLaunchType_MiniCAVE UMETA(DisplayName = "MiniCAVE"), + ButtonLaunchType_TWO_SCREEN UMETA(DisplayName = "Two Screen"), + ButtonLaunchType_CAVE UMETA(DisplayName = "CAVE"), + ButtonLaunchType_ROLV UMETA(DisplayName = "ROLV") +}; + +UCLASS(config=Engine, defaultconfig, meta=(DisplayName="nDisplay Launch Button")) +class UNDisplayLaunchButtonSettings : public UDeveloperSettings +{ + GENERATED_BODY() + +public: + UPROPERTY(EditAnywhere, config, Category = "General", meta = (DisplayName = "Start ")) + TEnumAsByte<ButtonLaunchType> LaunchType = ButtonLaunchType_MiniCAVE; + + /* + * TwoScreen Options + */ + UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen", meta = (DisplayName = "Launch Parameters", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_TWO_SCREEN")) + FString TwoScreenLaunchParameters = "-dc_cluster -dc_dev_mono -windowed -fixedseed -notexturestreaming"; + UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen", meta = (DisplayName = "Additioanl Launch Parameters for Master", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_TWO_SCREEN")) + FString TwoScreenAdditionalLaunchParametersMaster = ""; + UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen|Log", meta = (DisplayName = "Open Log Window for Master Node", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_TWO_SCREEN")) + bool TwoScreenLogMasterWindow = true; + UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen|Log", meta = (DisplayName = "Write Log for Floor-Left to Project Directory", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_TWO_SCREEN")) + bool TwoScreenLogToProjectDirLeft = true; + UPROPERTY(EditAnywhere, config, Category = "General|TwoScreen|Log", meta = (DisplayName = "Write Log for Front-Right to Project Directory", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_TWO_SCREEN")) + bool TwoScreenLogToProjectDirRight = false; + + /* + * Mini CAVE Options + */ + UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE", meta = (DisplayName = "Launch Parameters", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_MiniCAVE")) + FString MiniCAVELaunchParameters = "-dc_cluster -dc_dev_mono -windowed -fixedseed -notexturestreaming"; + UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE", meta = (DisplayName = "Additioanl Launch Parameters for Master", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_MiniCAVE")) + FString MiniCAVEAdditionalLaunchParametersMaster = "-log"; + + UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Open Log Window for Master Node", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_MiniCAVE")) + bool MiniCAVELogMasterWindow = true; + UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Floor-Node to Project Directory", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_MiniCAVE")) + bool MiniCAVELogToProjectDirFloor = true; + UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Front-Node to Project Directory", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_MiniCAVE")) + bool MiniCAVELogToProjectDirFront = false; + UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Left-Node to Project Directory", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_MiniCAVE")) + bool MiniCAVELogToProjectDirLeft = false; + UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Right-Node to Project Directory", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_MiniCAVE")) + bool MiniCAVELogToProjectDirRight = false; + UPROPERTY(EditAnywhere, config, Category = "General|MiniCAVE|Log", meta = (DisplayName = "Write Log for Back-Node to Project Directory", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_MiniCAVE")) + bool MiniCAVELogToProjectDirBack = false; + + /* + * CAVE Options + */ + UPROPERTY(EditAnywhere, config, Category = "General|CAVE", meta = (DisplayName = "Path to Launch Script", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_CAVE")) + FFilePath CAVELaunchScriptPath; + + /* + * ROLV Options + */ + UPROPERTY(EditAnywhere, config, Category = "General|ROLV", meta = (DisplayName = "Path to the ROLV nDisplay Config", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV")) + FFilePath RolvConfig; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV", meta = (DisplayName = "Launch Parameters", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV")) + FString ROLVLaunchParameters = "-dc_cluster -nosplash -fixedseed -dx11 -dc_dev_side_by_side -notexturestreaming -fullscreen dc_node=node_main"; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV", meta = (DisplayName = "Write Log to Project Directory", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV")) + bool ROLVLogToProjectDir = true; + + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|Projector", meta = (DisplayName = "Change Projector Mode on Startup", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV")) + bool SwitchProjector = true; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|Projector", meta = (DisplayName = "Switch to ", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV && SwitchProjector")) + TEnumAsByte<ProjectorDisplayType> ProjectorType; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|Projector", meta = (DisplayName = "Projector IP", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV && SwitchProjector")) + FString ProjectorIP; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|Projector", meta = (DisplayName = "Projector Port", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV && SwitchProjector")) + int ProjectorPort = 1025; + + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|VRPN", meta = (DisplayName = "Start VRPN in the Background", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV")) + bool StartVRPN = true; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|VRPN", meta = (DisplayName = "Path to VRPN Executable", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV && StartVRPN")) + FFilePath VRPNPath; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|VRPN", meta = (DisplayName = "Path to VRPN Config", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV && StartVRPN")) + FFilePath VRPNConfigPath; + + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|DTRACK", meta = (DisplayName = "Start DTrack in the Background", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV")) + bool StartDTrack = true; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|DTRACK", meta = (DisplayName = "DTrack IP", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV && StartDTrack")) + FString DTrackIP; + UPROPERTY(EditAnywhere, config, Category = "General|ROLV|DTRACK", meta = (DisplayName = "DTrack Port", EditCondition="LaunchType==ButtonLaunchType::ButtonLaunchType_ROLV && StartDTrack")) + int DTrackPort = 50105; +};