diff --git a/CAVELaunchButton.uplugin b/CAVELaunchButton.uplugin index a8c0f1c2f6bb2caa6d929ac143e0890159238fb2..dd743ef17ea2c4ae8d05421200e8535ee7bd8a9e 100644 --- a/CAVELaunchButton.uplugin +++ b/CAVELaunchButton.uplugin @@ -10,7 +10,7 @@ "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", - "CanContainContent": false, + "CanContainContent": true, "IsBetaVersion": false, "EnabledByDefault" : true, "Modules": [ diff --git a/Source/CAVELaunchButton/CAVELaunchButton.Build.cs b/Source/CAVELaunchButton/CAVELaunchButton.Build.cs index d2803e8ec6889ee9f755c11917f09c9781945497..af52f7f3da00c1bb275a5a69f6c4b2a853ca2bec 100644 --- a/Source/CAVELaunchButton/CAVELaunchButton.Build.cs +++ b/Source/CAVELaunchButton/CAVELaunchButton.Build.cs @@ -42,6 +42,8 @@ public class CAVELaunchButton : ModuleRules "Engine", "Slate", "SlateCore", + "Sockets", + "Networking" // ... add private dependencies that you statically link with here ... } ); diff --git a/Source/CAVELaunchButton/Private/CAVELaunchButton.cpp b/Source/CAVELaunchButton/Private/CAVELaunchButton.cpp index 38a97e3b7192b80583b7a6db42ca34b9ed0edced..038ffc109bc307f309f9dfaa98a416fb976ee82c 100644 --- a/Source/CAVELaunchButton/Private/CAVELaunchButton.cpp +++ b/Source/CAVELaunchButton/Private/CAVELaunchButton.cpp @@ -1,6 +1,8 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #include "CAVELaunchButton.h" +#include <string> + #include "CAVELaunchButtonStyle.h" #include "CAVELaunchButtonSettings.h" #include "CAVELaunchButtonCommands.h" @@ -8,6 +10,7 @@ #include "Framework/MultiBox/MultiBoxBuilder.h" #include "IPluginManager.h" #include "Misc/EngineVersion.h" +#include "Networking.h" #include "LevelEditor.h" @@ -58,10 +61,25 @@ if (RootWindow.IsValid()) RootWindow->Minimize(); const UCAVELaunchButtonSettings* Settings = GetDefault<UCAVELaunchButtonSettings>(); if (Settings->StartROLVInsteadOfCAVE) { - UE_LOG(LogTemp, Log, TEXT("This would launch ROLV!")); + + FString EditorExecutable = "UE4Editor.exe"; + FString Parameters = "\"" + FPaths::GetProjectFilePath() + "\" -game -dc_cluster -nosplash -fixedseed -dx11 -dc_dev_side_by_side dc_cfg=\"" + Settings->RolvConfig.FilePath + "\" -notexturestreaming -fullscreen dc_node=node_main -log ABSLOG=" + FPaths::ProjectDir() + "\\ROLV_Launch.log"; + + FProcHandle VRPN; + ProjectorDisplayType modeFromBefore; + if (Settings->StartVRPN) VRPN = FPlatformProcess::CreateProc(*Settings->VRPNPath.FilePath, *FString("-f \"" + Settings->VRPNConfigPath.FilePath + "\" -millisleep 0"), false, false, false, NULL, 0, NULL, NULL); + if (Settings->StartDTrack) SendToDTrack(Settings->DTrackIP, Settings->DTrackPort, "dtrack2 tracking start\0"); + if (Settings->SwitchBeamer) modeFromBefore = SwitchBeamerToState(Settings->BeamerIP, Settings->BeamerPort, Settings->BeamerType); + + FProcHandle instance = FPlatformProcess::CreateProc(*EditorExecutable, *(Parameters + " " + Settings->AdditionalParameters), true, false, false, NULL, 0, NULL, NULL); + 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->SwitchBeamer && modeFromBefore != DisplayType_Error) SwitchBeamerToState(Settings->BeamerIP, Settings->BeamerPort, modeFromBefore); } else { FString Config = IPluginManager::Get().FindPlugin("CAVELaunchButton")->GetBaseDir() + "/LaunchConfig/minicave.cfg"; - FString EditorExecutable = "UE4Editor.exe"; //FPaths::ConvertRelativePathToFull(FPaths::EngineDir()) + "/Binaries/Win64/UE4Editor.exe"; + FString EditorExecutable = "UE4Editor.exe"; FString Parameters = "\"" + FPaths::GetProjectFilePath() + "\" -game -windowed -fixedseed -notexturestreaming -opengl4 dc_cfg=\"" + Config + "\" -dc_cluster -dc_dev_mono"; const int num_nodes = 5; @@ -88,6 +106,90 @@ if (RootWindow.IsValid()) RootWindow->Minimize(); if (RootWindow.IsValid()) RootWindow->Maximize(); } +FSocket* openSocket(FString address, int port, FString socketName) { + FIPv4Address parsedAddress; + if (!FIPv4Address::Parse(address, parsedAddress)) { + UE_LOG(LogTemp, Error, TEXT("Could not parse Address %s"), *address); + return NULL; + } + + FIPv4Endpoint endpoint(parsedAddress, port); + FSocket* socket = FTcpSocketBuilder(*socketName); + if (!socket->Connect(endpoint.ToInternetAddr().Get())) + { + UE_LOG(LogTemp, Error, TEXT("Error connecting to server %s:%d via %s"), *address, port, *socketName); + socket->Close(); + return NULL; + } + return socket; +} + +int32 sendSocket(FSocket* socket, FString message) { + socket->Wait(ESocketWaitConditions::WaitForWrite, FTimespan::FromSeconds(5)); + std::string buffer = std::string(TCHAR_TO_UTF8(*message)); + int32 sent = 0; + if (!socket->Send((const uint8 *)buffer.c_str(), buffer.length(), sent)) + { + UE_LOG(LogTemp, Error, TEXT("Error sending via %s. Sent %d bytes"), *socket->GetDescription() , sent); + socket->Close(); + } + return sent; +} + +template<int32 bufferSize> FString receiveSocket(FSocket* socket) { + socket->Wait(ESocketWaitConditions::WaitForRead, FTimespan::FromSeconds(5)); + char receivebuffer[bufferSize]; + int bytesReceived = 0; + if (!socket->Recv((uint8*)&receivebuffer, bufferSize, bytesReceived)) { + UE_LOG(LogTemp, Error, TEXT("No valid response from %s. Response: '%s'"), *socket->GetDescription(), *FString(std::string(receivebuffer, bytesReceived).c_str())); + socket->Close(); + } + if (bytesReceived <= 0) return FString(""); + return FString(std::string(receivebuffer, bytesReceived).c_str()); +} + +void FCAVELaunchButtonModule::SendToDTrack(FString address, int port, FString message) { + FSocket* socket = openSocket(address, port, "DTrackSocket"); + if (!socket) return; + if (sendSocket(socket, message) <= 0) return; + FString response = 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 FCAVELaunchButtonModule::SwitchBeamerToState(FString address, int port, ProjectorDisplayType state) { + ProjectorDisplayType modeBefore = ProjectorDisplayType::DisplayType_Error; + FSocket* socket = openSocket(address, port, "BeamerSocket"); + if (!socket) return modeBefore; + + //Get mode from before + if (sendSocket(socket, ":TDSM ?\r") <= 0) return modeBefore; + FString response = receiveSocket<100>(socket); //%001 TDSM 000000 + + if (!response.IsEmpty()) { + int32 position = 0; + response.FindLastChar(' ', position); + modeBefore = (ProjectorDisplayType)FCString::Atoi(*response.RightChop(position)); + } + + if (response.IsEmpty() || sendSocket(socket, ":TDSM " + FString::FromInt(state) + "\r") <= 0) { + socket->Shutdown(ESocketShutdownMode::ReadWrite); + socket->Close(); + return modeBefore; + } + response = receiveSocket<100>(socket); + if (response.EndsWith(")")) { + UE_LOG(LogTemp, Error, TEXT("Beamer Type Change Failed. Response: '%s'"), *response); + } + socket->Shutdown(ESocketShutdownMode::ReadWrite); + socket->Close(); + return modeBefore; +} + void FCAVELaunchButtonModule::AddToolbarExtension(FToolBarBuilder& Builder) { Builder.AddToolBarButton(FCAVELaunchButtonCommands::Get().PluginAction); diff --git a/Source/CAVELaunchButton/Public/CAVELaunchButton.h b/Source/CAVELaunchButton/Public/CAVELaunchButton.h index b0f7a5554557aaa53b34cd2bc998de70ccc417be..a35e554ecc2d0977e1f0fed30c3a5e89832a56f1 100644 --- a/Source/CAVELaunchButton/Public/CAVELaunchButton.h +++ b/Source/CAVELaunchButton/Public/CAVELaunchButton.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "Modules/ModuleManager.h" +#include "CAVELaunchButtonSettings.h" class FToolBarBuilder; class FMenuBuilder; @@ -19,10 +20,13 @@ public: /** This function will be bound to Command. */ void PluginButtonClicked(); - -private: - void AddToolbarExtension(FToolBarBuilder& Builder); + void SendToDTrack(FString address, int port, FString message); + ProjectorDisplayType SwitchBeamerToState(FString address, int port, ProjectorDisplayType state); + +private: + + void AddToolbarExtension(FToolBarBuilder& Builder); private: TSharedPtr<class FUICommandList> PluginCommands; diff --git a/Source/CAVELaunchButton/Public/CAVELaunchButtonSettings.h b/Source/CAVELaunchButton/Public/CAVELaunchButtonSettings.h index 632c66375cc9e99249ada8ef9599b9d25422c55c..7525296d8806938f891d692ed5a56d15ce21f81a 100644 --- a/Source/CAVELaunchButton/Public/CAVELaunchButtonSettings.h +++ b/Source/CAVELaunchButton/Public/CAVELaunchButtonSettings.h @@ -3,18 +3,54 @@ #include "Engine/DeveloperSettings.h" #include "CAVELaunchButtonSettings.generated.h" +UENUM(BlueprintType) +enum ProjectorDisplayType +{ + DisplayType_Off = 0, + DisplayType_Frame_Sequential = 1, + DisplayType_Side_By_Side = 2, + DisplayType_DualHead = 3, + DisplayType_Error = 4 UMETA(Hidden) +}; + UCLASS(config=Engine, defaultconfig, meta=(DisplayName="nDisplay Launch Button")) class UCAVELaunchButtonSettings : public UDeveloperSettings { GENERATED_BODY() public: - UPROPERTY(EditAnywhere, config, Category = LaunchParametersWindows) + UPROPERTY(EditAnywhere, config, Category = LaunchParametersWindows) FString AdditionalParameters = ""; UPROPERTY(EditAnywhere, config, Category = LaunchParametersWindows) FString AdditionalParametersMaster = ""; - UPROPERTY(EditAnywhere, config, Category = Platform, meta = (ConfigRestartRequired = true)) + UPROPERTY(EditAnywhere, config, Category = ROLV, meta = (ConfigRestartRequired = true)) bool StartROLVInsteadOfCAVE = false; + + UPROPERTY(EditAnywhere, config, Category = ROLV) + FFilePath RolvConfig; + + UPROPERTY(EditAnywhere, config, Category = "ROLV|Beamer") + bool SwitchBeamer = true; + UPROPERTY(EditAnywhere, config, Category = "ROLV|Beamer", meta = (EditCondition = "SwitchBeamer")) + TEnumAsByte<ProjectorDisplayType> BeamerType; + UPROPERTY(EditAnywhere, config, Category = "ROLV|Beamer", meta = (EditCondition = "SwitchBeamer")) + FString BeamerIP; + UPROPERTY(EditAnywhere, config, Category = "ROLV|Beamer", meta = (EditCondition = "SwitchBeamer")) + int BeamerPort = 1025; + + UPROPERTY(EditAnywhere, config, Category = "ROLV|VRPN") + bool StartVRPN = true; + UPROPERTY(EditAnywhere, config, Category = "ROLV|VRPN", meta = (EditCondition = "StartVRPN")) + FFilePath VRPNPath; + UPROPERTY(EditAnywhere, config, Category = "ROLV|VRPN", meta = (EditCondition = "StartVRPN")) + FFilePath VRPNConfigPath; + + UPROPERTY(EditAnywhere, config, Category = "ROLV|DTRACK") + bool StartDTrack = true; + UPROPERTY(EditAnywhere, config, Category = "ROLV|DTRACK", meta= (EditCondition = "StartDTrack")) + FString DTrackIP; + UPROPERTY(EditAnywhere, config, Category = "ROLV|DTRACK", meta = (EditCondition = "StartDTrack")) + int DTrackPort = 50105; };