Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

leiavoia

Networking Infrastructure Overview (TBS slant - 4 questions)

This topic is 5396 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

What i need is some conceptual design feedback. The code i can work out on my own, but i need to know if the networking system i have already installed for my game is the right way to design such a thing. If it''s not, i''d rather work it out *now* rather than wire the whole game up then undo everything. Your advice and expertise are appreciated. These issues are probably rather common to all games, so perhaps the dialog would benefit everyone. Let''s start: The game: ---------------- The game is a turn based strategy complete with a NEXT TURN button, and the whole works. Think Civilization for a paradigm to visualize. As a result, speed is not an issue. I''m using TCP/IP and SDL_net for implementation. I''m programming in C++. For more details, you can look up the official site: http://www.project-axis.net/ The current system: ------------------- Right now there is an uber-class called "AxisNetwork" ("AXIS" is the name of the game) that controls anything network related. You can use it to switch on or off client or server functionality (so i can send out as a client, but receive as a server). For each action in the game, the network class has functions, so it''s a very fat interface. However, this allows it to hide the actual networking from the rest of the game. Under the hood, the client/server setup passes custom tailored data packets back and forth. There is a custom data packet for each major action in the game as well. So for instance, if a MoveUnit() command is given the network, it sends the appropriate data packet. The client receives the data packet which has built-in unpacking instruction. So on the client side, you might say "DataPacket->Unpack()" which would then move the unit accordingly on the client side. This gets messy with a 4x / TBS type of game since there are many different kinds of actions. For a FPS game, your actions are pretty limited to things like Shoot, Move, Jump, etc. In a 4x type of game, you''ve got all kinds of data wizzing around. QUESTION #1: Should i have a custom data packet for each type of action in the game, or is there some smarter way to pass information around? Creating custom data packets is a LOT of work and a nightmare to debug. The other sticky issue is that, with my current setup, if a client sends an action, say MoveUnit() again, the server gets it and amplifies the message to all other clients, much like a chat message would be. Now all the other clients unpack the same message and move the unit as specified in the data packet''s unpack routine. This creates a "fat client" system where all clients have all the game''s data in sync. this is good a few reasons, bad a few others: Good: ------ - we all have the same data. don''t have to worry about missing pointers or objects that never got created. - i''m guaranteed to have access to any data i need as a client. - easier to program. - reduces work on the server side Bad: ------ - increased network traffic - easily cheatable I would run into thin-client problems for things like drawing the main map screen. I need to know what units are where, what those units contain, what stats those units have, etc, and if any of that changes, the client needs to know about it because he has menues and screens where that data needs to be available to be properly shown. QUESTION #2: How do you get around having a "fat client" in a 4x game? How do other games do it? Any bright ideas? The third issue is with integrating the network code into the logic code in single vs multiplayer modes. If for instance, the player moves a slider on the interface which changes a statistic, the callback function as it is now might look like this:
Callback_PerformAction() {
	thing->DoThing();
	if ( network->IsOn() == true ) { // it''s a multiplayer game
		network->TellEveryoneElseToDoThingToo();
		}
	}
 
is this smart? The game will be chock-full of these kinds of "if network is on, do this" kinds of statements. Now every button and slider needs to have one of these. That''s a lot! The alternative that i''m thinking about is to have all actions sent by the network only when the NEXT TURN button is pressed, then have a massive frenzy of data packet traffic. All clients upload the states of *their* units, and the server amplifies those to the other clients. Then the server would handle actually making the units interact (i.e. combat resolution). QUESTION #3: Is it better to integrate the network code into the UI and have it be realtime or should i just do one massive update at the end of turn? It seems to me the that the one-update is the cleaner way to go. The final issue is with having a client and server on the same machine, i.e. "I''m the server in a multiplayer game". The problem is that when a data packet comes in to my machine, it doesn''t know if it''s a packet meant for the server or the client. The way i have it now, I activate both client *and* server and just keep the client''s network buffer-checking loop inactive, like network->StartServer(); network->StartClient(); network->StopClientFromListening(); The other problem of course is that when a packet comes in and the server handles it, it then send the message to everyone else, including myself. This is a problem. take a chat message for example: 1) i send a chat message 2) i receive chat messsage from myself as a server 3) send that chat message to everyone else on the clients list including myself 4) repeat steps 1-3 because i keep receiving the packet as a server like an idiot, never as a client Now all code needs a special clause to it, like "Do this if i''m a server, Do something else if i''m not" QUESTION #4: Is there a smarter way to handle having a client and server on the same machine? Sorry for the extra long post. I''m sure these are pretty serious issues that we can all benefit from talking about though. Thanks for reading. I would appreciate your comments. -- leiavoia The AXIS Project http://www.project-axis.net/

Share this post


Link to post
Share on other sites
Advertisement
Since your doing a TBS and not a realtime game, you can spend more time on parsing data packets. Instead of going the route of a unique packet type for each data set you need to transmit, develope a more general data binding method.

For instance, a class which binds data packets into key:value pairs. The keys are used to refrence the packet data, the value holds the actualy data itself, and an additional type data to make sure you dont try to read/write the wrong types.

This would be used as such :



void send_data( Packet_Data_Stream* p_stream )
{
try
{
p_stream->begin_packet();
p_stream->push("Max_Turns",1);
p_stream->push("Turn_Time",5.12);
p_stream->end_packet();
}
catch (Bad_Network_Stream_Exception&r)
{
//handle error

}

};

void rcv_data( Packet_Data_Stream* p_stream )
{
int max_turns=0;
float turn_time=0.f;

try
{
//notice reading data from stream is order invairant,

//since it uses the key to reference the variable

p_stream->begin_packet();
p_stream->pop("Turn_Time",&turn_time);
p_stream->pop("Max_Turns",&max_turns);
p_stream->end_packet();
}
catch (Bad_Network_Stream_Exception&r)
{
//handle error

}
};



So something like that maps packets into function instead of structs. And functions are much easier to add and maintain than the multitiude of structs, I''ve been there. There are a host of optimziation you can do, but suffice to say you can hash the key into 32 or 16 bits and have 1 byte for type data, and the data itself. So its still fairly efficient, for the flexiblity you gain.

Since the its order invariant, you can read/write in any order you wish.

To the next issue, since your making a TBS game, I suggest going the route the webservers take. Make your clients thin, build in some network UI code, run most of the simluation off the server, and serve the client pages like how a web server does.

That way you only have 1 game simulation, the one on the serve, and the clients are fully inusalted from hacking the server or the simulation.

Good Luck

-ddn


Share this post


Link to post
Share on other sites
leiavoia-

1. I''ve used a custom data packet for each type of action that can be performed. How else would you store the custom information for different types of actions/events? This process isn''t too difficult. I create a simple struct for each type of message and fill it with the needed information. So that you know what type of message is being recieved always make the first char (or first two chars if over 256 different message types) the "type" of message. When you read in the information check the first char to see what it''s value is and therefore the type of message. Then cast the message to the struct that relates to that type. It normally only takes me a couple of minutes to set up a new type of message this way.

2. I get around this by having the server do most of the processing. This stops cheaters and doesn''t have any significant performance implications. Basically all the client ever sends to the server is "can unit A do X?" and the server looks at this and if it is acceptable it sends back "Unit A can do X". As your game is Turn based it will make it a lot easier. You can send as much info as you want (within reason) and by using TCP you''ll be guaranteed that the data has arrived.

3. You should keep the network code as seperate as possible (in a seperate class if using OO). This will make changing the netcode a lot simpler as it develop''s. I use windows messaging (asynchronous) to recieve messaging and just process these incoming messages each frame. This is as real-time as you need as it occurs every frame.

4. All I did to overcome this is send messages from the server to client on one port (eg 5555) and send messages from client to server on a different port (eg 5556). This way the client listens on it''s port and the server listens on a different one. If you test with multiple clients you would then have to have each of them on their own port as well.

Hope this helps, and good luck with your development.

Doolwind


---------------------------------------------------------
Who of you by worrying can add a single hour to his life.
Matthew 6:27

Share this post


Link to post
Share on other sites
thanks for the replies. some response:

quote:
For instance, a class which binds data packets into key:value pairs. The keys are used to refrence the packet data, the value holds the actualy data itself, and an additional type data to make sure you dont try to read/write the wrong types. . . .
So something like that maps packets into function instead of structs. And functions are much easier to add and maintain than the multitiude of structs, I''ve been there. There are a host of optimziation you can do, but suffice to say you can hash the key into 32 or 16 bits and have 1 byte for type data, and the data itself. So its still fairly efficient, for the flexiblity you gain.


I''m not fully understanding what you are explaining here. My current system goes like this, for a chat message (pseudocode)

SERVERSIDE:

network->Chat("sender_name","Hello world!");

the network class then gets the function call and does this:
DataPacket_Chat* p = new DataPacket_Chat(sender, message);
p->Send(some_network_args);

the packet''s internal send routine then packs the data into the char buffer like this:
buffer->Add(size_of_this_packet);
buffer->Add(packet_type);
buffer->Add(size_of_sender_name);
buffer->Add(sender_name_string);
buffer->Add(size_of_message);
buffer->Add(message);
network->send(buffer, where_to);

CLIENTSIDE:

when the client gets it, it looks for the size, grabs that much info out of the network buffer, and looks at the first chunk (the type of datapacket) and reverse engineers the char-casted data it into a new object via an "auto-unpacking constructor".
// automatically grabs data from the net buffer to fill in member variables
DataPacket_Chat* p = new DataPacket_Chat(buffer_the_packet_data_is_in);
p->Handle(); // perform some action based on data

and voila! So you see, each game action needs a packet this way, otherwise the client has no concept of what it is you are trying to send and no way of knowing what it is you want done with the data.

Second issue:
From your two responses, one says put the network code in a seperate class, the other says put the network calls in the UI. You both vote for the thin client approach. There are two issues with thin clients:

1) "lazy UI". When i, say, move a slider to adjust a stat somewhere, it has to fire off a "May I?" message, the server sends back an "OK" message or a correction. That takes time over the internet. Consider something like scrolling the map screen for instance. Now some more units are visible, but the computer normally checks to see A) what units are on the map at those location, and B) if they are visible to the player. If that data is local, it''s no problem and the map would display instantly. If it''s across the Atlantic, it won''t.

2) this is also a single player game. Is it considered okay to put the single player game code on top of the network code or should it be seperate? What i mean is, if i play a single player game, should the machine still turn on a client and server and send packets to itself? or should it play "straight up" and just use the data structures in the game directly without messing with a network go-between?

The difference is that one is obviously faster. However, piggy-backing the single player game on top of the multiplayer network code would greatly simplify the UI interaction. So instead of


SomeUIFunction() {
thing->DoThing();
if ( network->IsMultiplayer() == true ) {
network->SendDoThingMessage();
}
}


i would just have this:


SomeUIFunction() {
network->SendDoThingMessage();
}


if i piggyback the network code for single player games, i get simplified coding, but less efficient interaction since every action has to go client->server->client just to get something done.



quote:
All I did to overcome this is send messages from the server to client on one port (eg 5555) and send messages from client to server on a different port (eg 5556). This way the client listens on it''s port and the server listens on a different one. If you test with multiple clients you would then have to have each of them on their own port as well.


That is brilliant. Obvious perhaps, but something i never thought of. thanks. Are you saying that every client needs to have a different port to connect to (i''m sure that''s not it), or are you saying every client on the same machine needs to have a different port? There will only be one client max on the same machine with the server. Any AI controlled players will be strictly handled by the server.

Share this post


Link to post
Share on other sites
quote:
That is brilliant. Obvious perhaps, but something i never thought of. thanks. Are you saying that every client needs to have a different port to connect to (i''m sure that''s not it), or are you saying every client on the same machine needs to have a different port? There will only be one client max on the same machine with the server. Any AI controlled players will be strictly handled by the server.


No, each client can have the same port normally, just if you want multiple clients on the one machine they will each have to listen on a different port.

quote:
What i mean is, if i play a single player game, should the machine still turn on a client and server and send packets to itself


Definately not. You should use your first piece of example code where you check "if ( network->IsMultiplayer() == true )". There is no reason to use a network messaging system (sockets) when you aren''t conneting to another computer. Just use normal function calls to achieve the same results.

quote:
Consider something like scrolling the map screen for instance


Not all actions need to be requested through the server. Something as simple as scrolling the map should be fine for the client to process, this will take some work of the server. You will have to look closely at your design as to what does or doens''t need to be requested through the server.

Doolwind

---------------------------------------------------------
Who of you by worrying can add a single hour to his life.
Matthew 6:27

Share this post


Link to post
Share on other sites
good advice.

what i''m thinking is that i should keep all data up to date on all machines, but only make the turn processing on the server. This way, things like movement and combat are all reserved for the server. The client issue orders to his agents and all machines are sync''d and updated with those same orders (so they know how to display them and such), but the actual actions will be carried out on the server.

So like i may program my units to do this and that. All other machines will know what i''m going to have my units do (exploitable i know), but only the server will have them actually do it, updating all clients with the results.

sound okay or am i missing anything?

Share this post


Link to post
Share on other sites
Personally, I would get the clients sending everything to the server. Then the server will send only the information to the other clients that each client should know about. This will reduce network traffic and cut down on exploitable information. For example, fog of war, don''t send a client information about units it can''t see.

Again this comes down to personal design. The fact you''re making a turn based game will make things a lot easier.

Doolwind

---------------------------------------------------------
Who of you by worrying can add a single hour to his life.
Matthew 6:27

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!