Events: Publish/subscribe

PreviousNext

Overview

This page discusses the implementation of the event system. The event system provides a publish and subscribe pattern for distributed system.
The following topics are covered:

Introduction

Basicaly, events in the multiplayer framework are like normal network objects. They have some special properties though: They have a short lifespan and can not be synchronized over the network. On the other hand, events need to be registered the very same way like every object. Check out the section about object synchronisation to get more information about that topic.
You use events for example to send messages to other peers. Peers can subscribe handlers to each event, that allows them to execute something or to process the data that was being sent. Afterwards, the event object will be destroyed. Unlike normal network objects, events do not need a unique ID, which is therefore 0 by default. You can of course use an ID for you own purpose, but you'll also have to serialize it yourself.
Because events are normal objects, they are sent through the very same unreliable mechanism. You can not be sure, that every remote peer has received your event. As there are various tasks, where it is necessary to know that an event has been properly received, the multiplayer framework supports a special kind of events, the so called two phase commit events (2PC). For such an event, the receiver of an event will immediately return an acknowledgement. Of course this acknowledgement could also be lost, so 2PC events will resend themselves until all remote peers either acknowledged the reception or a timeout occurred. The drawback of this method is performance: It takes significantly longer until a consensus is reached. You should take this into consideration, if you need to use 2PC events. As a consequence of this system, 2PC events need IDs again. Those IDs have less restrictions than normal object IDs: They must only be unique per event and peer/host. Remember: 'Normal' object IDs are unique on the whole network!
Object creation an destruction is an application of 2PC events.

Using events

A basic (non 2PC) event inherits from the class EM_NET_EVENT_OBJECT that itself inherits from EM_NET_OBJECT.

Publishing events over the network

To publish an event, ie sending it to another peer, you first need a new event instance. Since predefined events are already registered, you can do this by calling the corresponding creation query in EM_NET_OBJECT_TYPES or one of its descendants. The next step is to set the group to which you want to publish the event. Do this using set_group. The event will then be published to every connection in the specified group by calling publish. That's about all the magic there is!

Note:

On the remote peer, the event will not be received on the group you set with set_group! set_group only specifies the set of connections you want to send the event to. Events always arrive at the personal_group on the remote side of each connection. This behaviour is by design.

Hint:

EM_NET_BASE provides you with the convenience feature publish that publishes an event to the standard group.

Subscription for incoming events

You now know how to publish event. Now let's look at the other side of the connection: To receive events, you have to subscribe for them. Events that have no subscribers will just vanish. Some of the predefined events already have some procedures registered. The two events EM_NET_TIME_SYNC_REQUEST and EM_NET_TIME_SYNC_REQUEST are two examples of events that you usually don't have to deal with: They are registered and processed in the background. Of course, it is possible to subscribe another handler in case you need it.
To subscribe a handler (ie an agent) to an event, EM_NET_BASE provides you with the feature subscribe_by_type_id. You can get the event ID through EM_NET_OBJECT_TYPES or one of its descendants. The agent that you supply as argument will be called as soon as the event has been received.

Illustration of the event system

  • In the illustration, there are four peers: peer_A, peer_B, peer_C and peer_D.
  • The illustration shows the point of view from peer_C.
  • There are three groups: red, green and blue.
  • The three peers are distributed to these three groups. Each group has its own unique set of objects (the grey rectangles).
  • If peer_C sends an event to the green group the EM_NET_EVENT_CONTAINER_OBJECT from the green group is used to fulfil the task. That's why each group owns its container: This enables a distinction between the different destinations.
  • Because of the fact that groups are only used for organizational purposes and are not a part from the protocol itself, there's only one EM_NET_EVENT_CONTAINER_OBJECT for all incoming events. The default is to publish the incoming events to EM_NET_BASE and to the EM_NET_CONNECTION that was the sender of the event. If you would like to publish events to groups you can easily extend the framework: EM_NET_GROUP inherits as well from EM_NET_EVENT_CONTAINER_OBJECT which enables the programmer to subscribe for events per group. One would then need to implement a mechanism which selects all groups where an event should be published. This is highly program specific and enabling a solution per default would be performance consuming.
Hint: Implementing a chat is pretty easy when you use groups as chat-rooms. A chat message would then carry its destination chat room (actually the group name) as an additional field in the event. A dispatcher which listens to all incoming chat messages would then forward/publish the chat message event to the correct group.

Characteristics of 2PC events

Two phase commit events are the tool if you need reliable transmission of events. A receiver of a 2PC event will automatically acknowledge its reception. The sender remembers which host didn't respond after some time and will resend the event for such a host. This timeout interval, as well as the number of times an interval will be resent, can be specified by the application developer. If all connections responded with an acknowledge in time, a success agent will be called. In all other cases a timeout agent is called, supplying a list of the non-responding connections as argument.
A 2PC event is a class that inherits from EM_NET_2PC_EVENT_OBJECT. The following additional features are available:

Implement custom events

Non-2PC events

Implementing custom events is very similar to normal object registration. First, you need to create a new class that inherits from EM_NET_EVENT_OBJECT which will represent your event. Afterwards, register a unique type ID like it is described in the object synchronisation section. You may now extend the event with functionality and/or data you need. The last step is to implement the deferred features serialize, unserialize and serialization_byte_count.

2PC events

The only additional step for 2PC events is to serialize and unserialize the ID. If you fail to do so, you cannot publish the event.