22 May, 2015, Scionwest wrote in the 1st comment:
Votes: 0
I have wrote a notification manager that objects can subscribe to for specific message types, providing the manager with a callback. The objects are then notified via a publication from the notification manager. It looks something like this:

[TestMethod]
[TestCategory("Runtime.Game - NotificationManager")]
public void Publish_invokes_callbacks()
{
// Arrange
bool callbackCalled = false;
string messageContent = "Test";
var notificationCenter = new NotificationManager();
ISubscription subscription = notificationCenter.Subscribe<ShoutMessage>(
(msg, sub) => callback = msg.Content == messageContent);

// Act
notificationCenter.Publish(new ShoutMessage(messageContent));
subscription.Unsubscribe();

// Assert
Assert.IsTrue(callbackCalled, "The subscriber did not have its callback invoked.");
}


My objects would take a NotificationManager instance as a dependency in its constructor. My DI system is responsible for making sure that it provides a singleton instance of the NotificationManager. The other approach I have been playing with is just using eventing. This works fine, except that I have to make sure and provide all of my objects, a reference to the objects they need to subscribe to.

/// <summary>
/// Starts listening for network communication sent from the client to the server
/// </summary>
public void StartListeningForData()
{
this.Buffer = new byte[bufferSize];

// Start listening for messages from the client.
this.CurrentSocket.BeginReceive(this.Buffer, 0, bufferSize, 0, new AsyncCallback(this.ReceiveData), null);

// Subscribe to the result of processing a command from the client.
this.Player.CommandManager.CommandCompleted += this.HandleCommandExecutionCompleted;
}

private void HandleCommandExecutionCompleted(object sender, CommandCompletionArgs e)
{
if (!this.IsConnectionValid)
{
return;
}

// Temporarily just send back the same content we received from the client.
this.SendMessage($"{e.CommandResult.Result}");
}


The nice part of the notification manager is that I can pattern out the way things communicate, by having all the objects that need to react to events, use the NotificationManager to subscribe for those events. Is there any downsides to this? It also feels safer, as I can unsubscribe from the NotificationManager singleton instead of trying to make sure I unregister my event handlers prior to an object being disposed. The NotificationManager also supports predicates, so that I can only handle messages that are applicable to what the object actually needs to see. An example is only handling chat messages that are sent from a player in the same room as the character receiving the message

ISubscription subscription = notificationCenter.Subscribe<ShoutMessage>(
(msg, sub) => callbackCalled = msg.Content == messageContent,
msg => ((DefaultPlayer)msg.Sender).CurrentRoom == this.CurrentRoom);


What do you guys typically do for managing messages (chat, help, weather changes, npc movements, commanding etc) between objects in the engine?
22 May, 2015, quixadhal wrote in the 2nd comment:
Votes: 0
The only downside is in languages like C or C++, where hanging pointers can kill you. If an object doesn't properly unsubscribe, when the manager attempts to run the callback, you'll get a crash. So, it means you have to trust all the code that registers callbacks to properly deregister them, which might imply putting that in the object destructor.
22 May, 2015, Scionwest wrote in the 3rd comment:
Votes: 0
I thought of that. I have the same issue with events though, just inverted. The subscriber can leak memory if the reference becomes null before the subscriber unsubscribes. I was thinking that at least by returning the subscription itself, the subscriber can still unsubscribe, even if the object publishing the message has become null. I'm using C#, so as long as the NotificationManager has a reference to the subscriber (because it has not been manually unsubscribed), the callback will always run without an issue, even if the subscribing object has set the instance it owns to null.
22 May, 2015, plamzi wrote in the 4th comment:
Votes: 0
Scionwest said:
What do you guys typically do for managing messages (chat, help, weather changes, npc movements, commanding etc) between objects in the engine?



The nice part of the notification manager is that I can pattern out the way things communicate, by having all the objects that need to react to events, use the NotificationManager to subscribe for those events. Is there any downsides to this? It also feels safer, as I can unsubscribe from the NotificationManager singleton instead of trying to make sure I unregister my event handlers prior to an object being disposed. The NotificationManager also supports predicates, so that I can only handle messages that are applicable to what the object actually needs to see. An example is only handling chat messages that are sent from a player in the same room as the character receiving the message


In my event-based codebase, I extend the existing event framework provided by node.js, primarily because it doesn't offer an OOB method for re-registering events, which I want to be able to do in order to update code dynamically everywhere.

Listeners exist only on the object that is listening, so when you throw away the object, you don't have to worry about any event cleanups.

As for predicates for handling location-based events, e. g., I think that's pretty neat, but my C# reading skills are not so good so I can't make out where that predicate exists and can't judge its efficiency. Is this predicate a filtering method tied to a specific type of message called ShoutMessage?

FWIW, I achieve the same goal by using regular code to filter out creatures at the same location as the person speaking, and then emit a 'hear' event only on these entities when the sender is 'speaking'. So the trick is in the two-part event: emit speak, which then emits / triggers selective hear on targets within range. There's only one place in the code where 'speaking' is handled, so this seems optimal enough.
22 May, 2015, alteraeon wrote in the 5th comment:
Votes: 0
We use C/C++ on Alter Aeon, and use the event handler system instead of a notification manager. In our case, it's easier to simply add ref count hooks in the destructors of half a dozen classes than it is to add unregister hooks in a zillion places.

One of the solutions we've settled on is to make our event system strongly typed and to take a handful of the most common arguments. For example:

void schedule_event(const char *name, int type, int delay, void (*callback) (struct character *, struct obj *, Room * r), struct character *p, struct obj *o, Room * r);

This function schedules a function for callback with character, obj, and room args. If any of these the args are destructed before the event fires, the event simply doesn't happen. (We also have versions which set the arg to null and complete the callback anyway.)

Having a strongly typed system keeps the system safe and lets us use it all over the place. The server generally has 30k+ events in the queues at all times.

-dentin

Alter Aeon MUD
http://www.alteraeon.com
22 May, 2015, Scionwest wrote in the 6th comment:
Votes: 0
@plamzi:

Quote
As for predicates for handling location-based events, e. g., I think that's pretty neat, but my C# reading skills are not so good so I can't make out where that predicate exists and can't judge its efficiency. Is this predicate a filtering method tied to a specific type of message called ShoutMessage?


I broke down the predicate and callback a bit more to help with clarity. The predicate basically returns true/false if the message is applicable to the current subscriber. The notification manager skips the callback if the predicate returns false.

ISubscription subscription = notificationCenter.Subscribe<ShoutMessage>(this.HandleShoutMessage, this.CanHandleShoutMessage);

private void HandleShoutMessage(IMessage message, ISubscription subscription)
{
//…..
}

private void CanHandleShoutMessage(IMessage message)
{
return this.CurrentRoom == ((IPlayer)message.Sender).CurrentRoom;
}


Quote
Listeners exist only on the object that is listening, so when you throw away the object, you don't have to worry about any event cleanups.


The issue with that in C# is that if the listener goes away without first unsubscribing, the property will be set to null but the object will not be garbage collected. The event listener is a strong reference to the publishing object, so until the publishing object goes away the listener will still exist in memory. I will however look at other options that are similar to that, such as WeakEventing, which uses weak references instead. WeakEventing strips the listener off of the handler, making stepping through code impossible is the only draw-back.

@alteraeon
Quote
In our case, it's easier to simply add ref count hooks in the destructors of half a dozen classes than it is to add unregister hooks in a zillion places.


That's something else I tried with eventing in C#. Sadly, the deconstructors only get called when the garbage collector kicks in, and the GC won't fire if the class still has a strong reference to an event handler of any kind. It doesn't matter if it is the listener or the publisher in that scenario which is extremely frustrating.

Quote
Having a strongly typed system keeps the system safe and lets us use it all over the place. The server generally has 30k+ events in the queues at all times.


Completely agree with you. I built the notification manager so that it would publish strongly typed message types. So if a 3rd party person wants to build a plugin that acts on whisper messages, or party only messages, they can just request a notification manager in their constructor (the DI system will provide it) and then subscribe:

notificationManager.Subscribe<PartyChatMessage>(this.OnPartyChatMessageReceived, this.CanReceivePartyChatMessage);


Now I won't have to worry about trying to give a 3rd party object all of the possible references it _might_ need in order to subscribe to events, or build out abstracted services that can be used to fetch them. All messaging (more like actions/events) goes through a single mediator.

Thanks everyone for the feedback, it sounds like most of you use eventing which was the original direction I was going as well. I will take in to account your use-cases and suggestions. If you have any thing else to add, don't hold back! :biggrin:
23 May, 2015, Odoth wrote in the 7th comment:
Votes: 0
Any particular reason you don't want to use C# language built-in events?
23 May, 2015, Scionwest wrote in the 8th comment:
Votes: 0
Odoth said:
Any particular reason you don't want to use C# language built-in events?


I was wanting to provide a kind of messaging bus. Basically allowing objects to subscribe to events that take place in the engine, without actually having a reference to the publisher of the event. The engine is designed with mods and plugins in mind. Allowing other devs to build new features/replace features and the engine just consume them. Eventing would make the plugin architecture more difficult, because I'd have to wire up a abstract way of providing them references to objects that they might need. Instead, using a pub/sub method lets other devs just subscribe to a specific message type and not worry about actually having a reference to the object sending the message.

That was the original intent anyway. Loosely coupled messaging for other devs to subscribe to.
24 May, 2015, Odoth wrote in the 9th comment:
Votes: 0
So instead of having individual subscribers having references to individual publisher objects you want to route it all through your NotificationManager singleton. Instead of having explicitly defined events in NotificationManager, you use generic subscribe and publish methods so you can use any type as "notification id". Am I understanding this correctly?

Seems like a pretty cool idea. I don't see any big problems with it besides your stated concerns.
24 May, 2015, Tyche wrote in the 10th comment:
Votes: 0
TeensyMud implements both a publish/subscribe mechanism and an event system.
There was no particular reason for implementing both mechanisms.

http://sourcery.dyndns.org/svn/teensymud...

Any object which includes the Publish module can accept subscriber objects.
The subscribers implement an update method to listen for messages.
Subscribers don't need to exist in memory, they can be a memory reference or a database object id.

http://sourcery.dyndns.org/svn/teensymud...

You'll find a description of the TITS event sytems here:

http://www.mudconnect.com/SMF/index.php?...
26 May, 2015, Scionwest wrote in the 11th comment:
Votes: 0
Odoth said:
So instead of having individual subscribers having references to individual publisher objects you want to route it all through your NotificationManager singleton. Instead of having explicitly defined events in NotificationManager, you use generic subscribe and publish methods so you can use any type as "notification id". Am I understanding this correctly?

Seems like a pretty cool idea. I don't see any big problems with it besides your stated concerns.


Yeah, that's pretty much the gist of my implementation. When you publish, you publish a strongly typed notification Type. Each notification can have it's own set of properties etc. For instance, I have a InformationMessage notification that has a reference to the Target object the message is intended for, and then I have a ResponseRequest notification that has a string for the content that will be pushed to the client from the server, for things like questions that needs a response back.

I figured this would help me with commanding, as a Whisper command can just publish a WhisperMessage that has a Sender and a Target property. The server can be subscribed and push send the message to the correct Socket (server has a map of Sockets to their player classes).


Tyche said:
Any object which includes the Publish module can accept subscriber objects.
The subscribers implement an update method to listen for messages.
Subscribers don't need to exist in memory, they can be a memory reference or a database object id.

http://sourcery.dyndns.org/svn/teensymud...

You'll find a description of the TITS event sytems here:
http://www.mudconnect.com/SMF/index.php?...


Great stuff, thanks for the links I'll def spend some time digging through TeensyMud.
0.0/11