Skip to main content
GameDev.net gamedev.net
🔒 Locked

messaging system design

Started by paladinzzz Oct 30, 2004 at 11:01 AM 3 replies 950+ views
Original Post
paladinzzz
paladinzzz
Hi, Occasionaly I read people talking about game entity message passing system. I'm thinking about how to define a message. Most of the messages system define message like this: struct message { int messageid; DWORD var1, DWORD var2 }; Which I think is kind of hacky, since you need to know the actual data type passed in with var1 and var2, and use cast to turn it to the actually type from 'int'. I don't know if there are other solutions around. My thinking is something like this: class messageinterface { int getclassid() = 0; }; class mymessagetype_1 : public messageinterface { int getclassid() {return 1;} int parm1; const char * parm2; }; class mymessagetype_2 : public messageinterface { int getclassid() {return 2;} float parm1; float parm2; float parm3[5]; } Whenever I get a message by a pointer of messgeinterface, I get the classid and then cast the pointer to the actual message class, then I can access the params directly. Are there any drawbacks with this design? My most concern is how much memory footprint this going to take given hundreds of this kind of messages are created. Thanks
serratemplar
serratemplar
The only drawback I see with your idea is as much a plus perhaps as a minus, and that's complexity. Sticking whatever your data may be in a DWORD then reinterpret_casting it later is a sort of assembler-minded technique ("bits is bits" ~ context tells what they are)...so with the first example, we use the exact same data structure for all instances. For yours, we're counting on runtime polymorphism. One is faster and takes up less space, and the other is bulky and a little slower, but prettier format-wise.

Don't be afraid of casting. =) C is just a high-level assembler.
Mezz
Mezz
In the end, both structures you list use casting to some extent. In the first you cast the DWORDs directly, in the second you cast the encompassing structure so you can access the variables without needing to cast. Seems like six and two threes to me.

After some deliberation, I came up with a design (note: I havent implemented it yet, just thought it through and written stuff down about it) that would use two classes, a Message class and a MessageData class. Messages have just their type, and maybe a pointer to some MessageData, and it's the MessageData class which gets extended. If the message is one the receiving object wants to interpret, it can get at it's MessageData. Now you coult either cast here or not. I would choose not to, and to avoid casting have an ID sort of system where you query the MessageData class for a data member, which you identify by integer ID (or string, if you want) and it gets it and gives it back to the caller (I would use function overloading and pass-by-reference to get any basic type I wanted).

There are other ways to do this though, You could look into the boost library, which has a container which can store any type. Putting a vector of those in a message and you should have lots of flexibilty, havent tried it though.

-Mezz
Zakwayda
Zakwayda
In the messaging system I'm developing for my game (in progress, so can't testify to its effectiveness yet) I'm using a variable length argument class, with 'variants' as the argument type.

I think the last poster mentioned variants also - if you're not familiar with them, they are classes which can hold any data type. I learned about them from an online article (google 'c++ variant' or something), and I guess the boost library has one also.

The idea is that are ways you can get an arbitrary number of arbitrarily typed arguments passed around in a messaging system, without a lot of ugly casts, void*s, and stuff like that. So it can be done.
evolutional
evolutional
My messages are defined like this:

struct sEntityMessageParam{	enum msgType	{		MSG_TYPE_STRING,		MSG_TYPE_LONG,		MSG_TYPE_INT,		MSG_TYPE_BOOL,		MSG_TYPE_FLOAT,		MSG_TYPE_USER	};	sEntityMessageParam() {}	sEntityMessageParam( msgType pType, std::string *pData ) {}	sEntityMessageParam( msgType pType, size_t pData ) {}	sEntityMessageParam( msgType pType, long pData ) {}	sEntityMessageParam( msgType pType, void *pData ) : Type(pType) { Data.m_user = pData; }	msgType Type;	union	{		std::string		*m_string;		size_t			m_long;		int				m_int;		bool			m_bool;		float			m_float;		void			*m_user;	} Data;};struct sEntityMessageInfo{	sEntityMessageInfo() : msg(0), from(0), relay(false) { }	sEntityMessageInfo( long pMsg, Entity *pFrom, bool pRelay ) : msg(pMsg), from(pFrom), relay(pRelay) {}	long msg;	Entity *from;	bool relay;		// Relay to children?	std::vector<sEntityMessageParam>	params;};


It's probably not the fastest implementation around, but it's fairly simple to use and easily extendable.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.