Jump to content
  • Advertisement
Sign in to follow this  
AndreiVictor

Basic server framework. "game loop"

This topic is 2863 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

I am developing a simple server application using C++ and RakNet. I'm expecting to make a simple server that receives commands from clients and responding to them. The response time for each command varies on its complexity. However, I am not sure which is the best approach in making the loop of a server. I am troubled with the idea that multiple different clients can send commands at the same time. How should my server handle the commands?

Initially, I thought I would need to create a pool of connected clients on my server. Each connected client in the pool will have its state information. Now, given this "list" of connected clients in the pool should I:

a) on a single iteration, I traverse each client in the pool and do some processing on the command they requested (ala game loop), or;

b) create threads for each client and have their requests processed in their respective threads, or;

c) something you will suggest?

My application, in a high-level POV, would look like:

while( !m_terminate )
{
// input (receive packets)
CheckForIncomingPackets();

// output (process received packets)
}

Share this post


Link to post
Share on other sites
Advertisement
while (running) {
receive_incoming_packets();
process_messages();
while (now() - lastTime > dt) {
update_simulation();
lastTime += dt;
}
send_changes_to_clients();
]

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
while (running) {
receive_incoming_packets();
process_messages();
while (now() - lastTime > dt) {
update_simulation();
lastTime += dt;
}
send_changes_to_clients();
]


Hi thanks for replying.

I think that would fit for a game server, which contains game entity data.

I'm sorry if I failed to explain exactly what I'm looking for -- but how about patch servers or accounts/login servers?

Share this post


Link to post
Share on other sites
Quote:
Original post by AndreiVictor
but how about patch servers

Apache, nginx, any static HTTP server really.
Quote:
or accounts/login servers?

Umm... Ruby, Python, PHP or something. Those just need to check with DB and handle a few tokens. Web languages come with all the necessary plumbing built in. They also tend to take care of scaling and concurrency issues as far as viable.

If using C++, logic for an RPC server is about the same as most HTTP servers, just uses different encoding of requests. It's just a whole lot of boilerplate to even get going. But it's probably a waste of effort to do something like this in C++, unless there is a strong case for efficiency (aka really lots of users).

Share this post


Link to post
Share on other sites
Well think of it like those online games that require you to log in via the game client, and there are about over 5K users logged in at the same time. I'm sure DBMS can handle many users accessing the DB but how about your application?

Share this post


Link to post
Share on other sites
Just keeping a connection open is no cost. For any non-game server, you really just want to respond to requests or events as they come in. For an architecture where the user is connected to some log-in server for as long as he's playing, just open TCP, and use an efficient way of keeping many TCP connections open. boost::asio comes to mind, or using epoll directly on Linux, or I/O completion ports directly on Windows, coupled with a thread pool for handling requests.

5,000 idle TCP connections on a single server is not hard at all. The "1,000,000 client" project from last.fm put 1,000,000 TCP connections against a single commodity server; that server had like 32 GB of RAM and eight cores.

The things such a login server might need to do are fairly limited. Something like:
1) Accept a new connection, authenticate the user, and tell the user which game/zone server to connect to. Keep the connection open.
2) Accept requests from other nodes in the game cluster to send notifications to a particular user, and forward those notifications to that user. This is used for cross-game-zone messaging, such as "whisper."

That's about it! You might want to have one node that keeps the user->loginserver mapping in RAM, so it's easy to route a whisper or similar message to the right destination. That map ought to be very small. In fact, it might be possible to just replicate it onto each server in the cluster, and avoid that central point of failure (although the cross-cluster cost of updating that map as users come and go scales N-squared in that case).

Anyway, for any size game you're likely to build on your own, a single login server will be plenty! I would build it on Python/Twisted, or Erlang/OTP, but there are other fine systems too (Java-based, .NET, etc).

Share this post


Link to post
Share on other sites
Quote:
Original post by AndreiVictor
I am troubled with the idea that multiple different clients can send commands at the same time. How should my server handle the commands?


These days developing for multi-core machines and therefore using multi-threading has become more important.

At the lower level you have the OS delivering packets to your application. For nearly all games I would recommend using UDP because any required reliable delivery can be optimised for game data and it allows NAT punch through.

Your application can receive these packets on more than one thread. Typically this would be the module that processes and sorts these packets into streams of data coming from distinct clients. It handles the low level ACK/retry reliable delivery of some messages.

Then you might have a module that handles the actual network session. It manages how clients are added and removed from the network session, for example allowing clients to login with some credentials and then telling other connected clients when that person arrived in the session. This gives a more user friendly interface to the lower level client connections or socket handles.

Using the network session module could then be a higher level object oriented system, again this can use threads to receive packets, quickly process them and queue them for the object updates. Objects can be individually locked or the whole object list locked by the game code. Or the game code could call a poll method that forces the currently pending object updates to be applied.

As long as you have a mechanism for object access so that the game code can access consistent data whilst it is rendering or processing game logic or physics, then everything else that uses threads below your game code you don't need to worry about.

That is as long as the system you're using has been designed to use multiple threads and/or polling and has the API available so you can choose exactly what components you want will use what threading model. For example you might find it easier to allow the UDP manager and the network session layer to use threads while your high level object manager uses single threaded updates to your objects. Some systems cannot support multi-threading of course but it is advantageous to have a multi-platform API so the same code can be used for different versions. This is why when developing ReplicaNet I included the PollLayer API so the developer can choose what threading model suits them best.


Important questions that spring to mind are how easy is it to debug such a system where objects can be sending their updates to lots of different clients. This depends on what tools are available for you to use. For example you don't want to be in a situation where your game is getting spikes of data from a client and you're unable to debug under what conditions those updates are being sent. This is why I developed the RNVisualDebugger, this can collect object update information from replicated objects as the network session is running and can let you see which objects are using bandwidth at a given time. The bandwidth graph analysis and filtering lets you see exactly which updates are being sent, to who and why they were sent. You can then use this information to optimise the update filter parameters in your game. As far as I am aware none of the other network APIs offer this high level of detailed debugging for their object updates. Some network object systems can get quite complex, lots of objects, lots of object members, so having this kind of debugging available helps save a lot of developer time.


Quote:
Original post by AndreiVictor
I'm sorry if I failed to explain exactly what I'm looking for -- but how about patch servers or accounts/login servers?


Patches can be simple compressed files. Or you can have binary difference patches where large files can be updated with small patches. It takes longer to calculate these kinds of binary difference patches but I have to say I prefer the binary difference patching method because it reduces the bandwidth being used when users download them. This results in faster updates for users. Delivery of this patch data can be with normal HTTP based servers. A product patcher module can then use standard publicly available HTTP requests to get this data. Optimising an HTTP server (or cluster of servers) is an easy problem to solve.

User accounts/login servers can all exist as part of a larger system for tracking user account scores and other persistent data and friends and chat systems. Something like this setup for RNLobby. The lobby system can also support the NAT resolving and punch through server. Developing each component separately is a good choice because it allows for different components to be clustered as required by demands for server CPU and database access. For game sessions and persistent score data having some kind of mechanism to update statistics with server side paranoia checking of data is important. This means a game server doesn't always need to have direct database access, for security this is important. Instead a game server can request a client's statistics are updated or depending on your configuration any client in the game session could request that their statistics are updated. The statistics tracking server can be programmed to verify that game sessions have realistic scores before allowing them to get submitted to the database.

All of these lobby modules can use a database, like MySQL, so offer the ability for web developers to create web login and account management features.

[Edited by - Martin Piper on August 22, 2010 7:22:39 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

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

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!