Player Centric API - Dependency Injection

Provide power and flexibility to the game maker

There is new information available here.

The following is left as an archive.


Accessing Services

With the traditional BeamableAPI, services were accessed directly from the API instance, like so:

//Initialize the API and get a reference to it
var _beamableAPI = await Beamable.API.Instance;
//Access the inventory service
var inventoryService = _beamableAPI.InventoryService;
//Call functions from the service
inventoryService.GetCurrencies(...);

Since BeamContext provides an Api variable, we have the option to access services in mostly the same way:

//Initialize the BeamContext instance
var _beamContext = BeamContext.Default;
//Ensure it is finished initializing
await _beamContext.OnReady;
//Access the inventory service for the player
var inventoryService = _beamContext.Api.InventoryService;
//Call functions from the service, just like before
inventoryService.GetCurrencies(...);

This is a relatively small change. However, there is an important distinction to be made here: The services accessed through BeamContext are unique per player. As demonstrated in the Player Centric API - Code, you can create multiple players on the same machine, and each player will have its own set of services. In this example, service1 and service2 will be two unique services:

var ctx1 = BeamContext.ForPlayer("player1");
var ctx2 = BeamContext.ForPlayer("player2");

var service1 = ctx1.Api.InventoryService;
var service2 = ctx2.Api.InventoryService;

ServiceManager Deprecation

In legacy versions of Beamable, the ServiceManager was used to access services for the player. This has been deprecated and no longer works as of Beamable version 1.1. To update your code while changing as little as possible, you can use BeamContext.Default.ServiceProvider. However, this is not the recommended best practice, as beamContext.Api.{service} is more commonly used.

//Old
var inventoryService = ServiceManager.Resolve<InventoryService>();
//New
var inventoryService = _beamContext.Api.InventoryService;
//New (alternative option)
BeamContext.Default.ServiceProvider.GetService<InventoryService>();

Custom Services

Beamable's ServiceProvider allows you to create your own services and inject them, in the same way that all of Beamable's services are accessed. This is the structure of a custom service:

[BeamContextSystem]
public class MyCustomService : IBeamableDisposable
{
    [RegisterBeamableDependencies(-1000)]
    public static void RegisterMyStuff(IDependencyBuilder builder)
    {
        //Example of registering a singleton in the system
        builder.AddSingleton<abc>();
        //Example of removing a singleton from the system
        builder.RemoveIfExists<xyz>();
    }
    
    //Example function that your service may have
    public void DoThingForPlayer()
    {
        //Do a thing
    }
  
    public Promise OnDispose()
    {
        //Clean up the service
    }
}

This custom service can be accessed with the code below:

var _beamContext = BeamContext.Default;
var customService = _beamContext.ServiceProvider.GetService<MyCustomService>();

Let's break down this example into smaller pieces so we can better understand the functionality.

Interfaces

IBeamableDisposable: This is an optional interface for custom services. It contains the OnDispose method, which will be called when Beamable services are shutting down (e.g. when the BeamContext is stopped). This is a chance to clean up any hanging subscriptions or lifecycle functions you service may be maintaining.

Attributes

[BeamContextSystem]: this attribute has no parameters, and it acts as a filter so the Beamable system will only search for classes with this attribute as BeamContext services.
[RegisterBeamableDependencies]: Beamable will call this function when instantiating your service, which is a chance for the class to initialize anything necessary for the class to function properly (connecting to a service, creating helper objects, etc.). The argument in this attribute is order, which determines when this service gets initialized in relation to other services. By default, all Beamable services have an order of -1000.

Functions

RegisterMyStuff: This is an example function that passes in the IDependencyBuilder. This provides a way to add or remove singletons from BeamContext's ServiceProvider. This also allows you to override the default Beamable services with your own implementation (for example, if you wanted to use a custom implementation of InventoryService).
OnDispose: Called automatically when your service is shut down, as explained above. This comes from the IBeamableDisposable interface.