Opinions on my multiplay solution

Started by
11 comments, last by KnolanCross 11 years, 5 months ago
Ive developed a prototype for a game I am developing, I am just wondering on what more experienced network programmers think on my approach.

General:

  • 2D game, not a shooter obviously.
  • Using TCP - prefixing all messages with message lengths, all handled in the download algorithm.
  • Enabled NO_DELAY

Server:

  • Server has a fixed game loop of 60 ticks each second
  • At the start of each loop the server downloads all available data from all clients and tries to process any full messages received (i.e. if message length has been reached, etc...). During this stage, if a message received is for example, "player X moving left", then the server checks if the received coordinates from where the player moved from is not using any sort of speed hack by comparing current position in server memory (etc.. etc.. handled all of this), then all other connected clients have their upload buffer added to with this new movement message.
  • During the middle of a game loop (where the game tick is processed) all new messages to upload are placed into their respectful client upload buffers. (i.e. a NPC moved left/right).
  • Just before the end of the game loop the server uploads all data in all uploads buffers of all connected clients.
  • At the end of the game loop, all connections are checked to see if everyone is still connected correctly.
  • There is only one thread which handles all of this


Client:

  • Client has a fixed game loop of 60 ticks each second
  • Also downloads everything at the start of the loop
  • Also uploads everything just before the end of the loop
  • Checks the server connection at the end of the game loop
  • There is also only one thread which handles all of this.

What do you think about this approach? Obviously read/write calls from the network stream cannot be blocking calls.
All replies appreciated, thanks :)
Advertisement
That can work fine.
enum Bool { True, False, FileNotFound };
Ok, cool, thanks :)
Just know that 60 updates per second is a lot. Not sure if this is for the Internet, but with TCP you might see stuttering so you might want to lower that down to 10 to 20 in the future depending on your requirements.


Yeah, it is used over the internet. I thought about the frequency, but since the game I am making is not a similar to what a "shooter" game may use I thought why not.

Data is only sent when data actually needs to be sent, all messages are considered "important" and there's no need to constantly send out packets like that of what UDP connection may do.
My considerations:
- If you have some kind of id for each message, you mostly don't need to send the size of the message to the server (most of the messages will have an exact size).
- If you are using TCP, what do you mean by all connections are checked? I assume you are using select to implement this, since it is single-threaded, so the socket will be set and your receive will receive 0 bytes when a connection is finished or possibly a -1 if something went really wrong.

Currently working on a scene editor for ORX (http://orx-project.org), using kivy (http://kivy.org).

@KnolanCross
That idea about specific id not needing certain message lengths is interesting, might implement that.
For your second point it is important to note that I am only downloading data if .IsDataAvailable returns true (using .net TcpClient class) so the thread doesn't block. Only at the end of the loop each connection is checked by checking .Connected. This field only returns false though after a failed read/write, and just before the connections are checked necessary data is Uploaded to all clients meaning any failed uploads will render the .Connected field false.

What do you think about that?
Don't really know how the .net API works so I can't really tell if there is a better way to do so.

The points I would check are:
- Assuming .IsDataAvailable blocks the thread, will it return true if a connection drops?
- What happens is if the clients don't ask for updates when its idle (I am not assuming this happens, but with your explanation I couldn't be 100% sure) or if no client is connected. Wouldn't your simulation stop then?

Whenever you have a blocking single threaded approach is mostly good to have a timeout in the blocking part to check if everything is ok and avoid the program to stay locked forever. Other than that everything looks fine.

If it is a non-blocking API, I would recomend you to move to a blocking one, since a non-blocking approach is away more expensive.

Currently working on a scene editor for ORX (http://orx-project.org), using kivy (http://kivy.org).

Sorry .IsDataAvailable is called NetworkStream.DataAvailable.

Anyway on MSDN it says:
Use the DataAvailable property to determine if data is ready to be read. If DataAvailable is true, a call to Read returns immediately. If the remote host shuts down or closes the connection, DataAvailable may throw a SocketException.[/quote]
So I thought checking DataAvailable would never throw a exception/block the thread unless the remote (client closes the connection) - but that wont happen anyway because Ive handled graceful disconnects.

For point 2: the client would eventually get disconnected from the server either way (say if a NPC moved nearby). On the client side a latency message is sent every 500ms so they would loose connection always if the servers drops.

Regarding having only one single thread: I just didn't want to create a new thread for each new client (255 clients mean 255 threads). Do you think it is safe to use the .net Task Parallel Libraries on the main thread if it is needed? I am also planning to move to .net 4.5 and make extensive use of the new .net Async methods soon. Ill just slap a async keyword on every method that has a chance to block.

Thanks for reply's btw, really helps smile.png
checking DataAvailable would never throw a exception/block the thread unless the remote (client closes the connection)[/quote]

That is correct.

didn't want to create a new thread for each new client[/quote]

That is also correct.

use of the new .net Async methods[/quote]

You can do many cool things with those!

just slap a async keyword on every method that has a chance to block[/quote]

That is not necessarily the right thing to do. Async development needs more careful design and planning than that.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement