o3de / sig-network

9 stars 8 forks source link

Proposed RFC Feature: Headless Server Launcher Support #107

Closed spham-amzn closed 1 year ago

spham-amzn commented 1 year ago

Summary:

This RFC is to propose support for O3DE headless server launchers. Currently the server launcher is launched 'headlessly' by specifying the Atom null-renderer as either part of the command line or specified in a custom .setreg or .cfg file for the game project. When headless server mode is activated this way, the server launcher will simply suppress the creation of the client window and proceed with the server launcher process. However, to be a truly headless server, the determination of a graphical client vs a console application needs to be determined at compile time when the server launcher to built. While the behavior varies by platform, CMake requires that an executable be identified by the target type of either APPLICATION or EXECUTABLE. To make the server launcher truly headless, updates will be needed to refactor how the server launcher and related libraries are defined.

What is the relevance of this feature?

The general use case for dedicated game servers do not need any graphical client since there is no user interaction necessary. Server applications normally just need to launch and shutdown. Launching is done through executing the application, and shutdown can be done through a terminate signal (CTRL-C) or a kill request. Being able to build a true headless server launcher will remove the requirement of having a GUI manager installed on the machine that will host the server.

Feature design description:

To maintain backwards compatibility, an additional HeadlessServerLauncher target will be built in addition to the existing ServerLauncher target. The HeadlessServerLauncher  will behave in the follow ways:

  1. On Windows, the server launcher will not spawn any native window and return immediately. Instead, it will behave like a true console application (It will be linked with /SUBSYSTEM:CONSOLE instead of /SUBSYSTEM:WINDOWS). It will direct all of its output to the current terminal / command window from where it is executed. If it double-clicked from a file explorer, it will behave as with any console application: A default windows terminal will launch displaying the console stdout/stderr of the server process.
  2. On Linux, the server launcher will not have any dependencies on any X11/XCB system libraries. It will not spawn any native window and will output any messages from stdout/stderr to the console.
  3. There is no server launcher support for any other platform at this time.

Technical design description:

AzFramework Refactor

The majority of the lower-level code that interfaces directly with the host system's devices such as the display window and input keyboard, mouse, controllers, etc are defined in the AzFramework library. These interfaces are defined in their respective platform-independent classes:

These classes were designed to provide platform-specific services to the host machine through the use of the PIMPL idiom and Platform Abstraction Layer (PAL) pattern in O3DE. However, since AzFramework is a base library for O3DE and has a build dependency to almost all targets in O3DE, it will pull in dependencies to any platform-specific native libraries that handle windowing and input as well. This forces all projects to pull in these dependencies. On Linux platforms, that means it will pull in dependencies to XCB and X11 libraries, even if it does not use them.

What is needed is to split off the platform-specific services and their dependent library dependencies from AzFramework into its own library,  AzFramework.NativeUI.  The abstraction of the windowing and input devices makes this possible. The PIMPL idiom allows us to move the platform-specific implementations of the native window and input device interactions with the platform into the new library, and the inclusion of that library can be specified in targets that intend to use them. In the case of the headless server launcher, it will be intentionally omitted. 

When omitted, the opaque pointers to the underlying PIMPL implementations will be null , and the abstraction layers that hosts these PIMPLs will be protected with a null check and perform a no-op in those cases. This will in effect disable any attempt to read or write to the native window or devices in headless mode (if any).

AzGameFramework, AzToolsFramework, and the UnifiedLauncher

In order to remove the native ui and input dependencies for server launchers, we will need to be explicit in which target will include them. For O3DE, an application/executable can either be a game/server launcher or a tool. Game/server launchers have a dependency on AzGameFramework, while all tool applications have a dependency on AzToolsFramework. The new AzFramework.NativeUI library will be added as a dependency to AzToolsFramework since this RFC is to only make server launchers headless. For AzGameFramework , we will also add AzFramework.NativeUI as a dependency since game launchers (and unified launchers) will not be headless. 

In order to make server launchers headless, we will make a new AzGameFramework.Headless library, which will be the same as AzGameFramework, except for the following:

The Unified Launcher target defines declares three different possible launchers that projects can generate:

Currently, all three launcher types have build dependencies on:

As noted above, we will be adding a dependency of AzFramework.NativeUI  to AzGameFramework . 

In order to support a headless version of Server Launchers, we will need a separate CMAKE target omit the dependency of AzFramework.NativeUI from it. This means that for headless server launchers, it can no longer depend on AzGameFramework . We will need a headless version of AzGameFramework called AzGameFramework.Headless which will be the same as AzGameFramework but without the dependency on AzFramework.NativeUI.

Atom Render

Currently the server launcher can be launched without any specific Atom RHI  renderer by specifying the Atom Null Renderer in the startup argument -NullRenderer. This will prevent any graphics rendering and thus prevent the creation of any native client window. Even though we can do a dynamic switch to headless mode, the executable will still pull in dependencies to X11/XCB on Linux as a link dependency. For non-monolithic builds, the Vulkan RHI gem will attempt to load and bring in this dependency at runtime. For monolithic builds, this dependency will be pulled in at build time since the Vulkan RHI gem will be included in the executable (even if its not activated).

In order to solve this dependency issue, the HeadlessServerLauncher will rely on a combination of O3DE's build variants and creating headless versions of some targets that omit the dependency on any RHI implementations (Atom_Feature_Common and Atom_AtomBridge). These two targets are pulling in the runtime dependencies for the non-null RHI implementations using the PAL pattern of including additional cmake files:


PLATFORM_INCLUDE_FILES
    ${pal_dir}/additional_${PAL_PLATFORM_NAME_LOWERCASE}_runtime_deps.cmake

To create headless versions of Atom_Feature_Common and Atom_AtomBridge, the CMAKE target definition for them will be duplicated, with the exception of having a .Headless suffix in the target name, the omission of the additional_${PAL_PLATFORM_NAME_LOWERCASE}_runtime_deps.cmake, and an additional compile definition LY_HEADLESS=1, which will be needed in the gem's module code to distinguish between the normal and headless versions when declaring their module class (based on the gem name).

The headless versions will be specified through the use of a new HeadlessServers variant and will be aliased to target these headless versions instead of the normal ones. For example, the normal alias for the Atom_AtomBridge target looks like:


ly_create_alias(NAME Atom_AtomBridge.Servers NAMESPACE Gem TARGETS Gem::Atom_AtomBridge)

A new HeadlessServers variant alias will also be added to direct to the headless version of Atom_AtomBridge :


ly_create_alias(NAME Atom_AtomBridge.HeadlessServers NAMESPACE Gem TARGETS Gem::Atom_AtomBridge.Headless)

In order to ensure that the NullRenderer is always enabled when launching the headless version of the server launcher, it will query the application type to see if it is headless (This will be provided through updates to AzFramework)

What are the advantages of the feature?

What are the disadvantages of the feature?

How will this be implemented or integrated into the O3DE environment?

The new feature will be added as an additional target for the launchers.

Are there any alternatives to this feature?

The current Server Launcher supports launching without creating a native UI window through command-line arguments, however on Linux it will still pull in dependencies to X11 and XCB.

How will users learn this feature?

This feature will be added to the user guides for 

Are there any open questions?

drakeredwind01 commented 1 year ago

Votes: yea or nay

drakeredwind01 commented 1 year ago

Will be left open open for 72 hours then moved to the RFC folder. [2023-08-20]

AMZN-Gene commented 1 year ago

One thing that came up was the lack of console interactivity with the headless server. While you will be able to see log output, there won't be a way to enter in console commands (cvars). If that was required, devs would need to run a normal server with a gui