Send event to specific listener?

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Send event to specific listener?

      I am writing my first game and have run into a situation where it seems logical to send an Event to a specific Listener. However, this doesn't seem to fit the spirit of the EventManager. It seems that the EventManager is there to receive messages and dispatch them to its subscribed listeners, with no filtering, special cases, etc.

      My game is a simple turn-based card game. It is not poker, but that is a good example. My problem is that when I deal cards, I want to send a Deal event containing the player # and the card. However, each player/listener would subscribe to the event and thus receive the cards dealt to everyone. Is this kosher? Should the players just filter the event for their cards? While I am not particularly concerned with security at this point, dealing all cards to everyone doesn't seem right. Another example of the same situation is chat. What if one player wants to send a chat message to a specific opponent?

      Should I modify the EventManager to direct events to specific listeners? This solves one problem, but poses another: What do I do with a listener that wants to receive ALL events, like a Scoreboard or Recorder? In that case, those listeners would not receive such events.

      A semi-related question: Is the architecture described in the book suitable for a turn-based game? I like it, but maybe it isn't the best choice.
    • DaveK wrote:

      I am writing my first game and have run into a situation where it seems logical to send an Event to a specific Listener. However, this doesn't seem to fit the spirit of the EventManager. It seems that the EventManager is there to receive messages and dispatch them to its subscribed listeners, with no filtering, special cases, etc.

      My game is a simple turn-based card game. It is not poker, but that is a good example. My problem is that when I deal cards, I want to send a Deal event containing the player # and the card. However, each player/listener would subscribe to the event and thus receive the cards dealt to everyone. Is this kosher? Should the players just filter the event for their cards? While I am not particularly concerned with security at this point, dealing all cards to everyone doesn't seem right. Another example of the same situation is chat. What if one player wants to send a chat message to a specific opponent?


      There are several ways to accomplish this, depending on how you've set it up. I would probably make each player an actor and have a Player component attached to each that managed their current hand, money, etc. When the component is initialized, it would register itself with a PlayerManager class. This system knows about each of the players and manages all the turn stuff, like whose turn it is and what the current state is. The PlayerManager would be owned by the game logic. In fact, as I think about it more, the PlayerManager would likely be a subclass of Process.

      Now, for dealing cards. The question is, who is responsible for the deck and for actually dealing the cards? One idea would be to put the deck on the PlayerManager process (which is turning into a game manager). In this scenario, I wouldn't use events for dealing at all. I would just grab the appropriate Player and give them some cards since all the info is right there.

      Another idea would be to separate the deck into its own class (or set of classes). In this scenario, I'd define an interface and would probably have the Player directly access the deck. For example, say the player discards three cards, then draws three cards. He would call Deck::Discard() followed by Deck::Draw().

      My point is that I'm not convinced that decoupling the system for managing players and the deck will buy you anything. The systems pretty much have to be coupled together. The players have to have a hand, which is likely an array of Card objects. They're going to be interacting with the deck so often that all you're really doing is obfuscating the code.

      Another question to ask yourself is what code can you reuse? The only thing in this scenario that seems potentially reusable is the concept of a deck of cards. My recommendation is to go with my second option. Create a nice, generic Deck class which uses pure virtual Card objects. Specific uses of the Deck class can use different Card subclasses. This lets you create generic algorithms for shuffling, drawing cards, discarding, etc. The players become customers of this interface. Using events here is probably unnecessary.

      As for sending chat messages, this is definitely a case for the event system. I would create a ChatEvent class with an ActorId as one of the members. The receiver would be whatever you're using to handle network communication which would then package the event and send it over the wire to the specific player.

      Now, to answer the actual question, if you did want to send an event to a specific object, you first have to ask yourself what you mean by "object". Do you mean game actor or any generic object? How do you expect it to be handled? For example, how do you expect me as the sender of the event to know which receiver to send it to without being coupled to that receiver? And if they are coupled, why not just call the appropriate function directly?

      One idea is the concept of actor events, where you could send an event to an actor who would in turn forward that event to its components. This solves the problem from the previous paragraph because actors have an ActorId which isn't tied to Actor object itself; it's just an unsigned int used as a key to a map. The core idea is simple: Actor becomes an event manager. All actor events are handled by the object manager, game logic, or whoever owns the list of all actors. This object manager finds the appropriate actor (or actors) and forwards the event. The actor works just like the event manager (and perhaps is a subclass of EventManager). Components can register for specific events.

      One direct example of how this works in my engine is actor movement. Whenever an actor moves, the transform component sends an OnMove event to the actor its attached to. Different components can react differently. For example, the collision component might adjust the movement based on impassible objects.

      Either way, sending an event to a specific object is going to require you to know some identifier for that object. If you have an identifier, how much does it buy you to send an event? In my own engine, I rarely use actor events. Most of the time, if I need an actor I either have it's ID and just look it up in the object manager, or I send an event to the system that's managing the set of actors I care about (like the PlayerManager in my example above).

      A semi-related question: Is the architecture described in the book suitable for a turn-based game? I like it, but maybe it isn't the best choice.


      Yes, absolutely. The core architecture will work for pretty much anything. If you look at the code, you can probably drop anything in the TeapotWars project. That's a sample project to show us using the engine. Everything else is absolutely useful.

      Using GCC, I would write the vast majority of the game logic in Lua. You don't need the speed of C++ and the flexibility Lua gives you with tables and weakly typed variables will probably cut the amount of code you have to write in half. The game states can all be managed in Lua as well. Basically, C++ is the view and the controller. It talks to the model (Lua) through events.

      -Rez
    • I would like to thank you very much for taking the time to provide such a thoughtful and detailed answer. It is nice to see folks who are willing to help.

      Judging from your suggestion regarding chat messages, it seems that you are advocating an EventMgr more sophisticated than just a component that accepts subscriptions to events and dispatches them. That seems logical, but I envisioned a system where all game action was initiated by events passing through a central EventMgr and dispatched to whoever subscribed to them. Perhaps my conception of the GCC architecture is too simplistic or rigid.

      I am still hung up on capturing all actions in the game. My desire to do this is to replay games, gather statistics, or display a show-all scoreboard for outside observers. I envisioned doing this by subscribing to all events through the EventMgr.

      In regards to a player directly addressing the card deck, some games should broadcast drawn and discarded cards to the other players. For instance, in some poker games it is useful to know the number of cards another player drew.

      Though I didn't mention it in the previous post, I am wondering about the general game flow for such a game. Should the Game prompt players when it is their turn or should the players follow the game (via events) and understand when it is their turn? The former probably requires a few more events and a smarter game. The latter requires fewer events but a smarter player. Any thoughts here?


      Thanks again.
    • DaveK wrote:

      I would like to thank you very much for taking the time to provide such a thoughtful and detailed answer. It is nice to see folks who are willing to help.

      No worries. :)


      Judging from your suggestion regarding chat messages, it seems that you are advocating an EventMgr more sophisticated than just a component that accepts subscriptions to events and dispatches them. That seems logical, but I envisioned a system where all game action was initiated by events passing through a central EventMgr and dispatched to whoever subscribed to them. Perhaps my conception of the GCC architecture is too simplistic or rigid.

      You could do this, but I think it's overkill. Ask yourself what it really buys you to use the event system for certain things. For example, input handling is a great place to use events. It allows anything to subscribe to input events for handling with the input manager knowing or caring. Pathfinding is another one. In my engine, when an object needs to submit a pathing request, it sends an event, which keeps the pathing system decoupled with anything that needs to use it.

      Collision is an example where I don't use events. Every object that cares about collision has a Collider component which is inserted into a quadtree. During collision handling, if I detect that two objects collide, I have both Collider instances right there so I just call their OnCollide() functions.

      In a chess game I wrote, making a move is an event. I also send events to change whose turn it is. When it's the computer player's turn, running AI is not an event. I spin up a ChessAiProcess object, attach it to the process manager, and let it go. I don't need an event to do this. The AI processes and eventually sends the move event.

      My point is that events are great, but you don't need them for absolutely everything.


      I am still hung up on capturing all actions in the game. My desire to do this is to replay games, gather statistics, or display a show-all scoreboard for outside observers. I envisioned doing this by subscribing to all events through the EventMgr.

      Sure, this is a classic thing to do. You save the RNG seed and then every command to your engine gets wrapped inside a command object (or event). You keep a stack of them somewhere. This lets you undo or perform instant replays. My chess game does this; that's how I let the player undo a move. In my case, I don't actually store the event (my event manager destroys them anyway), I record a ChessMove object. I would suggest doing the same. To push the event system to capture everything since it's rare that you actually want to capture everything. Instead, do it in another system.


      In regards to a player directly addressing the card deck, some games should broadcast drawn and discarded cards to the other players. For instance, in some poker games it is useful to know the number of cards another player drew.

      The problem with your version is that everyone is going to know what everyone else has done. This is fine, but you're duplicating a bunch of information. Using your poker example, if I have five players and #3 just discarded four cards, I now have that knowledge represented in five different places (once for each player). That's wasteful and I guarantee something will get out of sync somewhere. You've denormalized the data.

      A better approach would be to use some kind of blackboard system, where there's a single, shared knowledge base. In this model, the knowledge base understands the actions of each of the players and your AI can use it to reason about what just happened. Player #3 threw away four cards, which means that his position is likely weak and I should attack him with my archers. (Disclaimer: I'm not a poker player.)

      Can you use events to update this blackboard? Of course. Should you? Maybe. It all comes down to that question: why? The disadvantage of events is that they obfuscate the intent and the chain that led to them. They're harder to debug for that reason (I can't just set a breakpoint to see who's giving me bad data, for instance). They are also harder to understand unless you know exactly how it all fits together and usually require more code (you have to write the event class, hook up the listener, etc.) The advantage of events is that they can keep systems separate and you can defer their processing.

      My point here is that I think it's dangerous to say that you want to use events for everything. Instead, you should use events where appropriate when the benefits outweigh the hindrances.


      Though I didn't mention it in the previous post, I am wondering about the general game flow for such a game. Should the Game prompt players when it is their turn or should the players follow the game (via events) and understand when it is their turn? The former probably requires a few more events and a smarter game. The latter requires fewer events but a smarter player. Any thoughts here?

      It depends. In my chess game, I have a GameManager class that manages the higher-level game. I have an array of all the players (with a length of 2, of course) and a turn index that tells me whose turn it is. When a player finishes their turn, they send an EndTurn event which causes the GameManager to increment that turn index (wrapping it back to 0 if it overruns). Then it directly calls the BeginTurn() function on the new player. For the human player, this changes the input mode so they can move pieces. For the AI player, it causes them to spin up their AI process.

      You could certainly do something similar. Either way, you'll probably have something that manages the overall game, including the players. This same system should manage the turn order.

      Another option that's pretty common is to use a state machine to handle the different game states.

      -Rez
    • DaveK wrote:

      I am writing my first game and have run into a situation where it seems logical to send an Event to a specific Listener. However, this doesn't seem to fit the spirit of the EventManager. It seems that the EventManager is there to receive messages and dispatch them to its subscribed listeners, with no filtering, special cases, etc.


      Just with regards to this bit, the event manager in my game does allow an additional "filter index" to be specified when registering a listener and passed when queuing or triggering an event. It's just an integer value that allows events of the same type to be filtered by the intended recipient. As an example, my "weapon component" may register an event handler function with its Entity ID as the filter index of the handler. Then when a button is pressed, my controller class would trigger a "Fire" event with the unique ID of its attached entity as the filter index. The event manager will then loop through all queued events and check if there are any handlers registered with a filter index that matches the one in the event object, and if so will call them.

      The filters are applied within the event manager so in my handler code I shouldn't have to worry about checking who the event is ultimately for.