15 Jul, 2009, Idealiad wrote in the 1st comment:
Votes: 0
I just wanted to break out this topic separately, because after reading flumpy's message linked and quoted below, and then reading more on the observer pattern and some Python implementations I'm wondering about rewriting how I handle events.

flumpy said:
I use the OO design pattern called the "Observer" patter. This pattern allows loosely coupled objects to send "events" to each other in much the same way as your GUI works. Every object (that I want) is Observable. All containers are Observers of all my objects. When something happens, and event is "fired" by the observable object and the observers are notified of something happening with an "update" method. This update decides what to do with the event, but mainly unless its a system event it gets distributed amongst the contents of the container.

Objects in the game (or even not in the game) can also be an event listener. The object can decide what to do with the event its self, as well as consume it so no one else gets it. The events get processed by sets of triggered behaviours that may or may not be added to the MOB object, which decide how to control the mob or player based on the type of the event. I have three methods on my listener interface, onBeforeMudEvent, onMudEvent and onAfterEvent. This gives me the ability to control what happens before, during and after an event has happened. E.g. when a player moves, a movement event is created, the mob in the room gets delivered the event before it happens and consumes it so the player cannot move. Or, when a player gets an object, an event happens, and afterwards it causes the room to shake and the player to be kicked out etc etc.

I also have an interface called Alive, which lets me get hold of the players "terminal" object and ultimately write things to their console. This just lets me get an easy handle on things that can be sent message events.

I don't have a "brain" encapsulating any of this, I suppose I could if I wanted, but it still gives me flexibility to have objects that observe others without being alive, or alive objects that don't react to events.

I think whatever you decide, remember to loosely couple the "things that happen" in your game from the "things that mobs do". This will give you the flexibility you need to create any kind of world you like.



So what I'm currently doing (note this is for a single player game, but a mud-based design seems like it would work as well), is using a singleton Command class instance that holds every command as a function. When an object, NPC or the player does a command (based either on some handler update method, AI behaviour, player input, etc.) the command gets sent to the Command instance, which checks the command against a rulebook.

The rulebook is a dictionary with each command and its list of rules. The rules themselves are functions which can either return as true (so think of these as conditions, if one is true the command will fail) or perform some action in the game world and not return anything. If no conditions return true then the game carries out the command and updates the world state accordingly.

Now from my reading of the observer pattern it seems like I would want to make commands events, which send messages to anyone listening. I'm wondering what the advantages of this approach are over something like what I just described. Currently I don't keep track of who is listening to what – if an object does a command that affects other objects I iterate over all objects and test conditions appropriately. I can see where this could get cumbersome in a mud, so is one advantage of the observer pattern that you can more efficiently keep track of what events affects which objects?

Also as flumpy mentioned, he has control over what's done before, during, and after an event, which my current structure doesn't really account for (except in a crude way with rules). So would this be a second advantage, the ability to structure the flow of events based on an initiating event?

I'm curious about other ways to structure events and commands as well.
15 Jul, 2009, Runter wrote in the 2nd comment:
Votes: 0
The concept of an event ranges anywhere from simple queues to timers to triggers to complex patterns. So I'm having to assume you're wanting to know more about the latter-est. It sounds like you may be interested in the asynchronous messaging paradigm in general. Just keep in mind that with that elegance you sometimes can get scalability problems–especially if you're a stickler for low level efficiency. It's usually the broadcast that bottlenecks if you have a lot of useless multicast. (Severely so when compared to a different pattern.)
15 Jul, 2009, Idealiad wrote in the 3rd comment:
Votes: 0
So keeping in mind I just learned about asynchronous messaging in some slideshow I found, do I have it right that this paradigm basically is an event queue? So an object does something, putting the event on an event queue, which is passed to every object subscribing to the queue, then those objects do things based on messages they get.
15 Jul, 2009, Runter wrote in the 4th comment:
Votes: 0
Idealiad said:
So keeping in mind I just learned about asynchronous messaging in some slideshow I found, do I have it right that this paradigm basically is an event queue? So an object does something, putting the event on an event queue, which is passed to every object subscribing to the queue, then those objects do things based on messages they get.


I've seen it implemented with subscribers and publishers. (Any object being eligible to publish events and any object also being able to subscribe to any number of other objects.)

I think the underlying implementation isn't important, but yes. Everything that is subscribing to those objects will receive events/triggers/whatever you want to call it from the publishers.
15 Jul, 2009, David Haley wrote in the 5th comment:
Votes: 0
Implementation can make a very big difference if you have one global event queue (presumably with centralized "event management"), vs. each object having its own subscriber list and sending events to each in turn. In the latter, it is much easier to get "infinite events" where things ping each other with events that cause the other to generate an event that causes the first to generate another event …… In the former, you can manage this somewhat – sometimes you actually want infinite events – but you keep FIFO, and you can cut things off if you feel like it, or delay them, or do something else.
15 Jul, 2009, elanthis wrote in the 6th comment:
Votes: 0
Observer pattern has pros and cons. It sounds simpler because you don't have to test conditions on broadcast. It can be more complex because you actually have to assign observers as appropriate. Which is better is a matter of your specific design and your goals. Hard to give a concrete answer.

In general for commands though I would not use an observer pattern myself. You can get a lot more control in less code by using a system like what you already have, and then having the commands send events as appropriate (which can be picked up by other objects, npcs, and players using an observer pattern, if you want). If you want commands that can be affected before, during, and after their run then you can easily enough do that with events/observers inside the command handler itself. If the command is "get" then send out pre-get, get, and post-get events as necessary. If you allow return values/messages in events then you can have objects that send back a "cancel" message during pre-get to stop the command from running at all.

I personally have little need for that kind of complexity and hence little desire to complicate the code so much. Instead of wanking off to intellectual masturbatory like "event webs" and stuff, I just make practical assumptions like "an object can be picked up if it has the gettable flag set" and get on with life using way less code and less headache. :) If I decide I need effects on objects that alter its gettable state, I simply code that into the isGettable() method and am done with it.

You can actually do a lot with some simple design put into your object behavior system. For example, if you want an object to be "hot" and hence ungettable, but you want an effect to happen when a player tries to pick it up (instead of just returning a "you can't pick up foo" message), then you can split your get behavior into two components: first you attempt a touch action and, if that succeeds, a get action. The touch action would call the touch() method which can check range, touchable flags, and effects, and return some kind of status. That's going to be sufficient for most game designs.

Super flexible event systems open up a lot of new possibilities, but you really have to ask yourself if you care about those at all. With a flexible event system, for example, you can have NPCs that try to react to a player's actions in the before or after, possibly attempting to act in the before but only managing to act in the after! For example, the player walks into a gallery showcasing a powerful teleportation stone. If he attempts to touch it, the guards will grab his hand and stop him. But maybe the player is really fast, so he manages to touch the stone before the guards grab his hand. Maybe there are three guards, and two happen to be fast enough to grab him just as he touches the stone. So – possibly entirely randomly – the player might have his hand grabbed and stopped by 1-3 guards, he might grab the stone as he's grabbed by 1-3 guards, or he grabs the stone before anyone can get to him. If he touches the stone, he teleports. Any guards that touched him when he touched the stone teleport. Sounds neat, doesn't it? The kind of stuff you only ever see in narratives and not text games. But there's a problem: narratives happen at the reader's pace. If it takes the reader 5 seconds to read through the description of what happened when Johny Four Fingers tried to grab the Stone of Teleportation, that's fine. If a player in a MUD takes 5 seconds to read all that information, by the time he gets to the end he might find out that the guards just chopped his hand off for teleporting them all to the Jungle of Doom on the other side of the world. Your game probably tries to draw the player's attention when combat starts though, so that same player is more likely going to just skip over all the detail about grabbing the stone and who reacted fastest or just fast enough and so on and jump into combat. By the time combat is over, the reason _why_ those 1-3 guards teleported with him is no longer interesting, as he's already switched focus to other things he has to deal with. You will then have broken one of the most important rules of game AI: the only AI that matters is AI that the player can appreciate. You could have changed that entire stone-grabbing incident into a simple get-action trigger on the stone that rolls some dice and either tells the player he was stopped by a guard or that he and several guards teleported. Way less code, way easier to design, way easier to debug, way easier to understand, and all of the actually important bits are maintained just as well: players go ZOT! along with some company. It's less impressive from a narrative standpoint, but then a text game is and never can be a novel. As similar as the mediums may seem, they are not the same, because one allows the author and reader to work at whichever pace they want while the other requires that the reader be ready and capable of reacting to realtime events and hence requires the author to be brief, succinct, and to the point when describing places, people, things, and events.

AI that the player can't directly observe is AI that doesn't need to happen. AI that the player can observe only has to seem interesting and intelligent; it doesn't actually have to BE interesting and intelligent.

So, think hard about what you actually want to do in your game. If you really want a simulated reality experience, then sure, you need to put a lot of thought into events, pre-events, post-events, event reactions, event ordering, listeners, watchers, observers, signals, slots, forwarders, chains, webs, etc. If you want an action-adventure-RPG that focuses on combat or just on story, you really don't need to worry about observers or pre/post event triggers. All you need will often be just the bare minimum to deal with direct object interaction and to allow AI to react to things the player has done. Your design is going to tell you where things lie on that scale though, not anybody on the forum. :)
15 Jul, 2009, Runter wrote in the 7th comment:
Votes: 0
16 Jul, 2009, quixadhal wrote in the 8th comment:
Votes: 0
Slightly off-topic, but if you want an example of a really elegant messaging system, look up the old Amiga OS. The entire OS, even hardware drivers, was designed around an event model that had subscribers, which was just about unheard of in 1985. Of course, a pre-emptive multi-tasking OS that ran smoothly in only 512K of RAM, with a full GUI, is also unheard of today. :)
16 Jul, 2009, Silenus wrote in the 9th comment:
Votes: 0
Perhaps a bit further off topic still. I am also somewhat curious about message oriented systems as well and the issues involved in designing code on top of such systems. In LPC for example calls are basically synchronous though you have the option to use callbacks and could probably in principle build a message oriented system. Are there any good MOM open source systems out there which I could look at or OS systems which have some sort of message broker type system(AmigaOS/Tripos)? I am curious how these compare with more conventional IPC primitives and what the pros and cons are.
16 Jul, 2009, David Haley wrote in the 10th comment:
Votes: 0
The Java Swing GUI framework uses the event subscription model very heavily, and the source is available. I'm not sure how helpful it would truly be to look at it, or any other sufficiently 'interesting' example, as you will be trawling through a lot of "noise" in terms of code related to the particular application and not event/messaging in particular.
16 Jul, 2009, flumpy wrote in the 11th comment:
Votes: 0
I'd just like to make it clear that I do not use events for my command system.

An event system like I described is only really useful when you /don't/ know what might need to know about something or where that something might be. If theres even a smifter of a chance you know that object x has method y and you can get to that object directly or via an interface of some sort, then you should probably use it.

I.E the command object you describe is a singleton, and you can get an instance of it easily. You know it will have methods like get, drop and so on, so you should call them. I think others have said the same. You can still fire events from those methods.

On the topic of command objects, one of the things I did was to use my scripting language to extend my command object without having a tonne of methods actually on that object. So I have a bunch of scripts in a directory that all do certain things, and the command object matches the name of the command with the script name and passes all the other arguments to the script to handle. Before I do this tho, I run the command through any objects (that are not alive) in the room the player is in, returning true or false if the command was completed.

I also took advantage of some dynamic groovy stuff that lets me call object methods and treat them like commands. So I can do this:

mob.say "hi there, how are you?"
mob.wield "broad sword 2"
mob.kill player


This is suprisingly easy, and I'm sure you could do this with ruby or lua too. Basically, groovy has a special meta-method that is called when a method cannot be found on an object. I override this method, and delegate the method to my command object (passing the string in as well) and hey presto, I can do any command in the script directory or any command in the room in a very easy way with practically any mob object.

I'm just throwing things at you here, I'm not sure if you actually want to know any of this stuff but since you are looking for inspiration I thought I'd chuck it your way :D
16 Jul, 2009, David Haley wrote in the 12th comment:
Votes: 0
I agree. Events aren't that useful if you know exactly what the "chain of command" is. They're useful when you truly have to broadcast something to a bunch of people and you don't know who they are or where they live. (Interesting imagery for a program, but eh… :wink:)
16 Jul, 2009, flumpy wrote in the 13th comment:
Votes: 0
David Haley said:
I agree. Events aren't that useful if you know exactly what the "chain of command" is. They're useful when you truly have to broadcast something to a bunch of people and you don't know who they are or where they live. (Interesting imagery for a program, but eh… :wink:)


You agree??!

Flippin nora, theres one for the history books :D

I shall cherish this moment forever *sniff*
16 Jul, 2009, David Haley wrote in the 14th comment:
Votes: 0
I disagree with your assessment of my agreement.

:devil:
16 Jul, 2009, flumpy wrote in the 15th comment:
Votes: 0
David Haley said:
I disagree with your assessment of my agreement.

:devil:


bah i knew it wouldn't last :wink:
16 Jul, 2009, flumpy wrote in the 16th comment:
Votes: 0
Silenus said:
Perhaps a bit further off topic still. I am also somewhat curious about message oriented systems as well and the issues involved in designing code on top of such systems. In LPC for example calls are basically synchronous though you have the option to use callbacks and could probably in principle build a message oriented system. Are there any good MOM open source systems out there which I could look at or OS systems which have some sort of message broker type system(AmigaOS/Tripos)? I am curious how these compare with more conventional IPC primitives and what the pros and cons are.


Like David implies, I think studying the pattern its self would be more useful than any specific implimentation of it. The subscriber pattern is a very simple one, you have a list of observers and methods to add those observers, and when you want to inform them of something you setChanged and notify them. That's it.

Like any asynchronous system, you can get race conditions, infinite loops, deadlocks and the like. Nothing can really help you there except experience and a good debugger. Unfortunately anything asynchronous is inherantly tricky to sort out because, well, its asynchronous, and the problem's not always where you think it might be. Observers aren't quite as tricky as dealing with threads, but they're not far behind…
0.0/16