A question of design

Started by
7 comments, last by ApochPiQ 18 years, 4 months ago
Hello Together, I am currently designing the network part of my Game Engine. For that I will have a "Network" class, having methodes like "SendMessage", "Connect" and stuff. What I am not sure of is how to handle recived messages and event. My Ideas: 1. Have a "Network::PollEvent" methode, which return events like "CONNECTION_ERROR" and also "MSG_RECIVED". The message is stored into data areas given at the parameters. The methode is supposed to be called so often in the game loop until it returns "NO_MESSAGE". 2. Have "Network::SetEventFunction" methode, which takes a function pointer. After the fucntion has been set, all events are handled by calling this function. 3. Have a "virtual Network::OnEvent" methode, which is called in case of an event. I do not like the option 3, because one would have to derive a class from "Network" just for overwritting this one methode. What do you think is the best approach? What do you think are the pros. and cons.? Any other approach you would advise me to use? Thanks! Nathan
Advertisement
No experienced user with an opinion?
I fear that I will choose an approach now and discover later that it is a stupid way of doing it ...
I have my network class written for IRC, with flexibility in mind.

The idea is that it can be used for any purpose either for IRC client or for game engine.

Here is the idea:

[ IRC CHATBOT ] <--> [ IRC_WRAPPER ] <--> [ NETWORK CLASS ]

the network class is constant each time. So I don't have to rewrite the same funcitonality again , I just write a new wrapper for it:

[ GAME ] <--> [ GAME WRAPPER ] <--> [ NETWORK CLASS ]

For My class I use a few things:

The message is receved, and put into buffer, waiting for finishing symbol to form a full packet. Like "\r\n" for IRC. Once it is form, I call OnPacketReceve function pointer, which then passes this packet to my wrapper. I haven't found any problems with this approach, and I do like the current state of my class.


Check it out I have a full source code available for my recent chatbot. Including the network class.

clicky
Hello and thanks for the reply!
Had a short look at your source. If I understand correctly, it's like my option 2, correct?
Greetings,
Nathan
I think it looks more like 3 option, though a little different.

The network class listens to socket. Once the message is receved, and formed into one solid packet, then OnPacketReceve() is called.

Then My wrapper which uses OnPacketReceve() gets the packet, and does some work with it.
Is OnPacketRecieve a virtual function which needs to be overloaded in a derived class or is it a pointer to function which needs to be set somehow?
it is function pointer. Check the IRC_wrap.h and IRC_wrap.cpp;

Here are some quotes from the source:

IRC_wrap.h
#include "KNA_NETWORK.h"//here is the network class - for the TCP/IP clientNet_Client client;//start the clientclient.Start();//assign callback function pointer client.OnPacketReceve = &OnPacketReceve;//simple connectclient.Connect("irc.quakenet.org",6667);//send disconnect messageclient.Disconnect();//destroy the clientclient.Destroy();


Note in my network class You should manually call Start() and Delete()

-- DMINATOR
Ok, thanks! I think I'll use a similar approach.
I'm a little bit late to the party, but just for the sake of information I'll offer an alternative, which is what I personally use for my own network communications systems.

The approach is based around the idea that virtually all network communications systems are designed as stream-style communication (meaning continuous data flow) as opposed to discrete, chunk-style communication, which is usually more useful for things like game netcode. I personally use sockets via WinSock directly, and operate them in non-blocking mode, so that they do not delay processing of my application if no data is available to receive or if I can't currently send data immediately.

For ease, I usually have three layers. The lowest is a simple socket wrapper which I use to encapsulate WinSock's functions and handle things like automatically closing the socket, throwing exceptions in case of error conditions, etc. It also makes the higher-level logic portable and (if you feel insane) can be used to separate the socket-ness from the rest of the network logic. This means it's possible to "connect" with things like saved network conversations in data files, and re-enact certain scenarios without having actual live connections. This ability can be invaluable for debugging.

The next layer up is a protocol wrapper. This takes care of all of the sending and receiving, and should be designed to communicate data back to clients in a way that makes sense best to them. For example, if you want to send a pair of coordinates over your connection, the protocol wrapper should let you call a function and get back a set of numbers; i.e. it should do the work of taking the serialized data from the network and turning it into the format your program is going to use everywhere else.

The final layer is the actual client code; this is the bit that actually uses the network connections to do something useful. It should be very easy to build this layer when the other two are in place.


I typically set up the protocol wrapper to use a unique-ID request/response system. Each packet has a unique identifier, and any response from the server to the client will reply with the same ID. This makes it easy for the protocol layer to match up what data goes with what request, which is important because you might need to handle requests in a different order than you receive them on your network connection for some reason. The other trick is to send the length of each "chunk" at the beginning of the request. So the first eight bytes (two unsigned ints in 32-bit C++) will look like this:

00000000 - Request ID number
00000000 - Length of chunk

Then you can set up a struct with a field for each one of these, and read/write that struct directly on your socket. When you get a header, have the protocol layer wait until it receives enough data to fill up the given chunk length. When all of that data arrives, you have a complete request, and you can put the request block into a queue for your high-level logic to handle. Wrapping a request in a class is very useful, especially since you can use subclassing to easily turn a block of bytes from your network connection (serialized data) into useful data in your program (deserialization).


With this done, the last step is to provide access to the request queue from the protocol layer. Usually I have a SendRequest(), ReceiveAnyRequest(), and ReceiveSpecificRequest(). The Any/Specific functions are used to either get the first request in the queue, or to look for a request with a specific ID (useful if you need to get a reply to a specific packet). Then just set up a loop that polls the system for requests, and you're all set.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement