Game Development Community

Working on a gernic event system for Torque

by J. Donavan Stanley · in Torque Game Engine · 06/07/2003 (11:10 am) · 42 replies

I'm currently close to being finished with a generic event notification system for Torque objects that I'll be submitting as a resource. Here's a short rundown:

The major players are:

Publisher - A mix-in class that supports "publishing" and broadcasting subscriptions.
Subscriber - Another mix-in that knows how to subscribe and receive notifications.
Subscription - The actual notification object. Mantains a pointer to it's publisher and a list of subscribers.
SimVariant - A Variant object that can represent any Torque type.

An example usage might be:

We have a item that, when equiped increases the health regen of a player. So in when the item gets equipped it does something like this: "player.subscribe( ID_HEALTH_REGEN, this );". Then each time the player object starts a health regen all of the subscribers get notified and receive a SimVariant containing the amount of health that's being regened. The item can then modify that value and it will get passed along the chain. (Or it can return false and stop the publisher from notifing any other subscribers).

Another possible usage is for when an NPC gets attacked. It can broadcast an "I'm being attacked" event and pass the attacker, the attackers location and the type of attack in a linked list of SimVariants to all NPCs within a certain range.

The reason I'm bringing this up is that when I release the resource I planned on adding a handful of subscriptions "out of the box" and leaving the rest up to the individual developers. So I thought I'd ask here if there were any particular events people were interested in.
#21
06/13/2003 (8:39 am)
The problem with callbacks is that you have to know all the interactions in advance. With an event system like this, you can add new subscribers without modifying the object sending the event. I'm looking forward to seeing this.
#22
06/13/2003 (10:11 am)
JDS: yeah, sounds like you have the right idea.

One of the benefits of a subscription model event system is that you dont have to have EVERY object handle the callback/event. Only things interested in recieving information about something happen are told about it.

I really do think the event/message based model is the most flexible for games in general. Although you can tie yourself in knots just as easily as doing direct function calling, plus the issue of message passing and debugging where they came from is kinda funky.

If you can, try and make it so that any triggered event is tracable directly through the call stack. Lets take this scenario:

Object A blows up, it sets event "BlownUp"
the BlowUp event has a list of recievers, which it signals
the objects holding the recievers get notified that the event has signalled and act accordingly.

So the only middlemen would be the event class, and the indirection through the list of recievers.

What I'm saying is that it should be reasonably simple to trace from an event being signalled, through to all the objects its signalling, with as few hops between.

Phil.
#23
06/13/2003 (10:33 am)
... and then there is game state serialization :)

-J
#24
06/13/2003 (12:48 pm)
@Phil - That's pretty much the way it's set up now. Iterating over the subscribers is handled by the Subscription itself. You can track ALL subscriptions by breaking in the Sunscription class itself, or you can set a condintional breakpoint in the Publisher and only break when it fires a particular event.

One thing I wanted to put in is the notion of piority so that some subscribers get notified first. Right now subscribers are stored as a list. What I really need a something like priority_queue but as a list instead. Time to break out the STL book I guess.
#25
06/13/2003 (2:43 pm)
@JDS: You may consider locking the update of the system to a certain rate... say 10x a second. This would allow you to avoid delta time adjustments.

-J
#26
06/13/2003 (3:19 pm)
@Josh - When I convert it to a generic message pump I'll be doing just that.
#27
06/14/2003 (12:28 pm)
Ahhh, I think I misunderstood what exactly you were trying to create. Phil Carlise clarified it nicely for me. Thanks. :)

I do like your idea, and look forward to seeing it in torque. Thanks. :)
#28
06/14/2003 (12:46 pm)
There were some great Game Developer articles about spreading work over time to make a game run smoother.

Listeners that don't need immediate notification and can live with a bit of fluctuation in their deliverly times can be set to a lower priority and its priority bumpped up every time it is skipped.

Having multiple threads gets tricky as you have one that is "guaranteed or immediate" notification listeners, one that handles listners that are more forgiving and bumps them and one that handles listeners that are non-guaranteed delivery.

Calculate a cost in time associated with delivery of each notification and don't call the subsequent notification threads if the cost runs over.

Like most threading situations you have to be careful about thread starvation and balance out the priority logic to make sure that the amount of work requested is realistic for a given time slice, but then again that is what profiling is for.

A good example is a paraphrase of the classic bank machine example.

Say that your game always wants HEALTH additions posted to a player before DAMAGE ( health substractions ) and the textures on the model should change to reflect the current damage on the player and the players HUD.

Health additions would have a higher priority than damage and the graphical feedback could tolerate a anywhere froma few ms to a few 100ms in difference between when the event fired and when the notification occured.

Other effects like blood dripping onto the ground might be things that are completely optional. Thus you get automatic framerate scaling without having to do any repeative work, the game profiles itself and constantly scales the details to what the client machine can handle.
#29
06/15/2003 (12:26 pm)
@JDS: The Publisher/Subscriber pattern (or the similar Subject/Observer) can be a good choice for the type of problem you are addressing, and I prefer it over simple callbacks, because it reduces coupling to an abstract form, making the system more adaptable and generic.

I'm using a slightly modified Subject/Observer pattern with a change manager, as described in the Design Patterns book [GHJV]. It is a multithreaded implementation governed by event timers, some of which are managed similarly to what Jarrod discusses above. I also rely on a read/write Monitor class for preventing concurrency problems (thread blocking and racing).

The generic solution that I've been using for about six months took about 4 weeks to design and refactor, and though it seemed initially overcomplex, I'm now extremely satisfied with it and the actual runtime overhead is neglible. I pulled out a lot of hair, but it ultimately was well worth the effort.
#30
07/09/2003 (9:59 pm)
So, is this concept dead???
#31
07/09/2003 (10:38 pm)
Always love when people use patterns in game programming (pub/sub, observer, etc.). I too would like to see this concept fleshed out but it seems to be idle.
#32
07/09/2003 (10:56 pm)
Gimme ;) I think I may need something like this and if you've already done it then there's no sense in me doing it as well :D
#33
07/09/2003 (11:22 pm)
I'm assuming your all aware of the onMessageCallback stuff already in the script right?

Also there is an excellent Tribes 2 related 'suite' of scripts which implements callbacks amazingly well in it. Its named support.vl2 and probably worth a glance over to see its functionality and if thats something your looking at doing. You can find a link to it at almost any major Tribes site.
#34
07/10/2003 (12:48 am)
Xgal, they're talking about making it for C++ stuff :)
#35
07/10/2003 (2:54 am)
No the concept isn't dead. It's mostly implemented in the GORPE tree however I got pulled away from it to work on terrain paging, and now I'm on a short vacation. It's still very much alive since a lot of my design hinges on it.
#36
07/10/2003 (6:27 pm)
@Robert as was previously mentioned in this thread this goes well beyond simple callbacks and accomplishes things you can't do in Torque script.
#37
08/04/2003 (7:02 am)
So, anything new here?
#38
08/10/2003 (7:59 pm)
I've been on a semi-forced vaction from external projects. I'm getting ready to start getting underway again.
#39
08/10/2003 (8:14 pm)
Well, if you need any help, please let me know. I will gladly assist in implementing your design.
#40
10/10/2003 (2:54 am)
A small update:

After I came back from my "vacation" I decided to change directions with the whole notification system. Trying to make it do what I felt it needed to do wasn't going to work, and it was bit heavy for my tastes.

The whole publisher/subscriber ABC system has been scrapped. The SimVariant object is the only piece of the old system I kept, since it's a handy construct for passing a linked list of mixed Torque types.

I'm leveraging the existing notification and event system with Torque. I've added a "GenericNotify" type to the SimObject::Notify struct. Objects can now call "registerGenericNotify" on another object passing it the ID of the generic notify they're interested in.

When the object wishes to inform other interested objects it simply calls "fireGenericNotify" passing the ID of the notification and a pointer to a SimVariant to represent the arguments (if any). Each object that has registered to receive notifcations will have "onGenericNotify" called and will receive a pointer to the object raising the notify, the ID of the notifcation as well as a SimVariant pointer containing the arguments (if any).

Once notified an object can manipulate the arguments (for example an armor object might recieve a "NOTIFY_PLAYER_DAMAGE_RECIEVED" event and reduce the amount of damage the player actually takes), perform other actions on the obejct itself, or any other processing needed. Once complete it returns a value to indicate if whatever caused the notifcation should be allowed to continue. It can also OR in a value indicationg that no more notifcations should be done for this.

SimNotifyEvent allows us to delay/schedual the notifcation till a later point. This works in the same fashion as other events.

Handy macros to ensure a consistant naming convention for the notification IDs as well as to expose them to the script engine as variables.

The final piece I'm working on now is allowing scipts to register for notifcations. I need to do a bit more research on this since there's a couple ways I could go with it.

The final piece I need