service_create - ryzom/ryzomcore GitHub Wiki


title: How to create a service description: published: true date: 2023-03-01T05:18:22.938Z tags: editor: markdown dateCreated: 2022-03-08T06:55:34.843Z

The heart and power of the NeL network module is layer five, Unified Networking. This is the delivered framework providing functionality for server-to-server communication as well as the necessary functionality to participate in a NeL Network Services (or NeLNS) shard. The NeLNS services will be discussed in detail in future chapters but the one service that will is critically important to this chapter is the Naming Service, or NS. The Naming Service provides a universal lookup system for the unified network classes, it allows you as a service developer to programmatically find other services within your shard by name.

A service is set of functionalities provided by a server. The class NLNET::IService is the base class of all services. This class can be found in the file nel/src/net/service.cpp and nel/include/nel/net/service.h

The NLNET::IService skeleton automatically performs the basic functionalities of any service, such as registration to the Naming Service, server start-up and shutdown, argument and config file parsing. The NLNET::IService base class is located in NeL, but the service applications are located in /code/nelns.

A shard is a set of services and contains standard services (that are in /code/nelns) and your services.

To create a shard you must have at least the naming_service running, other services are optional but should be launched as well.

All services try to connect to the admin_executor_service on the localhost so you should run it on every server to prevent this connection from failing (services retry to connect to the admin_executor_service evenly). You can launch the admin_service if you want to use the admin system. You can launch the login_service and welcome_service if you want to use client authentification.

How to create a user-defined service?

The first step in working with NeLNS is understanding that NeLNS provides the essential framework for a server but provides no game functionality. For example the Snowballs technology demo requires three services in addition to NeLNS: a frontend service (FES), a position service (POS) and a chat service (CHAT). The FES is the most common service - every shard will need an implementation of at least one FES, but the other two services implement game logic.

Service class

To create a service you will first need to define your service class. This class must inherit the NLNET::IService class - this is the basis for all NLNET services.

class CMyService : public IService
{
public:
    /// Initialization
    void init() { }
};

There are three methods that you may implement that the service system will call and what are some common uses:

Service Method Probable Uses
init Initialize service callbacks, other callback services if communicating with non-services, loading data, etc.,
update Called every loop. This is where you would start implementing your service logic.
release Disconnecting any non-service callback servers, releasing memory, etc.

Reimplement the methods init(), update() and release() of the class if you need to (optional). After each call to update(), which is done by the service skeleton every 100ms by default, the service will update all network connections (and call your callbacks), check if config files have changed, and flush all buffers of outgoing data.

This is enough to get your service running but isn't getting your service to do critically important tasks like receive messages from other services. To accomplish this we will need to learn about callback and setting up callback arrays.

Message callbacks

The notion of callbacks starts in an earlier layer of the NeL network module: layer 3, the message management layer.

Create an array of callbacks. These are just global functions.

void cbPing(CMessage &msgin, const std::string &serviceName, uint16 sid)
{
    // Process the ping message
}

TUnifiedCallbackItem CallbackArray[] =
{
    { "PING", cbPing }
};

Main function macro

Following the class definition and implementation of methods you will need to, in your source file, declare the class as a service using the NLNET_SERVICE_MAIN. This macro has seven (7) arguments:

  1. Service Class Name: e.g. CMyService (or NLNET::IService if you do not inherit from it.) (*)
  2. Short Service Name: e.g. "MYS"
  3. Long Service Name: e.g. "my_service" (**)
  4. Service Running TCP Port: e.g. set to 0 for auto-assignment
  5. Service Callback Array: e.g. CallbackArray
  6. Service Default Config Directory: e.g. "/opt/nel/etc"
  7. Service Default Log Directory: e.g. "/opt/nel/var/log"

The default config directory and default log directory can be an empty string ("") in which case the service will use the current working directory, or in otherwords it'll look for its configuration and write its logs to the directory that the service was run from. In the example above we used "EmptyCallbackArray" for the service callback array. This is a delivered callback variable that has no callbacks listed for the service. Callbacks will discussed in a later section. The long service name is important because the service will search the default configuration directory for this name plus .cfg for its configuration. The short service name is how you will refer to all running instances of this service. Finally the service class name is the class you defined earlier. The only method that requires implementation is the init() method, however it need not require any logic. Here is a brief example in code:

NLNET_SERVICE_MAIN(CMyService, "MYS", "my_service", 0, CallbackArray, "", "")

(*) If your service does nothing other than calling your callbacks, i.e. init(), update() and release() are empty, you don't need to create a new class: you can use NLMISC::IService directly and write NLMISC::IService instead of CMyService. {.is-info}

(**) The long name is used to find the config file. If the long name is "ping_service" the service will try to open the config file named "ping_service.cfg" {.is-info}

Example: my_service.cfg:

NSHost = "itsalive.nevrax.org";
NSPort = 50000;

Update rules

Use the setUpdateTimeout() to set the quantum of time use for the network update. You are absolutely certain that your update() function will not be called before this amount of time.

If you set the update time-out value higher than 0, all messages in queue will be processed until the time becomes greater than the time-out, before calling the user update().

If you set the update time-out value to 0, all messages in queue will be processed once before calling the user update().

If you set the update time-out value to -1, only one message will be process once before calling the user update().

The default time-out is 100ms, therefore the update() function of your service will be called every 100ms or so.

Examples

See the NeL NS services source code for examples on how to create a class inherited from NLMISC::IService.

Note that some NeL NS services use an older IService class and it's normal because these services are special. All your services should use the layer 5 IService, and if not, you should know exactly how to deal with that. {.is-info}

See also the net_layer5 samples in the nel/samples/net_layer5 directory.

Take a look on the nel/include/nel/net/service.h and nel/include/nel/net/unified_network.h header for more information about methods.

⚠️ **GitHub.com Fallback** ⚠️