diff --git a/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp b/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp
index 2a300c64d25792cb5d0b3264fddb3859b9360368..01d0c4e25e05650df6ce6e9201c400bc0a8cedcd 100644
--- a/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp
+++ b/Source/NDisplayLaunchButton/Private/NDisplayLaunchButton.cpp
@@ -1,337 +1,337 @@
-// 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()
-{
-#if PLATFORM_WINDOWS
-	return "UE4Editor.exe";
-#elif PLATFORM_LINUX
-	return "UE4Editor";
-#endif
-}
-
-/**
- * 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(), TEXT("LaunchConfig"), ConfigName + ".cfg");
-}
-
-/**
- * Kill an Array of Process-handles after waiting for them for a few seconds
- * @param Processes - Array of Processes to kill
- * @param Num_Nodes - Number of Processes in the Array
- */
-void FNDisplayLaunchButtonModule::KillProcesses(FProcHandle Processes[], const int Num_Nodes)
-{
-	float SecondsToWait = 5;
-	const float CheckInterval = 0.25;
-	FPlatformProcess::ConditionalSleep([Num_Nodes, Processes, CheckInterval, &SecondsToWait]()
-	{
-		SecondsToWait -= CheckInterval;
-		if(SecondsToWait <= 0) return true;
-		for (int i = 0; i < Num_Nodes; i++){
-			if(FPlatformProcess::IsProcRunning(Processes[i])) return false;
-		}
-		return true;
-	}, CheckInterval);
-	for (int i = 0; i < Num_Nodes; i++)	FPlatformProcess::TerminateProc(Processes[i]);
-}
-
-/**
- * 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;
-	}
-	
-	FString InsightParameters = "";
-	if(Settings->bEnableInsights)
-	{
-		if (Settings->bStatNamedEvents)	InsightParameters.Append("-statnamedevents");
-		
-		TArray<FString> TraceChannels;
-		if (Settings->bLog) TraceChannels.Add("log");
-		if (Settings->bBookmark) TraceChannels.Add("bookmark");
-		if (Settings->bFrame) TraceChannels.Add("frame");
-		if (Settings->bCPU) TraceChannels.Add("cpu");
-		if (Settings->bGPU) TraceChannels.Add("gpu");
-		if (Settings->bLoadTime) TraceChannels.Add("loadtime");
-		if (Settings->bFile) TraceChannels.Add("file");
-		if (Settings->bNet) TraceChannels.Add("net");
-		if (TraceChannels.Num() > 0) InsightParameters.Append(FString::Printf(TEXT(" -trace=%s"), *FString::Join(TraceChannels, TEXT(","))));
-		
-		if (Settings->TracehostIP.Len() > 0) InsightParameters.Append(FString::Printf(TEXT(" -tracehost=%s"), *Settings->TracehostIP));
-	}
-	
-	/* Enable/Disable Plugins temporarily */
-	if(!ChangePluginStateAndStoreConfig("SteamVR", false, SteamVRState)) return;
-	if(!ChangePluginStateAndStoreConfig("OculusVR", 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 + " " + InsightParameters + " "  + 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 + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr);
-		}
-		FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */
-
-		KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */
-	}
-	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 + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr);
-		}
-		FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */
-		
-		KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */
-	}
-	else if (Settings->LaunchType == ButtonLaunchType_TDW)
-	{
-		const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("tileddisplaywall") ,*Settings->TiledDisplayWallLaunchParameters);
-		
-		const int Num_Nodes = 6;
-		FString Windows_Node_Specific_Commands[Num_Nodes] = {
-			"dc_node=node_tl  WinX=200 WinY=200  ResX=480 ResY=272" + FString((Settings->TiledDisplayWallLogMasterWindow) ? " -log" : "") + ((Settings->TiledDisplayWallLogToProjectDirTL) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_TL_Master.log")) : "") + " " + Settings->TiledDisplayWallAdditionalLaunchParametersMaster,
-			"dc_node=node_tm  WinX=693 WinY=200  ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirTM) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_TM.log")) : ""),
-			"dc_node=node_tr  WinX=1186 WinY=200 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirTR) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_TR.log")) : ""),
-			"dc_node=node_bl  WinX=200 WinY=483  ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBL) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_BL.log")) : ""),
-			"dc_node=node_bm  WinX=693 WinY=483  ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBM) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_BM.log")) : ""),
-			"dc_node=node_br  WinX=1186 WinY=483 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBR) ? (" ABSLOG = " + GetFilePathInProject("TiledDisplayWall_BR.log")) : "")
-		};
-
-		FProcHandle Processes[Num_Nodes];
-		for (int i = 0; i < Num_Nodes; i++)
-		{
-			Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr);
-		}
-		FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */
-
-		KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */
-	}
-	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())
-				+ " " + InsightParameters), // Test this
-			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("OculusVR", 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)
+// 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()
+{
+#if PLATFORM_WINDOWS
+	return "UE4Editor.exe";
+#elif PLATFORM_LINUX
+	return "UE4Editor";
+#endif
+}
+
+/**
+ * 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(), TEXT("LaunchConfig"), ConfigName + ".cfg");
+}
+
+/**
+ * Kill an Array of Process-handles after waiting for them for a few seconds
+ * @param Processes - Array of Processes to kill
+ * @param Num_Nodes - Number of Processes in the Array
+ */
+void FNDisplayLaunchButtonModule::KillProcesses(FProcHandle Processes[], const int Num_Nodes)
+{
+	float SecondsToWait = 5;
+	const float CheckInterval = 0.25;
+	FPlatformProcess::ConditionalSleep([Num_Nodes, Processes, CheckInterval, &SecondsToWait]()
+	{
+		SecondsToWait -= CheckInterval;
+		if(SecondsToWait <= 0) return true;
+		for (int i = 0; i < Num_Nodes; i++){
+			if(FPlatformProcess::IsProcRunning(Processes[i])) return false;
+		}
+		return true;
+	}, CheckInterval);
+	for (int i = 0; i < Num_Nodes; i++)	FPlatformProcess::TerminateProc(Processes[i]);
+}
+
+/**
+ * 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;
+	}
+	
+	FString InsightParameters = "";
+	if(Settings->bEnableInsights)
+	{
+		if (Settings->bStatNamedEvents)	InsightParameters.Append("-statnamedevents");
+		
+		TArray<FString> TraceChannels;
+		if (Settings->bLog) TraceChannels.Add("log");
+		if (Settings->bBookmark) TraceChannels.Add("bookmark");
+		if (Settings->bFrame) TraceChannels.Add("frame");
+		if (Settings->bCPU) TraceChannels.Add("cpu");
+		if (Settings->bGPU) TraceChannels.Add("gpu");
+		if (Settings->bLoadTime) TraceChannels.Add("loadtime");
+		if (Settings->bFile) TraceChannels.Add("file");
+		if (Settings->bNet) TraceChannels.Add("net");
+		if (TraceChannels.Num() > 0) InsightParameters.Append(FString::Printf(TEXT(" -trace=%s"), *FString::Join(TraceChannels, TEXT(","))));
+		
+		if (Settings->TracehostIP.Len() > 0) InsightParameters.Append(FString::Printf(TEXT(" -tracehost=%s"), *Settings->TracehostIP));
+	}
+	
+	/* Enable/Disable Plugins temporarily */
+	if(!ChangePluginStateAndStoreConfig("SteamVR", false, SteamVRState)) return;
+	if(!ChangePluginStateAndStoreConfig("OculusVR", 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 + " " + InsightParameters + " "  + 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 + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr);
+		}
+		FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */
+
+		KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */
+	}
+	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 + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr);
+		}
+		FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */
+		
+		KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */
+	}
+	else if (Settings->LaunchType == ButtonLaunchType_TDW)
+	{
+		const FString Parameters = FString::Printf(TEXT("\"%s\" -game dc_cfg=\"%s\" %s"),*FPaths::GetProjectFilePath(), *GetConfigPath("tileddisplaywall") ,*Settings->TiledDisplayWallLaunchParameters);
+		
+		const int Num_Nodes = 6;
+		FString Windows_Node_Specific_Commands[Num_Nodes] = {
+			"dc_node=node_tl  WinX=200 WinY=200  ResX=480 ResY=272" + FString((Settings->TiledDisplayWallLogMasterWindow) ? " -log" : "") + ((Settings->TiledDisplayWallLogToProjectDirTL) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_TL_Master.log")) : "") + " " + Settings->TiledDisplayWallAdditionalLaunchParametersMaster,
+			"dc_node=node_tm  WinX=693 WinY=200  ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirTM) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_TM.log")) : ""),
+			"dc_node=node_tr  WinX=1186 WinY=200 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirTR) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_TR.log")) : ""),
+			"dc_node=node_bl  WinX=200 WinY=483  ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBL) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_BL.log")) : ""),
+			"dc_node=node_bm  WinX=693 WinY=483  ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBM) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_BM.log")) : ""),
+			"dc_node=node_br  WinX=1186 WinY=483 ResX=480 ResY=272" + ((Settings->TiledDisplayWallLogToProjectDirBR) ? (" ABSLOG=" + GetFilePathInProject("TiledDisplayWall_BR.log")) : "")
+		};
+
+		FProcHandle Processes[Num_Nodes];
+		for (int i = 0; i < Num_Nodes; i++)
+		{
+			Processes[i] = FPlatformProcess::CreateProc(*GetEditorExecutableName(), *(Parameters + " " + InsightParameters + " " + Windows_Node_Specific_Commands[i]), true, false, false, nullptr, 0, nullptr, nullptr);
+		}
+		FPlatformProcess::WaitForProc(Processes[0]); /* wait for only one of them */
+
+		KillProcesses(Processes, Num_Nodes); /* Kill potentially crashed processes */
+	}
+	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())
+				+ " " + InsightParameters), // Test this
+			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("OculusVR", 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)