Algorithm for Client-Server Games

2019-03-08 04:36发布

问题:

For stand alone games, the basic game loop is (source: wikipedia)

while( user doesn't exit )
  check for user input
  run AI
  move enemies
  resolve collisions
  draw graphics
  play sounds
end while

But what if I develop client-server-like games, like Quake, Ragnarock, Trackmania, etc,

What the Loop/Algorithm for client and the server parts of the game?

回答1:

It would be something like

Client:

while( user does not exit )
    check for user input
    send commands to the server
    receive updates about the game from the server
    draw graphics
    play sounds
end

Server:

while( true )
    check for client commands
    run AI
    move all entities
    resolve collisions
    send updates about the game to the clients
end


回答2:

Client:

connect to server
while( user does not exit && connection live)
    check for user input
    send commands to the server
    estimate outcome and update world data with 'best guess'
    draw graphics
    play sounds
    receive updates about the game from the server
    correct any errors in world data
    draw graphics
    play sounds
end

Server:

while( true )
    check for and handle new player connections
    check for client commands
    sanity check client commands
    run AI
    move all entities
    resolve collisions
    sanity check world data
    send updates about the game to the clients
    handle client disconnects
end

The sanity checks on the client commands and world data are to remove any 'impossible' situations caused either by deliberate cheating (moving too fast, through walls etc) or lag (going through a door that the client thinks is open, but the server knows is closed, etc).

In order to handle lag between the client and server the client has to make a best guess about what will happen next (using it's current world data and the client commands) - the client will then have to handle any discrepancies between what it predicted would happen and what the server later tells it actually happened. Normally this will be close enough that the player doesn't notice the difference - but if lag is significant, or the client and server are out of synch (for example due to cheating), then the client will need to make an abrupt correction when it receives data back from the server.

There are also lots of issues regarding splitting sections of these processes out into separate threads to optimise response times.

One of the best ways to start is to grab an SDK from one of the games that has an active modding community - delving into how that works will provide a good overview of how it should be done.



回答3:

It really isn't a simple problem. At a most basic level you could say that the network provides the same data that the MoveEnemies part of the original loop did. So you could simply replace your loop with:

while( user doesn't exit )
  check for user input
  run AI
  send location to server
  get locations from server
  resolve collisions
  draw graphics
  play sounds
end while

However you need to take into account latency so you don't really want to pause your main loop with calls to the network. To overcome this it is not unusual to see the networking engine sitting on a second thread, polling for data from the server as quickly as it can and placing the new locations of objects into a shared memory space:

while(connectedToNetwork)
    Read player location
    Post player location to server
    Read enemy locations from server
    Post enemy locations into shared memory

Then your main loop would look like:

while( user doesn't exit )
  check for user input
  run AI
  read/write shared memory
  resolve collisions
  draw graphics
  play sounds
end while

The advantage of this method is that your game loop will run as fast as it can, but the information from the server will only be updated when a full post to and from the server has been completed. Of course, you now have issues with sharing objects across threads and the fun with locks etc that comes with it.

On the server side the loop is much the same, there is one connection per player (quite often each player is also on a separate thread so that the latency of one won't affect the others) for each connection it will run a loop like

while (PlayerConnected)
    Wait for player to post location
    Place new location in shared memory

When the client machine requests the locations of enemies the server reads all the other players locations from the shared block of memory and sends it back.

This is a hugely simplified overview and there are many more tweaks that will improve performance (for instance it may be worth the server sending the enemy positions to the client rather than the client requesting them) and you need to decide where certain logically decisions are made (does the client decide whether he has been shot because he has the most up to date position for himself, or the server to stop cheating)



回答4:

The client part is basically the same, except replace

run AI
move enemies
resolve collisions

with

upload client data to server
download server updates

And the server just does:

while (game is running)
{
    get all clients data
    run AI
    resolve collisions
    udpate all clients
}


回答5:

You can use almost the same thing, but the most of you logic would be on the server, you can put timers, sounds, grafics, and other UI components on the client app. Any business rule (AI,Movements) goes in the server side.



回答6:

A very useful and I would argue pertinent paper to read is this one: Client-Server Architectures

I gave it a read and learned a lot from it, a lot of sense was made. By separating out your game into strategically defined components or layers, you can create a more maintainable architecture. The program is easier to code, and more robust than a conventional linear program model like the one you've described.

That thought process came out in a previous post here about using a "Shared Memory" to talk between different parts of the program, and so overcoming the limitations of having a single thread and step-followed-step game logic.

You can spend months working on the perfect architecture and program flow, read a single paper and realise you've been barking up the wrong tree.

tldr; read it.