Questions on the new Online Service Framework

Hey,

We’re preparing to undertake a bit of a refactor of our custom online subsystem implementation. We’re exploring a move over to the new online services framework (https://dev.epicgames.com/documentation/en\-us/unreal\-engine/overview\-of\-online\-services\-in\-unreal\-engine?application\_version\=5\.5\) that appears to be deprecating the old online subsystem as we like what appears to be a more modular approach to adding service components because we have a lot of custom service components. I had a few questions about the design philosophy and opinionated choices made in both the online APIs.

One piece of behavior that I would really like to implement for us is for all our online service components (Lobby, Stats, etc) to be configured from data, ideally a data asset rather than an INI file because they are more user friendly, easier to add custom logic and to strip from builds.

So, the rough user stories would be as follows.

  1. As a Developer I can specify which online service components should be added at runtime via a Data Asset.
  2. As a Developer I can configure service components differently based on conditions via a data asset (e.g. in source control branch X then configure service component Y with parameter X)

The current setup of the both online service frameworks make this hard because they expect their service components to be registered via code and they use templated class types to add/remove/lookup a specific service based on type FOnlineComponentRegistry so this makes registering service components from data tricky as we won’t be able to determine their type at compile time which the current APIs require.

So, my questions are as follows.

  1. Is there a way to add service components to an online service system from data? Ideally a UDataAsset. I saw the LoadConfig code but it’s not really clear how that is meant to work and I can’t find any documentation on it.
    1. In reality, what I am asking for here is very similar to the way that AActor and UActorComponent work, the online subsystem is the actor and the service components would be the actor components in that analogy.
    2. *Or like how the game features works with injecting content into known core systems (*https://dev.epicgames.com/documentation/en\-us/unreal\-engine/game\-features\-and\-modular\-gameplay\-in\-unreal\-engine).
  2. It seems like there are some obvious applications of UObjects in both the online frameworks, e.g. the BEGIN_ONLINE_STRUCT_META structs could be achieved with UObject reflection, not to mention better integration with editor tooling for developers so, why the aversion to using UObjects in the online frameworks?
  3. Are there any visual developer tools in the editor for online subsystems?

Cheers!

One piece of information that would be good to know is, are there intentions to allow developers to implement their own FOnlineComponentRegistry?

I think if this was more open then it would solve most of the problems I outlined above as I could implement this in a way that is easier to drive from data. It seems like this kind of freedom is enabled in other registry classes for example FAccountInfoRegistry so why not FOnlineComponentRegistry?

Another example being that by default all components added to the Components array will be forced to tick with no option to turn this off. This is wasteful if you have a large number of service components which do not need to tick, like we do.

I am debating writing my own FOnlineComponentRegistry and ignoring the base class version entirely to allow for this functionality.

Could you give me any insight into the future intentions here?

I am continuing to speak to my rubber ducky and wanted to share what I think might be an unintentional oversight in the TOnlineAsyncOp API.

After thinking about all the problems I raised above, it occoured to me that perhaps all of the “Common” APIs like FOnlineServicesCommon, FAuthCommon, etc… are all technically optional. The only code that must be implemented are the actual interfaces themselves so IOnlineServices, IAuth, etc.

Glancing at the code it seems feasible, so I started down that route and while it is more boilerplate for me to write, I can implement my service components registration just how I want them, totally data driven and I have a lot more control over how things are handled which works for me.

This almost works perfectly but there is one fly in the ointment, TOnlineAsyncOp. It takes a reference to FOnlineServicesCommon as a construction parameter. After looking at the code for that class, I don’t think this hard reference is really needed, let’s take a look at where it is used.

  1. TOnlineAsyncOp(FOnlineServicesCommon& InServices, ParamsType&& Params)
    1. The constructor stores the reference to FOnlineServicesCommon but I see no reason why this couldn’t be a IOnlineServicesPtr instead.
  2. FOnlineServicesCommon& GetServices() { return Services; }
    1. This function does not appear to be called at all, I tried changing the name of it and no compiler errors for a missing function were raised so, if it’s not used now, surely it could either be deleted or changed to IOnlineServicesPtr instead.
  3. DECLARE_MULTICAST_DELEGATE_FourParams(FOnOnlineAsyncOpCompleted, const FString& OpName, const FOnlineServicesCommon& OnlineServicesCommon, const UE::Online::FOnlineError& OnlineError, double DurationInSeconds)
    1. As far as I can tell again, this function isn’t used directly anywhere but again, couldn’t it use IOnlineServicesPtr instead?

It really feels like the inclusion of FOnlineServicesCommon in TOnlineAsyncOp is a mistake here as it breaks the implementation agnosticism of all the online interfaces and forces developers to derive their classes from FOnlineServicesCommon which feels unnecessarily restrictive.

If you could confirm the following as true then I can probably mark this whole question as answered.

  1. The “Common” (e.g. FOnlineServicesCommon, FAuthCommon, etc) APIs are optional.
  2. The hard referencing of FOnlineServicesCommon in TOnlineAsyncOp should either be deleted or should be changed to IOnlineServicesPtr instead.
    1. If so, could you let me know what the preference is so I can implement this in our version of the Engine.

Cheers!

I’ll start with your original questions:

1. There’s a lot here.

  • FOnlineServiceCommon’s LoadConfig is intended as a way to configure the individual components/operations with a hierarchy corresponding to the OnlineServices/Interface/Component/Operation so that common values can be set at any layer. In addition, it provides overrides (AddConfigSectionOverride) that can be used to provide values unique to a specific runtime configuration. For example, we use that to select a configuration for the specific backend/environment that the game is running in. To dynamically enable/disable components, I could see us adding a general per-component setting here that could be used to disable a component.
    • An example of how the config and the overrides work is as follows. Say that you want to use the OnlineServicesEOSGS leaderboards. You want to adjust the operating caching policy for it, setting different amount of times for how long you want the result of a JoinableOperation to be cached.

; DefaultEngine.ini [OnlineServices.EOS] ; define defaults for the entire oss, only reuse a result if the operation has been called with the same parameters while it is in flight CacheExpiration=UponCompletion bCacheError=false [OnlineServices.EOS.Leaderboards] ; define defaults for all operations in the leaderboard interface, reuse the result if it is called with the same parameters within 5 minutes CacheExpiration=Duration CacheExpirySeconds=300 [OnlineServices.EOS.Leaderboards:Development] ; provide an override when AddConfigSectionOverride("Development") is called, reusing the result if it is called with the same parameters within 10 seconds CacheExpirySeconds=10

  • OnlineServices is very much GConfig driven, so it wouldn’t exactly cover your user stories with data assets, but it could possibly work with inis + the config section overrides, along with some additional logic to enable/disable components based on that.
  • You could implement a different config provider (IOnlineConfigProvider) for your implementation that utilizes data assets and register it via FOnlineServicesCommon::SetConfigProvider to have a non-GConfig based approach for configuring it.
  • As for adding blueprint defined/implemented interfaces, I could see a couple approaches:
    • If the specific interface can be defined in code, define the interface, make a basic implementation of the apis and then internally use a UObject based pimpl that can be extended by BP.
    • I’ll have to spend some more time investigating this, but we could look at adding support for data defined interfaces (for example, an interface defined in blueprint). It would involve extending the component registry with an additional component registration path that has a runtime/data defined type name and appropriate hooks so that systems like the exec handler could also extend to the data defined interface operations.

2. UObjects were avoided for a couple reasons:

  • It allows OnlineServices to be utilized in lightweight programs/commandlets that do not need to initialize the engine and/or UObjects.
  • The available types are not limited to those supported by UHT, which enables us to use more standard types (eg, TVariant) without needing to wrap them or create an equivalent USTRUCT

3. There are not any visual tools yet, but we are hoping to develop some in the future, such as being able to inspect the internal state, run scriptable scenarios, have a ui for running the various operations on the interfaces (the common layer already provides exec commands for this), and tracing the operations.

And for your latest questions:

1. Common is not necessary, but is intended to be the base on which to build an OnlineServices implementation. You will be missing out on some features if you don’t utilize it. For example, the hooks to the exec system are all handled by that. Future tools may also be built in the common layer.

2. I agree that TOnlineAsyncOp should only reference the IOnlineServices interface and not FOnlineServicesCommon. I have created an internal ticket for us to address this in a future release.

OK - thanks for all the information here, I will think about it, glad that the TOnlineAsyncOp discovery was true/useful.

Feel free to mark this as closed.

Cheers!