Emesary: Multiplayer bridge for FlightGear

Introduction

The multiplayer bridge allows notifications to be routed over MP. The model creates an incoming bridge specifying the notifications that are to be received and the bridge will messages from multiplayer models.

The elegance of the bridge is that neither the sender nor the receiver need to know about each other; all notifications just appear in the recipient method where they can be handled. Each aircraft would have one (or more recipients) and just handle the incoming message.

Usage

Normally an aircraft model will create both an incoming and outgoing bridge, although some aircraft may only wish to listen or transmit.

Creating an outgoing bridge

All you need is a list of notifications to route, This is a list of preconstructed notifications that are necessary to access the encode / decode methonds.

var routedNotifications = [notifications.GeoEventNotification.new(nil)];

then use the following call. the parameters are as folows:

  1. ident, used to identify the bridge
  2. list of notifictions to route. Often, but not always the same as the incoming notifications to route

By default the outgoing bridge will attach itself to emesary.GlobalTransmitter, but in more advanced usage it is possible to attach to any transmitter.

   var outgoingBridge = emesary_mp_bridge.OutgoingMPBridge.new("F-15mp",routedNotifications);

Creating an incoming bridge

Again a list of notifications to route, you can reuse the same list for both incoming and outgoing.

var routedNotifications = [notifications.GeoEventNotification.new(nil)];

and the following call will cause an incoming bridge for each MP aircraft to be created when each MP item connects. The design of the bridge requires each MP model to have its own bridge (to manage the message indeces)

var incomingBridge = emesary_mp_bridge.IncomingMPBridge.startMPBridge(routedNotifications);

Outgoing routing

There are two basic types of notifications that can be routed over the bridge;

  • Distinct Messages – these are messages where only the last one is important, e.g. position updates.
  • Standard messages – message that relate to an action or event that is unique, such as jettison of tanks. All of these messages will be routed.

A model that wishes to route notifications will need to create an outgoing bridge using the method emesary_mp_bridge.OutgoingMPBridge.new

Incoming routing

Each model specifies a list of messages that it is interested in and capable of handling to the method emesary_mp_bridge.IncomingMPBridge.startMPBridge

Notifications

The nasal module that declares notifications to be routed over a bridge needs to be included in both the outgoing and the incoming model.

A notification needs to specify methods that to encode and decode the parts of the notification that need to be sent over the bridge. The bridge will itself take care of calling these methods at the right time.

The properties will be packed up and sent over by the bridge using the specified multiplayer generic string. There is a limit of the size of multiplayer packets that can be transmitted so it is important to only send the absolute minimum firstly by good design and secondly by choosing the right encoding, bytes are best.

Standard GeoEventNotification

There is a new standard notification that is part of the distribution to notify of something that happens at a specific geographic location. This could be many things (there is a list inside Nasal\notification.nas). An example could be a cargo drop using the 1000kg classification. Each event sent will be one of the following, Created, Moved, Deleted. Upon receipt the model can perform the appropriate action.
I’ve got a working example of (what I’ve called) a GeoEventNotification. This is basically something that happened at a position.

So to notify of droptanks

   var m = notifications.GeoEventNotification.new(me.variant, "DT-610", 1, 48 + pylon_index);
    emesary.GlobalTransmitter.NotifyAll(m);

This will usually be received by the player’s aircraft over MP. The magic numbers in the above are documented inside notification.nas

Handling bridged messages

Each bridged message is received in the normal way, usually via the GlobalTransmitter, the only difference is that bridged messages are marked as being bridged so that an outgoing bridge knows not to route it out again.

Example recipient

var EmesaryRecipient =
{
    new: func(_ident)
    {
        var new_class = emesary.Recipient.new(_ident);
        new_class.ansn46_expiry = 0;
        new_class.Receive = func(notification)
        {
            if (notification.NotificationType == "GeoEventNotification")
            {
                print("received GeoNotification from ",notification.Callsign);
                print ("  pos=",notification.Position.lat(),notification.Position.lon(),notification.Position.alt());
                print ("  kind=",notification.Kind, " skind=",notification.SecondaryKind);
                if(notification.FromIncomingBridge)
                {
                    if(notification.Kind == 1)# created
                    {
                        if(notification.SecondaryKind >= 48 and notification.SecondaryKind <= 63)
                        {
                            # TBD: animate drop tanks
                        }
                    }
                }
                return emesary.Transmitter.ReceiptStatus_OK;
            }
            return emesary.Transmitter.ReceiptStatus_NotProcessed;
        };
        new_class.Response = ANSPN46ActiveResponseNotification.new("ARA-63");
        return new_class;
    },
};
#
#
# Instantiate receiver. 
var recipient = EmesaryRecipient.new("F-15-recipient");
emesary.GlobalTransmitter.Register(recipient);

Summary

In summary, an aircraft sends a notification and all other MP aircraft can receive it (if they want to). This makes inter-aircraft communication possible and with a nicely structured API. It’s also worth noting that because Emesary is doing all of the work if there is a future improvement then your code will not need to change to take advantage of it.

This will allow a set of standard modules to animate items such as droptanks falling, pilots ejecting, etc. Once there is a standard module for something it can simply be included into normal module load.