Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

 
Advanced search

1075919 Posts in 44152 Topics- by 36120 Members - Latest Member: Royalhandstudios

December 29, 2014, 03:15:41 PM
TIGSource ForumsDeveloperTechnical (Moderators: Glaiel-Gamer, ThemsAllTook)Bitstreams, messaging, and networking (c++)
Pages: [1]
Print
Author Topic: Bitstreams, messaging, and networking (c++)  (Read 706 times)
eigenbom
Level 10
*****



View Profile WWW
« on: December 13, 2012, 06:02:59 PM »

I'm beginning to think about networking for my game and how it all fits together with the event system. I'm interested to hear of anyone's experience in this area. I'll summarise what I think I need to do..

Atm Moonman uses a simple struct to handle game events:
Code:
struct Message {
  int type;
  union {
    struct {
      ID who;
      ID what;
    } someoneDroppedSomething;

    struct {
      ID who;
    } someoneDied;

    struct {
      ID from, to, which, toWhere, ...;
    } someoneSwappedItems;
  };
};
But this is pretty bad as sizeof(Message) is the size of the largest struct in the union, which seems especially bad for networking. So I am going to rewrite this and use bitstreams, manually just writing and reading the parts of the messages, and discarding the actual structs.

Raknet offers a bitstream class, from which you can create packets, so I could use that. Ideally the code for single player and multiplayer would look identical, except for the actual transmission and creation of the packets. But maybe that's a bit too idealistic? I'm not quite sure whether to run my single player mode maybe as a server-client type setup, where instead of sending packets I'm just passing the pointer to the bitstream..?

Anyways, there's not really a question here, but I'm interested in any comments you guys have.

Smiley
Logged

Ivan
Owl Country
Level 10
*


alright, let's see what we can see

Valaam0
View Profile
« Reply #1 on: December 13, 2012, 06:05:51 PM »

You can just connect locally to 127.0.0.1. That's basically what games like quake and HL do for single player.
Logged

http://polycode.org/ - Free, cross-platform, open-source engine.
eigenbom
Level 10
*****



View Profile WWW
« Reply #2 on: December 13, 2012, 06:10:10 PM »

Ah, yeh that makes sense, but then you're running full instances of the server and client (on two threads) right? I suppose that's inevitable if I want to reduce the complexity of writing for both single and multi..
Logged

ThemsAllTook
Moderator
Level 10
******


Alex Diener


View Profile WWW
« Reply #3 on: December 13, 2012, 06:30:32 PM »

With regards to sending binary data around, even without the size issue you mentioned, it's usually not advisable to directly send structs over the network or write them to the filesystem. Unless you're OK with your game running on only one CPU architecture, your data types may end up being different sizes (long and int are 32 bits on some archs, 64 on others) or endiannesses (big endian seems to be mostly gone, but might come back some day), and your structs might end up packed differently (extra padding bytes to align things on 32-bit boundaries). Conventional wisdom is to serialize your data into a format with known size, endianness, and packing before sending it, and deserialize it on the other end by the same rules.

I don't have much experience yet with real-time networking (been slowly working on a prototype to try it out), but I can see arguments for both methods that have been mentioned here. You might lose a bit of responsiveness if you have your single player mode communicating between separate server and client processes/threads, but if you do, it'll reduce the amount of code difference between single and multiplayer modes and potentially save you some debugging effort. If responsiveness is important enough to the single player game, it might be worth architecting your code such that you can run your game simulation synchronously for single player mode and asynchronously for multiplayer mode. Minecraft recently removed its synchronous single player mode in favor of a local server-client setup, and it lost quite a lot of responsiveness in doing so. Hard to say how much of that is due to the specific way Minecraft is coded and how much is fundamental to the way such a system works, though...
Logged
Xienen
Level 3
***


Greater Good Games


View Profile WWW
« Reply #4 on: December 13, 2012, 06:35:36 PM »

There's nothing wrong with creating custom communication structures for multiplayer.  Creating a series of structures that are filled in to transmit the data for a given event...bonus points for creating your own byte packing system.  Obviously it's easiest to just transmit the state of all objects every time they change, but it's truly a better experience if you can simulate the events on remote clients and develop acceptable limits on how different the events can operate on each machine.  You could even use a combination of these 2 systems, which is how Red Orchestra: Heroes of Stalingrad was optimized to handle 64 players, which was based off the Unreal Engine's remote simulation system.

As for the singleplayer/multiplayer issue, you can put multiplayer statements right into the single player code and have a check in the multiplayer packing system that checks to see if the game is, in fact, connected to a remote client.  If not, it can still run through all the same code, but just never try to transmit the data.  This is, again, how it's done in RO:HoS/UE3.
Logged

Crimsontide
Level 3
***


View Profile
« Reply #5 on: December 13, 2012, 06:59:09 PM »

...
 Minecraft recently removed its synchronous single player mode in favor of a local server-client setup, and it lost quite a lot of responsiveness in doing so. Hard to say how much of that is due to the specific way Minecraft is coded and how much is fundamental to the way such a system works, though...

Just a thought, you could get most of the advantages by using a message passing system between your client/server.  In the same process you run your 'server' and 'client' but in separate threads.  The single player messages are sent straight through a simple buffer/pipe/MPI using shared memory (aka very fast), and in the case of multiplayer messages are sent via network to the real server.  99% of the code, and all the messages are identical, so you could still get almost all the advantages without any latency.
Logged
eigenbom
Level 10
*****



View Profile WWW
« Reply #6 on: December 13, 2012, 07:31:28 PM »

Thx for the comments guys, there's lots to think about! It's super tempting to run the server and client on different threads and communicate via localhost, so maybe I'll do that, then I can fall back to a faster method if the game runs too slowly. Performance-wise, I only need to support maybe 4 simultaneous connections. It's not a big game, more of a 4 people explore a big world together type game. And I'm happy to make crazy restrictions, like there's one "master" client, and all other clients have to be within some distance to him.

@themsalltook, Yeh endianness is an important consideration. And actually the save files already take this into account. raknet and sfml both support this, so it shouldn't be an issue.

Quote
Obviously it's easiest to just transmit the state of all objects every time they change, but it's truly a better experience if you can simulate the events on remote clients and develop acceptable limits on how different the events can operate on each machine.

@Xienen Yeh I've done some client-side prediction for a simple game before, but I haven't thought about "acceptable limits". Am I right in saying that each client has a version of the world that may differ slightly? A simple example being the position of an object.. then if that position is too far from its true position it gets snapped back into its true place?
Logged

Ivan
Owl Country
Level 10
*


alright, let's see what we can see

Valaam0
View Profile
« Reply #7 on: December 13, 2012, 07:40:34 PM »

This is a great series of tutorials on network programming btw:

http://gafferongames.com/networking-for-game-programmers/
Logged

http://polycode.org/ - Free, cross-platform, open-source engine.
Gregg Williams
Level 10
*****


Retromite code daemon


View Profile WWW Email
« Reply #8 on: December 13, 2012, 07:52:47 PM »

I've always worked in raw bitstreams, while defining client/server packets that are fixed or variable length, and follow some defined layout of data per packet. A network thread then runs, and reads in any data received, picks up the packet ID and continues to read data to build up the packet. (This also handles when a single client/server defined packet's data is actually split across multiple incoming parts like what can happen with TCP/IP streams.)

How to treat this in SP is a good question, and I'm not sure. All the games I've ever worked with when it comes to MP, had dedicated servers that players typically weren't able to host themselves. Usually the servers where even coded in different language than the client.
Logged

LordJekky
Level 0
**


View Profile
« Reply #9 on: December 14, 2012, 01:55:15 AM »

I don't use c++ much, so i can't say how realistic it is in that case, but in C# i keep the network code separated from the game code so far as is possible - the game code sees a "Connection" object, which it can use to send messages and respond to events (incoming messages, disconnections, etc), and the rest of the network system is hidden from it. Messages can be structs or classes (each way has advantages and issues).

If the game is single player (or if hosting a multiplayer game) the messages are sent directly, no network involved (also meaning no connection to localhost - this wouldn't work if you wanted a dedicated server though). In multiplayer, messages are serialised (possibly compressed depending on the amount of data) and then sent via network.

To cut down on some work, it is possible to use reflection to do some of the serialising work (though this may be slow.. if speed is an issue, which it may be in a real-time system, then doing it manually might be a better option).

I assume that c++ can do most, if not all of this (and if it can't, there are surely libraries which can).

I find that this approach is good.. it allows the game code for single player or multiplayer to be identical (cutting down on a lot of bugs.. if you write separate modes you'll find that a lot of issues will appear only in multiplayer.. but most of your early testing will be single player). It also doesn't have too many speed issues for single player, since the messages are passed directly (if you have large amounts of data to send using classes for the messages could also gain some speed.. though in c++ i suppose you could simply pass pointers to the structs).

Just my personal experiences though.. may not work for everyone Smiley
Logged
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #10 on: December 14, 2012, 11:52:25 AM »

People seem to have you covered on the bistream part, but I'd reommend something like Protocol Buffers to handle serializing classes into bitstreams. Saves you from worrying about the exact bitformat you need, being reasonably efficient and debuggable. Further, it creates all the classes and code for you, which can save a lot of tedium repeating field names dozens of times.
Logged
Xienen
Level 3
***


Greater Good Games


View Profile WWW
« Reply #11 on: December 14, 2012, 03:47:46 PM »

@Xienen Yeh I've done some client-side prediction for a simple game before, but I haven't thought about "acceptable limits". Am I right in saying that each client has a version of the world that may differ slightly? A simple example being the position of an object.. then if that position is too far from its true position it gets snapped back into its true place?

Yeah, that's the exact idea.  This keeps objects states from jittering around as the client receives new data that conflicts with the latest prediction.  Which in the case of something like player's positions, can end up making your game look broken.
Logged

eigenbom
Level 10
*****



View Profile WWW
« Reply #12 on: December 14, 2012, 04:23:43 PM »

Lot's of advice to sort through, thanks a bunch. Smiley

@boris Protobufs sound really good but are probably overkill for me, I think I would feel more comfortable just having a single file that handles all the serialising and debugprinting of the messages manually, even if that means more maintenance on my end. But I'll let it percolate in my mind..

@Jekky Sounds like a good approach, and that way I can keep working on single player until I'm ready to jump to multi.

@Xienen Yeah makes sense, and really you are trading off frequent little jitters for infrequent large jitters. The acceptable limits I suppose would have to be maybe half the width of the smallest collectable object or something, in order to still appear responsive.. although I guess that depends on how much you simulate on the client.


Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic