r3_servicesmanager - Framework-agnostic service manager

Summary

A services manager for the CitizenFX platform. It keeps track of various providers, all registered for a specific service. Scripts can retrieve a registered provider, which implement a specific interface, and use it without knowing anything about the implementation of the provider itself.

Why r3_servicesmanager?

If you’ve ever wanted to make a resource for FiveM and published it, you probably got the question to support <insert any framework here>. And if you’ve ever made a server and installed a lot of resources on it, you probably ran into “bridge”-like resources which handled the compatibility with frameworks.

r3_servicesmanager provides a solution for this. It gives developers a way to support many different frameworks (and other scripts) without having to program support for each one. Resources can simply register themselves and, as long as they implement the defined interfaces, will be compatible with every script utilising r3_servicesmanager for its dependencies.

Some features of the services manager:

  • You no longer need to create many if statements for every framework you want to be compatible with, just retrieve the provider and call the method you need.
  • No need to figure out which provider you need. The services manager always gives you the registered provider with the highest priority for which the required resource is started.
  • Any resource can make itself compatible, no need to update your script to add support. This also means that you don’t need to update your resource when <insert any resource> has an update, the provider simply needs to be updated and it will be compatible again!
  • Server owners can choose what to use! Since the providers are returned based on a priority, you can increase the priority of your preferred <framework, progressbar, etc.> and it will be used in every script utilising this. And you can also swap out resources (as long as they are compatible with this), for example if you want to switch the style of notifications in your server you can remove the old notification script and put in a new one which will then be used instead.
  • Runs at 0.00 ms.

What is supported?

Pretty much anything can register itself under a service key, but to keep things clear I’ve defined some interfaces for common services (notifications, jobs, frameworks, etc.). These can all be found at the services documentation page. Type definitions in Lua and TypeScript can be found in the repository. If you need an API for a service that is not defined, feel free to ask for it here or create an issue on github! I’ll check if I can define it if it’s commonly used.

There are some providers included with this resource for popular resources such as ESX, QBOX and ox_lib. These can be found in the providers folder of the repository. If you need any other providers you are welcome to ask for them here or create an issue as well.

You can also define your own “service”, and use it across your own resources. Just register it under a non-taken service key, preferably something that will never be taken i.e. including your username, and you can retrieve it in your resources.

Usage

Registering a service provider

You can register a service provider using the register export:

exports.r3_servicesmanager:register(service, provider, priority, resource)

This will register your provider for the given service with a given priority.

Parameters:

  • service: string The service to register.
  • provider: table A table of functions implementing the defined interface for the service.
  • priority: ServicePriority The priority of the provider, it is recommended to use priority 2 and make it configurable for server owners.
  • resource: string The resource this provider provides the service for. The provider will be unregistered automatically if this resource stops. Usually you should put GetCurrentResourceName() here.

Returns:

  • nil
Loading a service provider

You can retrieve a service provider using the load export:

local serviceProvider = exports.r3_servicesmanager:load(service)

This loads the provider directly if one is registered.

Parameters:

  • service: string The service to load the provider for.

Returns:

  • table The currently registered provider for the service with the highest priority.
  • nil If there is no provider registered for the service.
Retrieving a registration

If you need more information about a provider, you can retrieve the RegisteredProvider completely using the getRegistration export:

local registeredProvider = exports.r3_servicesmanager:getRegistration(service)

This way you can check which provider is currently active, and check for which resource it is and what priority it has for instance.

Parameters:

  • service: string The service to get the registration for.

Returns:

  • RegisteredProvider The full registration of the currently registered provider for the service with the highest priority.
  • nil If there is no provider registered for the service.
Retrieving all registrations for a service

You can also retrieve all registrations for a specific service using the getRegistrationsForService export:

local registeredProviders = exports.r3_servicesmanager:getRegistrationsForService(service)

Parameters:

  • service: string The service to get the registrations for.

Returns:

  • RegisteredProvider[] A list of registered providers for the service.
Retrieving all registrations for a resource

You can also retrieve all registrations for a specific resource using the getRegistrationsForResource export:

local registeredProviders = exports.r3_servicesmanager:getRegistrationsForResource(resource)

Parameters:

  • resource: string The resource to get the registrations for.

Returns:

  • RegisteredProvider[] A list of registered providers for the resource.
Getting all known services

To retrieve all known services you can use the getKnownServices export:

local knownServices = exports.r3_servicesmanager:getKnownServices()

Returns:

  • string[] A list of services that are known by the services manager, meaning they have a provider registered currently or had at least one before.
Checking if there is a provider for a service

To check if there is currently a provider registered for a service you can use the isProvidedFor export:

exports.r3_servicesmanager:isProvidedFor(service)

Parameters:

  • service: string The service to check if there is a provider registered for it.
Events

Events are triggered when a provider gets registered or unregistered. The following events get triggered:

  • r3_servicesmanager:providerRegistered
  • r3_servicesmanager:providerUnregistered

Both events provide the RegisteredProvider in question as an argument.

Types

The following types are used with the provided exports:

ServicePriority

The priority of a service provider.

Fields:

  • Lowest: 0
  • Low: 1
  • Normal: 2
  • High: 3
  • Highest: 4

RegisteredProvider

A registration of a provider for a service.

Methods:

  • getService(): string The service the provider provides.
  • getProvider(): table The provider object.
  • getPriority(): ServicePriority The priority of the provider.
  • getResource(): string The resource the provider was registered for.
  • getInvokingResource(): string The resource that registered the provider.

Currently defined services

This is a quick overview of the methods available for each service. For full details on each service please visit the corresponding documentation here. Providers for each service have been included for popular open-source resources, currently these include resources from esx, qb-core, qbox and overextended. More providers are planned (suggestions on which to do first are welcome!). This resource should also be compatible with RedM, so I will be making some providers for that soon too.

Banking

The banking providers provide a service that allows resources to interact with bank accounts.

Banking service documentation

Server

local bankingProvider = exports.r3_servicesmanager:load("banking")
bankingProvider.getAccountBalance(account)
bankingProvider.addAccountBalance(account, amount)
bankingProvider.removeAccountBalance(account, amount)
bankingProvider.accountHasBalance(account, amount)
Callback

The callback providers provide a service that allows resources to register and trigger callbacks.

Callback service documentation

Client

local callbackProvider = exports.r3_servicesmanager:load("callback")
callbackProvider.triggerServerCallback(name, cb, ...)
local result = callbackProvider.awaitServerCallback(name, ...)

Server

local callbackProvider = exports.r3_servicesmanager:load("callback")
callbackProvider.registerServerCallback(name, cb)
Context menu

The context menu providers provide a service that allows resources to show players a context menu.

Context menu service documentation

Client

local contextMenuProvider = exports.r3_servicesmanager:load("contextMenu")
contextMenuProvider.openMenu(contextMenuOptions)
contextMenuProvider.closeMenu()
Economy

The economy providers provide a service that allows resources to interact with the economy, specifically players their money.

Economy service documentation

Server

local economyProvider = exports.r3_servicesmanager:load("economy")
economyProvider.getPlayerBalance(playerId, account)
economyProvider.setPlayerBalance(playerId, account, amount)
economyProvider.addPlayerBalance(playerId, account, amount)
economyProvider.removePlayerBalance(playerId, account, amount)
economyProvider.playerHasBalance(playerId, account, amount)
Employment

The employment providers provide a service that allows resources to interact with players their jobs.

Employment service documentation

Client

local employmentProvider = exports.r3_servicesmanager:load("employment")
employmentProvider.getPlayerJob()
employmentProvider.playerHasJob(jobName, jobGrade)
employmentProvider.onPlayerJobChanged(callback)

Server

local employmentProvider = exports.r3_servicesmanager:load("employment")
employmentProvider.getJob(jobName)
employmentProvider.jobExists(jobName, jobGrade)
employmentProvider.getOnlineJobCount(jobName)
employmentProvider.getPlayerJob(playerId)
employmentProvider.setPlayerJob(playerId, jobName, jobGrade)
employmentProvider.playerHasJob(playerId, jobName, jobGrade)
Identity

The identity providers provide a service that allows resources to retrieve identifying information of a player.

Identity service documentation

Server

local identityProvider = exports.r3_servicesmanager:load("identity")
identityProvider.getPlayerIdentifier(playerId)
identityProvider.getPlayerName(playerId)
Inventory

The inventory providers provide a service that allows resources to interact with the inventory of players.

Inventory service documentation

Server

local inventoryProvider = exports.r3_servicesmanager:load("inventory")
inventoryProvider.addItem(playerId, itemName, amount)
inventoryProvider.removeItem(playerId, itemName, amount)
inventoryProvider.getItemCount(playerId, itemName)
inventoryProvider.hasItem(playerId, itemName, amount)
Metadata

The metadata providers provide a service that allows resources to store metadata specific to a player.

Metadata service documentation

Server

local metadataProvider = exports.r3_servicesmanager:load("metadata")
metadataProvider.getPlayerMetadata(playerId, key)
playerDataProvider.setPlayerMetadata(playerId, key, value)
Notification

The notification providers provide a service that allows resources to send notifications to players.

Notification service documentation

Client

local notificationProvider = exports.r3_servicesmanager:load("notification")
notificationProvider.showNotification(message, notificationOptions)

Server

local notificationProvider = exports.r3_servicesmanager:load("notification")
notificationProvider.showNotification(playerId, message, notificationOptions)
Player state

The player state providers provide a service that allows resources to check whether players are fully loaded.

Player state service documentation

Client

local playerStateProvider = exports.r3_servicesmanager:load("playerState")
playerStateProvider.isPlayerLoaded()
playerStateProvider.onPlayerLoaded(callback)
Progress

The progress providers provide a service that allows resources to show a progress bar to players.

Progress service documentation

Client

local progressProvider = exports.r3_servicesmanager:load("progress")
progressProvider.startProgress(label, duration, progressOptions)
progressProvider.cancelProgress()
Stash

The stash providers provide a service that allows resources to interact with the inventory of objects (i.e. stashes, vehicles).

Stash service documentation

Server

local stashProvider = exports.r3_servicesmanager:load("stash")
stashProvider.addItem(inventoryRef, itemName, amount)
stashProvider.removeItem(inventoryRef, itemName, amount)
stashProvider.getItemCount(inventoryRef, itemName)
stashProvider.hasItem(inventoryRef, itemName, amount)
Target

The target providers provide a service that allows resources to create targetable interactions.

Target service documentation

Client

local targetProvider = exports.r3_servicesmanager:load("target")
targetProvider.enableTargeting(bool)
targetProvider.addGlobalObject(options)
targetProvider.removeGlobalObject(optionNames)
targetProvider.addGlobalPlayer(options)
targetProvider.removeGlobalPlayer(optionNames)
targetProvider.addGlobalVehicle(options)
targetProvider.removeGlobalVehicle(optionNames)
targetProvider.addModel(models, options)
targetProvider.removeModel(models, optionNames)
targetProvider.addEntity(netIds, options)
targetProvider.removeEntity(netIds, optionNames)
targetProvider.addLocalEntity(entities, options)
targetProvider.removeLocalEntity(entities, optionNames)
targetProvider.addSphereZone(parameters)
targetProvider.addBoxZone(parameters)
targetProvider.addPolyZone(parameters)
targetProvider.removeZone(name)
Text UI

The text UI providers provide a service that allows resources to show a text UI to players.

Text UI service documentation

Client

local textUIProvider = exports.r3_servicesmanager:load("textUI")
textUIProvider.showTextUI(text)
textUIProvider.hideTextUI()
Usable items

The usable items providers provide a service that allows resources to register usable items.

Usable items service documentation

Server

local usableItemsProvider = exports.r3_servicesmanager:load("usableItems")
usableItemsProvider.registerUsableItem(itemName, cb)
5 Likes

Anything R3 puts out has always been FIRE! Once again, they are putting effort into something to benefit all, and doing so for a Free release. 10/10 R3!

1 Like