Files
UnrealEngineUWP/Engine/Source/Programs/UnrealMultiUserServer/Private/UnrealMultiUserServer.cpp

154 lines
4.1 KiB
C++
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
#include "ConcertSettings.h"
#include "ConcertSyncServerLoop.h"
#include "RequiredProgramMainCPPInclude.h"
IMPLEMENT_APPLICATION(UnrealMultiUserServer, "UnrealMultiUserServer");
int32 RunUnrealMultiUserServer(int ArgC, TCHAR* ArgV[])
{
#jira UE-83339 - Disaster Recovery can fail to recover its session when the project is opened from the Project Browser - Fixed a disaster recovery bug preventing the Editor from recovering a session because another instance of the Editor on another project already locked all the sessions. Problem: On windows, the CrashReportClientEditor (hosting disaster recovery service) is started in the static initialization, before the engine is initialized, not allowing lot of command line configuration. The Editor project browser would start a first CrashReportClientEditor instance, which would load and lock all the available sessions (unless another CrashReportClientEditor was running). When the user selected a project, a new Editor and CrashReportClientEditor were launched before the first one was closed. The second instance could not access the existing sessions because they were still locked by the first instance. Solution: Because CrashReportClientEditor is launch before the engine is initialized, we don't have any context at the launch time. The best the was to delay the moment when the server reloads the existing sessions and enable each clients to store their sessions in different folders (repositories) mounted on demand by the server. Implementation details: - Implemented new RPC API to allow the client to list/create/load/drop specific repositories containing its own sessions on demand. - Updated the Concert server to manage multiples directories where session can be stored/found (session repositories) rather than just one. - Added a settings to allow the user to specify where the disaster recovery sessions should be stored on the disk. Now default in the current project folder. - Added a settings to prevent the Concert server from scanning the sessions in the default location. - Updated disaster recovery to start without any session repository and let the client decide if a new one needs to be created or an existing one be mounted to restore a previous session. - Changed the code to let disaster recovery client manage its session history rather than letting the server rotate the old session. Defaulted the history to 0, user has no flow to visualize and pick from the history. #rb Jamie.Dale #ROBOMERGE-SOURCE: CL 10260823 in //UE4/Release-4.24/... #ROBOMERGE-BOT: RELEASE (Release-4.24 -> Main) (v591-10236483) [CL 10260830 by patrick laflamme in Main branch]
2019-11-15 12:55:57 -05:00
FString Role(TEXT("MultiUser"));
FConcertSyncServerLoopInitArgs ServerLoopInitArgs;
ServerLoopInitArgs.SessionFlags = EConcertSyncSessionFlags::Default_MultiUserSession;
#jira UE-83339 - Disaster Recovery can fail to recover its session when the project is opened from the Project Browser - Fixed a disaster recovery bug preventing the Editor from recovering a session because another instance of the Editor on another project already locked all the sessions. Problem: On windows, the CrashReportClientEditor (hosting disaster recovery service) is started in the static initialization, before the engine is initialized, not allowing lot of command line configuration. The Editor project browser would start a first CrashReportClientEditor instance, which would load and lock all the available sessions (unless another CrashReportClientEditor was running). When the user selected a project, a new Editor and CrashReportClientEditor were launched before the first one was closed. The second instance could not access the existing sessions because they were still locked by the first instance. Solution: Because CrashReportClientEditor is launch before the engine is initialized, we don't have any context at the launch time. The best the was to delay the moment when the server reloads the existing sessions and enable each clients to store their sessions in different folders (repositories) mounted on demand by the server. Implementation details: - Implemented new RPC API to allow the client to list/create/load/drop specific repositories containing its own sessions on demand. - Updated the Concert server to manage multiples directories where session can be stored/found (session repositories) rather than just one. - Added a settings to allow the user to specify where the disaster recovery sessions should be stored on the disk. Now default in the current project folder. - Added a settings to prevent the Concert server from scanning the sessions in the default location. - Updated disaster recovery to start without any session repository and let the client decide if a new one needs to be created or an existing one be mounted to restore a previous session. - Changed the code to let disaster recovery client manage its session history rather than letting the server rotate the old session. Defaulted the history to 0, user has no flow to visualize and pick from the history. #rb Jamie.Dale #ROBOMERGE-SOURCE: CL 10260823 in //UE4/Release-4.24/... #ROBOMERGE-BOT: RELEASE (Release-4.24 -> Main) (v591-10236483) [CL 10260830 by patrick laflamme in Main branch]
2019-11-15 12:55:57 -05:00
ServerLoopInitArgs.ServiceRole = Role;
ServerLoopInitArgs.ServiceFriendlyName = TEXT("Multi-User Editing Server");
#jira UE-83339 - Disaster Recovery can fail to recover its session when the project is opened from the Project Browser - Fixed a disaster recovery bug preventing the Editor from recovering a session because another instance of the Editor on another project already locked all the sessions. Problem: On windows, the CrashReportClientEditor (hosting disaster recovery service) is started in the static initialization, before the engine is initialized, not allowing lot of command line configuration. The Editor project browser would start a first CrashReportClientEditor instance, which would load and lock all the available sessions (unless another CrashReportClientEditor was running). When the user selected a project, a new Editor and CrashReportClientEditor were launched before the first one was closed. The second instance could not access the existing sessions because they were still locked by the first instance. Solution: Because CrashReportClientEditor is launch before the engine is initialized, we don't have any context at the launch time. The best the was to delay the moment when the server reloads the existing sessions and enable each clients to store their sessions in different folders (repositories) mounted on demand by the server. Implementation details: - Implemented new RPC API to allow the client to list/create/load/drop specific repositories containing its own sessions on demand. - Updated the Concert server to manage multiples directories where session can be stored/found (session repositories) rather than just one. - Added a settings to allow the user to specify where the disaster recovery sessions should be stored on the disk. Now default in the current project folder. - Added a settings to prevent the Concert server from scanning the sessions in the default location. - Updated disaster recovery to start without any session repository and let the client decide if a new one needs to be created or an existing one be mounted to restore a previous session. - Changed the code to let disaster recovery client manage its session history rather than letting the server rotate the old session. Defaulted the history to 0, user has no flow to visualize and pick from the history. #rb Jamie.Dale #ROBOMERGE-SOURCE: CL 10260823 in //UE4/Release-4.24/... #ROBOMERGE-BOT: RELEASE (Release-4.24 -> Main) (v591-10236483) [CL 10260830 by patrick laflamme in Main branch]
2019-11-15 12:55:57 -05:00
ServerLoopInitArgs.GetServerConfigFunc = [Role]() -> const UConcertServerConfig*
{
UConcertServerConfig* ServerConfig = IConcertSyncServerModule::Get().ParseServerSettings(FCommandLine::Get());
if (ServerConfig->WorkingDir.IsEmpty())
{
ServerConfig->WorkingDir = FPaths::ProjectIntermediateDir() / Role;
}
if (ServerConfig->ArchiveDir.IsEmpty())
{
ServerConfig->ArchiveDir = FPaths::ProjectSavedDir() / Role;
}
return ServerConfig;
};
return ConcertSyncServerLoop(ArgC, ArgV, ServerLoopInitArgs);
}
#if PLATFORM_MAC // On Mac, to get a properly logging console that play nice, we need to build a mac application (.app) rather than a console application.
class CommandLineArguments
{
public:
CommandLineArguments() : ArgC(0), ArgV(nullptr) {}
CommandLineArguments(int InArgC, char* InUtf8ArgV[]) { Init(InArgC, InUtf8ArgV); }
void Init(int InArgC, char* InUtf8ArgV[])
{
ArgC = InArgC;
ArgV = new TCHAR*[ArgC];
for (int32 a = 0; a < ArgC; a++)
{
FUTF8ToTCHAR ConvertFromUtf8(InUtf8ArgV[a]);
ArgV[a] = new TCHAR[ConvertFromUtf8.Length() + 1];
FCString::Strcpy(ArgV[a], ConvertFromUtf8.Length() + 1, ConvertFromUtf8.Get());
}
}
~CommandLineArguments()
{
for (int32 a = 0; a < ArgC; a++)
{
delete[] ArgV[a];
}
delete[] ArgV;
}
int ArgC;
TCHAR** ArgV;
};
#include "Mac/CocoaThread.h"
static CommandLineArguments GSavedCommandLine;
@interface UE4AppDelegate : NSObject <NSApplicationDelegate, NSFileManagerDelegate>
{
}
@end
@implementation UE4AppDelegate
//handler for the quit apple event used by the Dock menu
- (void)handleQuitEvent:(NSAppleEventDescriptor*)Event withReplyEvent:(NSAppleEventDescriptor*)ReplyEvent
{
[NSApp terminate:self];
}
- (void) runGameThread:(id)Arg
{
FPlatformMisc::SetGracefulTerminationHandler();
FPlatformMisc::SetCrashHandler(nullptr);
RunUnrealMultiUserServer(GSavedCommandLine.ArgC, GSavedCommandLine.ArgV);
[NSApp terminate: self];
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)Sender;
{
if(!IsEngineExitRequested() || ([NSThread gameThread] && [NSThread gameThread] != [NSThread mainThread]))
{
RequestEngineExit(TEXT("UnrealMultiUserServer Requesting Exist"));
return NSTerminateLater;
}
else
{
return NSTerminateNow;
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)Notification
{
//install the custom quit event handler
NSAppleEventManager* appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:self andSelector:@selector(handleQuitEvent:withReplyEvent:) forEventClass:kCoreEventClass andEventID:kAEQuitApplication];
// Add a menu bar to the application.
id menubar = [[NSMenu new] autorelease];
id appMenuItem = [[NSMenuItem new] autorelease];
[menubar addItem:appMenuItem];
[NSApp setMainMenu:menubar];
// Populate the menu bar.
id appMenu = [[NSMenu new] autorelease];
id quitMenuItem = [[[NSMenuItem alloc] initWithTitle:NSLOCTEXT("UMUS_Quit", "QuitApp", "Quit").ToString().GetNSString() action:@selector(terminate:) keyEquivalent:@"q"] autorelease];
[appMenu addItem:quitMenuItem];
[appMenuItem setSubmenu:appMenu];
RunGameThread(self, @selector(runGameThread:));
}
int main(int argc, char *argv[])
{
// Record the command line.
GSavedCommandLine.Init(argc, argv);
// Launch the application.
SCOPED_AUTORELEASE_POOL;
[NSApplication sharedApplication];
[NSApp setDelegate:[UE4AppDelegate new]];
[NSApp run];
return 0;
}
@end
#else // Windows/Linux
INT32_MAIN_INT32_ARGC_TCHAR_ARGV()
{
return RunUnrealMultiUserServer(ArgC, ArgV);
}
#endif