Design Question (Identifying Unique Entities)

Started by
4 comments, last by bpopp 20 years, 5 months ago
I'm currently prototyping for a large-scale multiplayer RPG (notice how I didn't say M-M-O-R-P-G? and I'm trying to build a really solid, scaleable design. So far it's going really well, but one part of the engine that just feels "not so good" is the message handling system. I currently have a pure virtual interface called IMessage which is extended by a base class called BasicMessage. BasicMessage handles packaging, unpackaging, setting values, querying for values, etc. It is overridden by each individual message which is then just responsible for message specific details. I feel pretty good about everything up to this point. The part that doesn't feel quite right is that the BasicMessage currently has a static MSG_TYPE field which I arbitrarily override with a sequential number in each new message's implementation. I'm using this static variable to identify a message and handle the message appropriately by the other systems (MessageHandlers, etc.) This feels kludgy because each message has to be manually assigned a type ID upon construction. This type ID then has to be used in corresponding handlers on both the server and the client. It's not quite as bad as it could be because instead of using the hardcoded id, I'm using something like ChatMsg::GetType(). It's not awful and I've seen similar designs in other projects, but it just feels like this will get overwhelming as I add more and more message types. Am I being paranoid? bpopp http://www.bpopp.net/articles/view.php?id=403 [edited by - bpopp on November 13, 2003 4:34:03 PM]
Advertisement
I don''t personally think it would be that big of a deal to structure them that way. Just use an enum structure to hold the id''s with a consistant nameing convention for each of the classes.
Check out the typed message pattern.

I use a variation of it without inheritance, but Boost.Function in its place. Base Event class just stores handlers. Cheap, easy, type-safe, I don't see how life gets better:
ApplicationStartupEvent e;e.commandLine = commandLine;e.instance = instance;e.trigger();  


Oh, and only things that register for that message actually get it.

[edited by - antareus on November 13, 2003 5:48:52 PM]
--God has paid us the intolerable compliment of loving us, in the deepest, most tragic, most inexorable sense.- C.S. Lewis
Cool. I thought of doing something similar but I ran into a problem. I really don''t want to have to create a separate handler for each message type (there could be hundreds and some will only be a few lines of code), which means multiple message should be able to call the same handler. Which, I think, leaves me where I am. When the message calls the Handlers corresponding HandleMessage function, how does the handler know which IMessage type it is dealing with.

I''ve seen projects that use define macros to implement run-time typing. Is this why?
If you''re going to be serializing the messages and sending them across the network, then you need *some* way of determining the type on the other end. So unless you can guarantee that all your messages will have unique sizes, you *have* to store some value that is a ''type ID.'' Those values also have to be the same on both client and server, which is why #defining them makes so much sense.

The approach I''ve taken is to set up a std::map<int, CMMPointer<MsgFunctor> > to map message type IDs to function handlers (MsgFunctor is a special version of Functor for handlers of type void (*)(IMessage*)). I recieve a message, put it into a buffer and check the ID tag, and then send it to the correct handler. It should be simple to have multiple messages handled by a single handler that way.

Richard "Superpig" Fine
- saving pigs from untimely fates, and when he''s not doing that, runs The Binary Refinery.
Enginuity1 | Enginuity2 | Enginuity3 | Enginuity4 | Enginuity5
ry. .ibu cy. .y''ybu. .abu ry. dy. "sy. .ubu py. .ebu ry. py. .ibu gy." fy. .ibu ny. .ebu
"Don''t document your code; code your documentation." -me

Richard "Superpig" Fine - saving pigs from untimely fates - Microsoft DirectX MVP 2006/2007/2008/2009
"Shaders are not meant to do everything. Of course you can try to use it for everything, but it's like playing football using cabbage." - MickeyMouse

Ok, I feel better. I forgot about the serialization issue which, as Richard said, pretty much requires a unique type id of some sort. I''d still love to hear some alternate suggestions, but for now I think I''ll stick with what I''ve got (his is really just a throw-away prototype anyway).

As for the Functors, I think I''m doing pretty much the same thing. I have a IHandler interface which all of my Handlers will override. It describes a pure virtual function called HandleMessage( IMessage*) which each of the handlers will implement. Each Handler is registered with the HandlerFactory (singleton) along with the msgType I talked about earlier (using a std::map). The factory insures that there is only one instance of any given handler and handles the routing of messages to the appropriate one based on messages type.

-bpopp

This topic is closed to new replies.

Advertisement