Raknet Questions

Started by
11 comments, last by darkzerox 18 years, 10 months ago
1. In a simple example, I create a bitstream and write (outBitstream.Write) one integer (the identifier) and two floats (x and y positions). When I send and receive this, I tried reading (inBitstream.Read) in one integer and two floats. However, through trial and error I discovered it wasn't "aligned" correctly. Ex, what should have been the "y" coordinate was the "x" coordinate, and the rest was jibberish. Reading in three floats however, it works correctly. My best guess is that it "aligns" it to the largest type. What's the best way around this, if I want to send it as an int, and read it as an int, and get the correct data? Also, once we receive the packet we check packet->data[0] for the identifier. I *really* don't need to inBitstream.Read this in again, after I already know what it is. Is there a way to move the Read cursor to the next data member? Or perhaps when it returns the identifier, it moves the cursor for you? 2. Excuse me for being "general", but my game gets up to 10-20s behind. What do I do? I'm sending as UNRELIABLE_SEQUENCED. 3. I don't really understand the purpose of this function
unsigned char GetPacketIdentifier(Packet *p)
{
    if ((unsigned char)p->data[0] == ID_TIMESTAMP)
    return (unsigned char) p->data[sizeof(unsigned char) + sizeof(unsigned long)];
    else
    return (unsigned char) p->data[0];
}
I understand that it returns the packet identifier, but if it is a timestamp, what exactly does it return? 4. Just for confirmation, "ping" times are round-trip times, from, say, my computer to someone else's and back to mine, correct? I'm assuming it's impossible to find the time it takes for a one-way trip because the system clocks could be different. 5. Servers, clients and peers. I'm making a two player game (to be expanded later). I send some data from the server to the client which updates the server-player on his screen, and the client sends some data to the server which updates his player. In this way, the "server" doesn't really play a lead role; they both send data back and forth. What's the difference in using "peer" and how does it work? You would still need a "server", wouldn't you? For a game with more than two players, what would be the best approach. I could send data from each player to every player, updating their coordinates, or from each player to a server, which in turn sends it to everyone. Now I'm just speculating here (I really have no network knowledge), but if a person has a slow computer (or connection?) it would be better to send it to the server, so that the slow computer has to send less data out. However, this means that to get from computer A to B, it has to go through a third computer: the server. So, if I'm not mistaken, if everyone has identical computers and connections, it would be better to send their coordinates to everyone, but if everyone had poor connections, but the server was extremely fast, it would be better to send to the server first? 6. My game has destructable terrain. Obviously it's important that the terrain is the same on each computer. What would be the best approach to ensure that they are the same? I could send only the bullets over the net, but what if they don't collide exactly the same? I've tried this and noticed that although pretty close, sometimes the bullet my penetrate slightly further into the terrain on one computer, than on the other. This probably has to do with how frequent the checks are and such, however I'm using a line-check algorithm. It checks every point along the bullets path, from its old position to its new position to check if it has collided with anything. However, this doesn't seem to be "perfect" enough. (Probably CPU costly too). I could try sending the entire map over the net, but it is a 2560x1200 pixel SDL_Surface*. Which brings me to my next question... 7. How exactly would I send an SDL_Surface*? I understand that I have to cast it to a char, but then how do I get it back? And since it already is a pointer, it makes it even more confusing. I know I've asked a lot of questions, but hopefully someone else will find these useful too. If I've asked a question that has already been answered, it would be nice if you could simply point me in the right direction (a link).
Advertisement
It's been a few months since ive done any game development / networking, but i'll try my best to answer your questions...

Quote:Original post by darkzerox
1. In a simple example, I create a bitstream and write (outBitstream.Write) one integer (the identifier) and two floats (x and y positions).


Are you sure you are writing / reading the same size data types? you shouldn't use an integer as an ID, but instead just a single byte (unsigned char)... unless you have more then 256 types of messages... but even then, i would still just use 2 chars so that 256 of my messages only had a one byte header. rememeber, if you write an ENUM, cast to an unsigned char first!

Quote:
Also, once we receive the packet we check packet->data[0] for the identifier. I *really* don't need to inBitstream.Read this in again, after I already know what it is. Is there a way to move the Read cursor to the next data member? Or perhaps when it returns the identifier, it moves the cursor for you?


It doesn't move it for you, since operator [] cannot be that smart (remember i believe data is just a normal pointer). Instead, you just tell the BitStreams constructor to ignore the first byte... so instead of doing (something like)

BitStream input(data)

do:

BitStream input(data+1)

Quote:
2. Excuse me for being "general", but my game gets up to 10-20s behind. What
do I do? I'm sending as UNRELIABLE_SEQUENCED.


This could be an entire thread on its own... there are MANY factors which could be the cause of a big lag like that...

Quote:
3. I don't really understand the purpose of this function

*** Source Snippet Removed ***


When you send a bitstream, the first byte should be an identifier. If you want to timestamp your packets, then that first byte MUST be ID_TIMESTAMP. Not only that, but the following 4 bytes after that should be the timestamp itself. The byte after that is the real ID of the message... what that code does is return the ID... if its timestamped (if data[0] == ID_TIMESTAMP) then it must skip the first 5 bytes to grab the real ID.

Quote:
4. Just for confirmation, "ping" times are round-trip times, from, say, my computer to someone else's and back to mine, correct? I'm assuming it's impossible to find the time it takes for a one-way trip because the system clocks could be different.


Yes, ping is RTT. However, it IS possible to find one way times... thats what timestamps are for! RakNet makes it very easy to find this... just timestamp your packet, and when you get the packet on the other end, do RakNetGetTime() - timestamp to find out how long it took to get there...

Quote:
5. Servers, clients and peers. I'm making a two player game (to be expanded later). I send some data from the server to the client which updates the server-player on his screen, and the client sends some data to the server which updates his player. In this way, the "server" doesn't really play a lead role; they both send data back and forth. What's the difference in using "peer" and how does it work? You would still need a "server", wouldn't you?


I'm confused on your example here... when you say "seerver-player", what do you mean? I think you mean the server's AI player... in this case, you are playing with just yourself and the server... or are you saying that the server is the actual game that the player can control? If you only want to have 2 players, i would still recommend keeping a client / server architecture. It will be much easier to expand to multiple players this way. Plus, it allows you to prevent cheating if you run the server on a trusted machine...

Also, i think you are confusing what RakNet calls "Server" and "Peer" and what Server and Peer mean in networking terms... In RakNet, a Server and Client just inherit from the Peer class and add more features... the server and client in RakNet are nothing more then a peer with extra bells and whistles... some people just use peers even in a client / server game, simply because they do not want those extra bells and whistles.. in fact, you might want to do this too - some of those bells & whistles are not that great for my situation... for example, when a new player connects, the server will automatically tell all clients this players IP and port... which isnt so great for security reasons.... its also usually something you would want to do yourself, in case you wanted to send more information..

Quote:
6. My game has destructable terrain. Obviously it's important that the terrain is the same on each computer. What would be the best approach to ensure that they are the same? I could send only the bullets over the net, but what if they don't collide exactly the same? I've tried this and noticed that although pretty close, sometimes the bullet my penetrate slightly further into the terrain on one computer, than on the other. This probably has to do with how frequent the checks are and such, however I'm using a line-check algorithm. It checks every point along the bullets path, from its old position to its new position to check if it has collided with anything. However, this doesn't seem to be "perfect" enough. (Probably CPU costly too).


things like this are fairly complicated to implement.. if it were me, id say goodbye to the destroyable terrain, at least in multiplayer mode [grin]. try to keep the physics as simple as possible if you want to keep all your hair.

Quote:
I could try sending the entire map over the net, but it is a 2560x1200 pixel SDL_Surface*. Which brings me to my next question...

7. How exactly would I send an SDL_Surface*? I understand that I have to cast it to a char, but then how do I get it back? And since it already is a pointer, it makes it even more confusing.


First of all, why is your map an SDL_Surface* ? This kind of seems unnessasary.. perhaps you could extract only the data you needed and send that. To send an SDL Surface (which i don't recommend doing unless its 100% nessasary), check out the SDL docs to read on how to grab the raw binary data from that SDL_Surface. It's probably something like surface->data ...

Remember, if you just want to send players the map that is currently being played, there are other ways. Web servers are great for this so that your game doesnt choke all of a sudden when a new player connects. Giving more details on what type of game you are making would help me give you a better solution though.
FTA, my 2D futuristic action MMORPG
Quote:Original post by graveyard filla
Are you sure you are writing / reading the same size data types? you shouldn't use an integer as an ID, but instead just a single byte (unsigned char)... unless you have more then 256 types of messages... but even then, i would still just use 2 chars so that 256 of my messages only had a one byte header. rememeber, if you write an ENUM, cast to an unsigned char first!


Oops.... it was an enum. I think I *was* reading it as a char (although not unsigned), but sending it as an enum (which is interpreted as an int?). That could have been the problem :) Thanks.

Quote:
do:

BitStream input(data+1)


Heheheh.. that's good thinking. Don't know why I didn't think to do that :D


Quote:
This could be an entire thread on its own... there are MANY factors which could be the cause of a big lag like that...



Thought so :) Although I *think* the big thing I was doing wrong was only reading one packet per frame. So if one game runs slightly faster than the other, they would all get queued up because its not reading them in fast enough, eh? So I just put a simple while loop and had it read til there were no more packets. It's just a tiny bit behind now... I'm sure with some cleaner code I could get it up a little more.


Quote:
When you send a bitstream, the first byte should be an identifier. If you want to timestamp your packets, then that first byte MUST be ID_TIMESTAMP. Not only that, but the following 4 bytes after that should be the timestamp itself. The byte after that is the real ID of the message... what that code does is return the ID... if its timestamped (if data[0] == ID_TIMESTAMP) then it must skip the first 5 bytes to grab the real ID.


Ahhhh... I thought it was trying to return the time itself, but that wouldn't make any sense at all. Returning the real identifier makes sense :D

Quote:
Yes, ping is RTT. However, it IS possible to find one way times... thats what timestamps are for! RakNet makes it very easy to find this... just timestamp your packet, and when you get the packet on the other end, do RakNetGetTime() - timestamp to find out how long it took to get there...


Hm... I just can't imagine how raknet could find the difference in system times. Oh well.


Quote:
I'm confused on your example here... when you say "seerver-player", what do you mean? I think you mean the server's AI player... in this case, you are playing with just yourself and the server... or are you saying that the server is the actual game that the player can control? If you only want to have 2 players, i would still recommend keeping a client / server architecture. It will be much easier to expand to multiple players this way. Plus, it allows you to prevent cheating if you run the server on a trusted machine...

Also, i think you are confusing what RakNet calls "Server" and "Peer" and what Server and Peer mean in networking terms... In RakNet, a Server and Client just inherit from the Peer class and add more features... the server and client in RakNet are nothing more then a peer with extra bells and whistles... some people just use peers even in a client / server game, simply because they do not want those extra bells and whistles.. in fact, you might want to do this too - some of those bells & whistles are not that great for my situation... for example, when a new player connects, the server will automatically tell all clients this players IP and port... which isnt so great for security reasons.... its also usually something you would want to do yourself, in case you wanted to send more information..


By server-player I meant the human player, who was playing on the computer which was running the game server. I don't have any AI yet...it's going to be a touch hard to do in my game. Need to find some kind of advanced path finding thing. Not only would it need to find the shortest open path, but it has to find a path that the player can actually follow by jumping and such. It's a worms-type game. (more about that later)

Thanks for clearing up those definitions :) I'd use peer.. but it doesn't appear to have a Start(...) function... thus I'm confused how I'd actually start the server. Guess I can dig some more up on that later.



Quote:
things like this are fairly complicated to implement.. if it were me, id say goodbye to the destroyable terrain, at least in multiplayer mode [grin]. try to keep the physics as simple as possible if you want to keep all your hair.


That hard eh? Oh boy.. and this was supposed to be a "simple" do-able project. I could have chose something more complicated...

Maybe my Mario-style game was a better idea :) But I had too much trouble with collision... (though I've learned a lot more now). Tile-based collision ought to be easier than pixel-based eh? Actually.. probably not...


Quote:
First of all, why is your map an SDL_Surface* ? This kind of seems unnessasary.. perhaps you could extract only the data you needed and send that. To send an SDL Surface (which i don't recommend doing unless its 100% nessasary), check out the SDL docs to read on how to grab the raw binary data from that SDL_Surface. It's probably something like surface->data ...

Remember, if you just want to send players the map that is currently being played, there are other ways. Web servers are great for this so that your game doesnt choke all of a sudden when a new player connects.


Why is my map an SDL_Surface? Well, I simply loaded my map from a *.gif. SDL already knows how to blit an SDL_Surface pretty fast, so I figured I'd just leave it.

In my original version I actually did take the map and converted it to booleans... but then I had to draw it pixel by pixel... which actually wasn't *too* bad... but its a fairly physics-intense game, so I need it as fast as possible.

I didn't think sending the SDL_Surface would be the best approach, but it has to be good to know doesn't it? You can apply that same knowledge to anything else.


Quote:Giving more details on what type of game you are making would help me give you a better solution though.


more details here
On the topic of sending an SDL_Surface:

You obviously want to send the actual data, not the 4 byte pointer :). This is complicated by the fact that SDL_Surface also has pointers.

Your code should look something like this:

//To send:SDL_Surface * surface;send( socket , &(surface->w) , sizeof( surface->w ) , 0 );send( socket , &(surface->h) , sizeof( surface->h ) , 0 );//..also probably want to send things like color depth, pallet info, etc..SDL_LockSurface( surface ); //so we are allowed to access pixels...for ( int row = 0 ; row < surface->h ; ++row ){    //note: pixels is allready a pointer...    send( socket , surface->pixels + (row * surface->pitch) , surface->w * surface->format->BytesPerPixel , 0 );}SDL_UnlockSurface( surface );//To recieve:int width , height ; //same type as SDL_Surface->w and SDL_Surface->hrecv( socket , &width , sizeof(width) , MSG_WAITALL );recv( socket , &height, sizeof(height) , MSG_WAITALL );//..recieve color depth, pallet info, etc...SDL_Surface * surface = CreateRGBSurface( ... , width , height , ... );SDL_LockSurface( surface );for ( int row = 0 ; row < surface->h ; ++row ){    recv( socket , surface->pixels + (row * surface->pitch) , surface->w * surface->format->BytesPerPixel , MSG_WAITALL );}SDL_UnlockSurface( surface );


Note that I make my share of naughty assumptions in this code... such as that both hosts have the same byte order, that the code will never fail (no error checking), etc...

Just giving the basic idea :-).
Hm... probably better to pack a whole bunch of pixels into one packet though, no?

But yes, i get the general idea.

If i were to do that, I'd probably just have an array of pixel colors. the rest can be deduced.


Not sure what I should do at this point. if i should bother to continue with this game, if i should keep it single player and work on AI....

or i should go back to my old game with all my new knowledge...

I really want to complete something... just one game!

I've started so many.. all start off hopeful and excited.. but then as soon as the engine is built, and the excitement wears off.. and it all just becomes tedious... I give up!
Quote:Original post by darkzerox
Hm... probably better to pack a whole bunch of pixels into one packet though, no?

But yes, i get the general idea.

If i were to do that, I'd probably just have an array of pixel colors. the rest can be deduced.


I never messed with Winsock or TCP even really, but AFAIK his example is using TCP which treats everything as a steam... e.g. there are no "packets".. I believe packets are a UDP concept.

I'm confused though.. how is your entire map just a surface? Do you really just blit a chunk of a huge surface to render your screen / map ?

Quote:
Not sure what I should do at this point. if i should bother to continue with this game, if i should keep it single player and work on AI....

or i should go back to my old game with all my new knowledge...

I really want to complete something... just one game!

I've started so many.. all start off hopeful and excited.. but then as soon as the engine is built, and the excitement wears off.. and it all just becomes tedious... I give up!


that all depends on how much you enjoy network programming... network programming is an art... it takes ALOT of patience, skill, and dedication to make a multiplayer game... there are MANY factors which you have to consider... as hplus would say, there is an entire contiuum of factors to balance out that go into making a MP game work - hiding latency, bandwith usage, keeping things in sync... each one of these will offset the other and you must fine the perfect balance... which is different for every genre and every game...

for example, if you wanted a perfect sync (something that destroyable terrain kind of asks for, ofcourse it may or may not be neccasary), you could sacrifice latency hiding to do this... have the server just send data to all clients at the same time, and have clients wait for a round trip before executing commands... the clients all see a ping/2 old simulation... this keeps a near perfect sync but with the disadvantage of lag... in certain games this is fine... in an action game, this might be acceptable or it could not be though... there are many things you can do to try and get working... if you make a post with a lot of details then you will get the help here.

[Edited by - graveyard filla on June 9, 2005 1:15:05 PM]
FTA, my 2D futuristic action MMORPG
Quote:Original post by darkzerox
Heheheh.. that's good thinking. Don't know why I didn't think to do that :D


Just FYI, BitStream also has an IgnoreBits() function. You could just do IgnoreBits(8) instead.
FTA, my 2D futuristic action MMORPG
Quote:Original post by graveyard filla
I'm confused though.. how is your entire map just a surface? Do you really just blit a chunk of a huge surface to render your screen / map ?


That's exactly what I'm doing.

What else would you suggest? The map is really just an image. Sure I could make it into some form of array, but I'd still have to draw it pixel by pixel.

Quote:Original post by graveyard filla
Just FYI, BitStream also has an IgnoreBits() function. You could just do IgnoreBits(8) instead.


Oooh. that's good to know :D

Quote:that all depends on how much you enjoy network programming...


Not particularily enjoying it :D I suppose there is some "enjoyment" to be had when you come up some intricate plan to send data and have it run at an acceptable rate... however, I'm still learning here. I want something a can complete, with success :) My little game seems to have become to advanced for my own good.

I can barely get it to run at a good frame rate SINGLE player.

Quote:Original post by darkzerox
What else would you suggest? The map is really just an image. Sure I could make it into some form of array, but I'd still have to draw it pixel by pixel.


Well, doesn't that leave you with either really small maps or really huge file sizes? i'm not so sure about 2d but if you were using a 3d API the texture memory usage would be unacceptable IMO..... If you don't mind having smaller maps and larger files though then stick to what you're doing.. it's all up to you really... But, if you wanted to make it nicer, I would use either tiles or if you wanted nice physics just use whole objects with different types of geometry.. If you are going for multiplayer, I would simplify the physics and just use square geo with tiles... It would simplify things a lot more.. If you were doing single player though it would be a lot easier.


Quote:

Not particularily enjoying it :D I suppose there is some "enjoyment" to be had when you come up some intricate plan to send data and have it run at an acceptable rate... however, I'm still learning here. I want something a can complete, with success :) My little game seems to have become to advanced for my own good.

I can barely get it to run at a good frame rate SINGLE player.


Do you have enough experiance to be tackling the project you want to do? Maybe you should do some simpler games first like pong or pacman. If you have no network programming experience I would strongly recommend making something like an online pong game, just to learn the basics of net programming. Trust me, it will save you a lot of time in the long run when you start to realize your code is complete crap and you have to refactor [grin].
FTA, my 2D futuristic action MMORPG
Quote:Original post by darkzerox
Hm... probably better to pack a whole bunch of pixels into one packet though, no?


The TCP layer should automatically take care of that for you - 1 send != 1 packet with TCP. Basically, there's a small delay (~1 second I want to say?) by default before a packet is sent if it's undersized (that is, under the MTU). That way, if you then call more send()s before the second is up, that data will automatically be piggybacked along.

I remember this from my vauge recollections about telnet programming, where one usually disabled this so that there wasn't a constant delay between typing into the telnet program and having the program respond - such a delay is not desired in a highly interactive application. Most Many Some games don't suffer from this problem automatically due to their high throughput, meaning a packet is allways "filled up" very quickly (since once it's "full" it can immediately be sent out).

Looking at my tcp manpage, this is known as "the Nagle algorithm", which can be disabled like so:

protoent * tcp_protoent = getprotobyname( "TCP" );
unsigned int delay = 0; //for false, non-zero for true
setsockopt( socket , tcp_protoent->p_proto , (void *)&delay , sizeof(delay) );

Not sure if I've got that right (I'm just going off my manpages), but it's my best guess off the top of my hat.

This topic is closed to new replies.

Advertisement