Page 1 of 2

Game architecture

Posted: 23 Sep 2005 17:21
by uzurpator
OK - this is probably the most important part of all topics - as it will destine the whole game.

Now - what game architecture will we use? Client - Server? Peer-2-Peer?

Should the engine be multi threaded? And if so - how?

As for me - I think that we should develop two things. Client and server in a multi threaded fashion. This will require to develop a protocol to send data to and from the server, but will ensure that if something goes askew - it can only do so in one place (server).

As for multi threading - since multi-core cpus are starting to be avalible to everyone - I think it is mandatory for the game to support multi-threading. The size of data we are going to manipulate will require every bit of processing power we can get.

As for the detailed diagrams - I'll have to give it some thought :)

Posted: 24 Sep 2005 01:36
by uzurpator
Ok - a quickie showing my idea of the game structure

Asterisk shows what is required NOW to do.

Code: Select all

Server
|-> Network
|-> * Map
|     |-'
|     | '
|     |-'
|     \- * Tiles
|-> * Data
|     |-'
|     | '
|     |-'
|     \-> * Parser
|-> * Time
\-> * Client
    |-> * Interface
    |     |-> * GUI
    |     |     \-> * Renderer
    |     |           |- * GL
    |     |           \- D3D
    |     \-> * SDL
    |         |-> * Keyboard
    |         \-> * Mouse
    |-> * Timer
    \-> Sfx

Server - main class, the watchdog responsible for keeping the game run at steady pace (25 fps?), it also starts all methods from lower classes.

Network - class responsible for communication with other servers via TCP/IP network (note - only one server actually runs the game, the rest just feeds data to the clients).

Map - a class containing (and modifying) all data about towns, tiles, vertices and alike. Also contains map generator. The dots indicate various classes that will hold town data, track data etc.

Tiles - a class holding information on tiles (what kind of terrain are they). It contains map generator. It also holds geometry data - that is normals, vertex colors and various other properties that are associated with Tiles.

Data - class that holds all definitions of all game objects (tiles, buildings, trees, vehicles, shaders - everything). It holds important Parser class which will mow through all configuration files providing all data needed to make the game.

Time - class being the watchdog looking at the passing time (real time and game time alike)

Client - second most important class, it is responsible for all the things that are actually seen. It is responsible for drawing and input handling. Client is responsible for sending the server all the commands from the user.


Interface - A class handling user input.

GUI - a class responsible for drawing and handling Graphical User Interface

Renderer - a class that will draw all the stuff. It holds preprocessed models (in a form of display lists) and shaders.

GL/D3D - respectively - openGL and Direct 3D classes responsible for the drawing :)

SDL - a class responsible for testing controls - eg mouse and keyboard input.

Timer - client level time measure - only for real time.

Now typical use case would look like this (in the game loop)

Code: Select all

server -> start server frame // start measuring miliseconds in this frame
server -> time -> update Calendar
server -> read and process client commands
server -> map -> update Vehicles
server -> map -> update Towns
server -> map -> update Other
server -> Prepare Client Data
server -> client -> start client frame
server -> client -> Interface -> SDL -> test Keyboard
server -> client -> Interface -> SDL -> test mouse
if (mouse click) server -> client -> Interface -> GUI -> test for window collisions
if (window collision) server -> client -> inteface -> GUI -> determine commands
if (keyboard) server -> client -> interface -> SDL -> keyboard -> determine commands
server -> client -> Inteface -> GUI -> Renderer -> draw windows
server -> end client frame
server -> end server frame

Posted: 25 Sep 2005 21:29
by uzurpator
Comments? Anyone?

Posted: 25 Sep 2005 22:10
by DominionSpy
uzurpator wrote:Comments? Anyone?
I was going to wait until I had drawn up an alternative structure for the program but I'll post what I've been working on.

I don't think this structure gives us enough decoupling between the different parts. I think we should work towards a completely separate client and server and also between the data (model), view and control.

This is a rough diagram showing the structure I'm thinking of:

Code: Select all

Game
|-> Server (only part able to change data)
|   |-> AI
|   \-> Model
|       |-> Map
|       |   |-> Tiles
|       |   |-> ?
|       |   \-> ?
|       \-> Data
|           |-> Track
|           |-> Vehicles
|           \-> ?
|-> Client
|   |-> Control
|   |   |-> FrameRateController
|   |   |-> SoundController
|   |   |-> GamePainterController (controls the paint cycle)
|   |   |-> TimeController
|   |   \-> InputController
|   \-> View
|       |-> GamePainter
|       |-> VehiclePainter
|       |-> TrackPainter
|       \-? etc.
\-> Network
This doesn't show where interfaces are used because that would make it very complex. I'm working on a UML class diagram to show the full structure and interaction between the parts and post that when it's ready.

Posted: 25 Sep 2005 22:46
by uzurpator
DominionSpy wrote:I don't think this structure gives us enough decoupling between the different parts. I think we should work towards a completely separate client and server and also between the data (model), view and control.
Well. One thing that comes to mind here...

Maybe we should develop two applications altogather - TEserver and TEclient - split them into two executables and communicate using solely TCP/IP loopback on a local machine. This way we could skip the network class - and get quasi multithreading. And for "free" we get network problems resolved.

What needs to be resolved here is synchronisation and data protocol between client and server. I'll have to give it a thought.

Posted: 26 Sep 2005 10:57
by Hellfire
DominionSpy wrote:

Code: Select all

Game
|-> Server (only part able to change data)
|   |-> AI
|   \-> Model
...
A long time ago, we thought that it might be worthwile to consider the AI to be a client playing the game too. This will have some advantages:
  • To the game, all players are the same
  • AI is completely transparent to the game core.
  • In multiplayer games, the AI can take over if a player disconnects.
  • In multiplayer games, a player can connect to a game and take the place of one of the AI players.

Posted: 26 Sep 2005 13:54
by DominionSpy
Hellfire wrote:
DominionSpy wrote:

Code: Select all

Game
|-> Server (only part able to change data)
|   |-> AI
|   \-> Model
...
A long time ago, we thought that it might be worthwile to consider the AI to be a client playing the game too. This will have some advantages:
  • To the game, all players are the same
  • AI is completely transparent to the game core.
  • In multiplayer games, the AI can take over if a player disconnects.
  • In multiplayer games, a player can connect to a game and take the place of one of the AI players.
I was actually meaning vehicle AI (such as routing), rather than opponent AI, but you're right, it should have been put with the client rather than the server.

Posted: 26 Sep 2005 20:45
by Grunt
Anything under "Model" should be available to both the client and the server; although only the server can actually modify that information, the client will still need to have it in order to be able to render the player's view and be able to accurately represent what the client can do.

I am leaning towards writing a completely separate client and server whereupon a singleplayer game is one where the only human player on the server is the client; this solves the issues of having to deal with a separate single player component.

Posted: 26 Sep 2005 23:58
by fujitsu
Grunt wrote:Anything under "Model" should be available to both the client and the server; although only the server can actually modify that information, the client will still need to have it in order to be able to render the player's view and be able to accurately represent what the client can do.

I am leaning towards writing a completely separate client and server whereupon a singleplayer game is one where the only human player on the server is the client; this solves the issues of having to deal with a separate single player component.
Yeah, there should be no distinction between a single-player game and a multiplayer one. It will make things easier for all of the coders, and it will mean that the AI can be used identically in multiplayer and single-player without having to write slightly different interfaces. It will be much easier, in the long run, to have a single-player game being a multiplayer game without the multiple players.

William.

Posted: 27 Sep 2005 18:02
by uzurpator
Grunt wrote:Anything under "Model" should be available to both the client and the server; although only the server can actually modify that information, the client will still need to have it in order to be able to render the player's view and be able to accurately represent what the client can do.
No no no no.

The (local or not) client cannot access server only data. We are talking about hundreads of megabytes of information. This stunt might work for local client, but via internet it is compleatly unacceptable. For this sole reason we need to start at the stiff bandwidith constraints from scratch.

The client only recieves snapshots (small packages) of data from the server - and sends requests to the server for data. Nothing more.

GUI, drawing and collision detection however are client only occurances.

In the simplest model client asks:

"Give me a leaf of data: 20x20 tiles starting at 482x345"
or
"Give me info on train 282"

and server responds accordingly. What we need to make sure that the data stream will not exceed ~10 kb/s and that client/server will not go async.
I am leaning towards writing a completely separate client and server whereupon a singleplayer game is one where the only human player on the server is the client; this solves the issues of having to deal with a separate single player component.
Hm - yes - but should we develop:

1 application with both client and server embedded?
2 apps communicating via loopback on local machine?

DominionSpy:

Then the

Code: Select all

AI
class is not needed. You will be able to keep the - say train movement routines within

Code: Select all

Train
class - which makes Model class unnecessary.

Code: Select all

Game
|-> Server (only part able to change data)
|      |-> Map
|      |   |-> Tiles
|      |   |-> Vehicles
|      |   |    |-> Train
|      |   |    \-> ?
|      |   \-> ?
|      \-> Data
|          |-> Track
|          |-> Vehicles
|          \-> ?
.
.
.

Posted: 27 Sep 2005 21:32
by fujitsu
uzurpator wrote:
Grunt wrote:Anything under "Model" should be available to both the client and the server; although only the server can actually modify that information, the client will still need to have it in order to be able to render the player's view and be able to accurately represent what the client can do.
No no no no.

The (local or not) client cannot access server only data. We are talking about hundreads of megabytes of information. This stunt might work for local client, but via internet it is compleatly unacceptable. For this sole reason we need to start at the stiff bandwidith constraints from scratch.
I was hoping he wasn't saying that the client shouldn't have access to the entire map data... I agree with you uzurpator, it would be massive...
... wrote: The client only recieves snapshots (small packages) of data from the server - and sends requests to the server for data. Nothing more.

GUI, drawing and collision detection however are client only occurances.
Collision detection is client!!?? What collision detection do you mean?
... wrote: In the simplest model client asks:

"Give me a leaf of data: 20x20 tiles starting at 482x345"
or
"Give me info on train 282"

and server responds accordingly. What we need to make sure that the data stream will not exceed ~10 kb/s and that client/server will not go async.
Are you talking kilobytes, or kilobits? Whatever you were talking about, we shouldn't need to transfer much data to the client. We simply need to give it details about the vertices, terrain, etc of the tile, and anything that is on the tile, along with an ID for reference with the server. The client doesn't need all of the data about a tile that a server does, as the client doesn't do pathfinding or anything like that. It doesn't need all of the data about a station, because it will request it if and when it needs it, which it may not need to do very often at all.

I also think that the bandwidth could be limited by just transfering what is needed, say by giving no data for a tile that hasn't changed since the last request. This would take the bandwidth down massively, and the client would only need the data for the current viewport.

The only problem with this approach (that I can see), comes up when we notice that we really have to have a minimap. This means that the data for all of the tiles is going to have to be transferred to the client. We could always have the server do some of the minimap generation, or we could have a tiny amount of data for each tile retrieved, just enough to display the minimap. Otherwise, opening the minimap would create...............................lag.
... wrote:
I am leaning towards writing a completely separate client and server whereupon a singleplayer game is one where the only human player on the server is the client; this solves the issues of having to deal with a separate single player component.
Hm - yes - but should we develop:

1 application with both client and server embedded?
2 apps communicating via loopback on local machine?
I think two apps, or we are going to have problems with having to keep the multi- and single-player code working, and if we have seperate methods for each, we will have to test both multi- and single-player, whereas if we go with the method that has been accepted for most of this topic, we really only have to thoroughly test one method, for the other is effectively the same.
... wrote: DominionSpy:

Then the

Code: Select all

AI
class is not needed. You will be able to keep the - say train movement routines within

Code: Select all

Train
class - which makes Model class unnecessary.

Code: Select all

Game
|-> Server (only part able to change data)
|      |-> Map
|      |   |-> Tiles
|      |   |-> Vehicles
|      |   |    |-> Train
|      |   |    \-> ?
|      |   \-> ?
|      \-> Data
|          |-> Track
|          |-> Vehicles
|          \-> ?
.
.
.
Well, I think that the AI should probably be integrated within the vehicles.

William.

Posted: 27 Sep 2005 22:09
by uzurpator
fujitsu wrote:Collision detection is client!!?? What collision detection do you mean?
GUI vs Game World. Obviously the server does not know where the windows are (and why should it know?) - so the client must scan what was clicked and when.

Collisions between game objects are obviously server side occurances.

Sorry for being ambigous.
Are you talking kilobytes, or kilobits?
Kilobytes. 80 kbps. Maybe 120 kbps or so.
Whatever you were talking about, we shouldn't need to transfer much data to the client. We simply need to give it details about the vertices, terrain, etc of the tile, and anything that is on the tile, along with an ID for reference with the server.
say we send 20x20 patch (about one screen)

21x21 vertex height * short int = 882 bytes
21x21 vertex data (indexed) * byte = 441 bytes

so you just sent 1 kB of data. Say we compress = with 50% ratio = 600 bytes = 662 bytes. And you didn't even started to send tile info. With 20 snapshots/second you already ate whole bandwidth avalible to you...

This actually gives a reason to give the client access to server data on a local host. To avoid sending millions of vertices we need to give the server and client access to geometry data - and doing that without client access on local machine would double memory requirements.

Toughie.
The client doesn't need all of the data about a tile that a server does, as the client doesn't do pathfinding or anything like that. It doesn't need all of the data about a station, because it will request it if and when it needs it, which it may not need to do very often at all.
Obviously - but as you see - the client at least needs data on geometry - to preserve bandwidth.

Also - the protocol needs to be asynchronous - ie - we send the client only the data that is neede for the viewport to display correctly. But - we need to make sure that we don't send much raw data, but mostly indices (like: train 32 broke, or train 36 moved 1 unit forward)
I also think that the bandwidth could be limited by just transfering what is needed, say by giving no data for a tile that hasn't changed since the last request. This would take the bandwidth down massively, and the client would only need the data for the current viewport.
Hmm - yes - I see we are on the same page :)
The only problem with this approach (that I can see), comes up when we notice that we really have to have a minimap. This means that the data for all of the tiles is going to have to be transferred to the client. We could always have the server do some of the minimap generation, or we could have a tiny amount of data for each tile retrieved, just enough to display the minimap. Otherwise, opening the minimap would create...............................lag.
Indeed. But we can keep the last minimap the client requested and just send an update to it. A diff would suffice.
I think two apps, or we are going to have problems with having to keep the multi- and single-player code working, and if we have seperate methods for each, we will have to test both multi- and single-player, whereas if we go with the method that has been accepted for most of this topic, we really only have to thoroughly test one method, for the other is effectively the same.
Yes - I also lean toward that solution. The problems however persist with local client and data redundancy.

Posted: 28 Sep 2005 09:43
by fujitsu
I would like to quote most of this, but it would get incredibly long! So...
uzurpator wrote:
fujitsu wrote:Collision detection is client!!?? What collision detection do you mean?
GUI vs Game World. Obviously the server does not know where the windows are (and why should it know?) - so the client must scan what was clicked and when.
Ahh, good. I don't really count that as collision detection, but I guess it could be called that. Thanks for clearing that up.
... wrote:
Are you talking kilobytes, or kilobits?
Kilobytes. 80 kbps. Maybe 120 kbps or so.
Whatever you were talking about, we shouldn't need to transfer much data to the client. We simply need to give it details about the vertices, terrain, etc of the tile, and anything that is on the tile, along with an ID for reference with the server.
say we send 20x20 patch (about one screen)

21x21 vertex height * short int = 882 bytes
21x21 vertex data (indexed) * byte = 441 bytes

so you just sent 1 kB of data. Say we compress = with 50% ratio = 600 bytes = 662 bytes. And you didn't even started to send tile info. With 20 snapshots/second you already ate whole bandwidth avalible to you...

This actually gives a reason to give the client access to server data on a local host. To avoid sending millions of vertices we need to give the server and client access to geometry data - and doing that without client access on local machine would double memory requirements.

Toughie.
Very true, we need to work this out somehow... But why 20 whole snapshots per second? Couldn't we do what you specify below for the minimap? And wouldn't many of the tiles be completely empty? These are some questions that need to be answered before we work out what on earth we are going to do about the data transfer...
... wrote:
I also think that the bandwidth could be limited by just transfering what is needed, say by giving no data for a tile that hasn't changed since the last request. This would take the bandwidth down massively, and the client would only need the data for the current viewport.
Hmm - yes - I see we are on the same page :)
I must say I don't get what you mean...
... wrote: Indeed. But we can keep the last minimap the client requested and just send an update to it. A diff would suffice.
Well, not quite a diff, not particularly good for things like that :P... But yes, that idea would be good.
... wrote: Yes - I also lean toward that solution. The problems however persist with local client and data redundancy.
Yeah, the two program idea is good, but it will have to be thought about a lot!

---------------------

So I ended up quoting most of it, but meh.

William.

Posted: 28 Sep 2005 13:47
by Grunt
uzurpator wrote:
Grunt wrote:Anything under "Model" should be available to both the client and the server; although only the server can actually modify that information, the client will still need to have it in order to be able to render the player's view and be able to accurately represent what the client can do.
No no no no.

The (local or not) client cannot access server only data. We are talking about hundreads of megabytes of information. This stunt might work for local client, but via internet it is compleatly unacceptable. For this sole reason we need to start at the stiff bandwidith constraints from scratch.

The client only recieves snapshots (small packages) of data from the server - and sends requests to the server for data. Nothing more.

GUI, drawing and collision detection however are client only occurances.

In the simplest model client asks:

"Give me a leaf of data: 20x20 tiles starting at 482x345"
or
"Give me info on train 282"

and server responds accordingly. What we need to make sure that the data stream will not exceed ~10 kb/s and that client/server will not go async.
You misunderstand my point - I'm trying to get across that the client will still need access to the data structures in order to make sense of the data the server sends to the client. Of course the client doesn't need to know everything about everything in the game world - probably just what it can see at this given point in time or otherwise can get information about (e.g. if you have a list of trains open).

uzurpator wrote: Hm - yes - but should we develop:

1 application with both client and server embedded?
2 apps communicating via loopback on local machine?
It's possible to have the all-in-one application spawn two threads which would then communicate via loopback - this seems to me as though it would be a good solution.

Posted: 28 Sep 2005 22:08
by fujitsu
Grunt wrote:
uzurpator wrote: Hm - yes - but should we develop:

1 application with both client and server embedded?
2 apps communicating via loopback on local machine?
It's possible to have the all-in-one application spawn two threads which would then communicate via loopback - this seems to me as though it would be a good solution.
The thread-spawning thing was exactly what I had in mind for single player.

William.

Posted: 29 Sep 2005 19:17
by uzurpator
Well - if it is made as two seperate apps then we don't need to worry about the thread spawning stuff -> less work. Also it would make it easier to distribute two files - eg upated client or updated server.

Good - however this topic is about game structure - namely - class tree. We should concentrate on the classes essential for the engine to work (ie - generate terrain, make working interface etc).

DominionSpy: have you finished your proposition?

Posted: 29 Sep 2005 23:36
by DominionSpy
uzurpator wrote:DominionSpy: have you finished your proposition?
No, I was doing it in Paint Shop Pro and it crashed half-way through :(.
I'm going away for the weekend, so I won't be able to work on it till then.

The basic idea is that there is a hierarchy of interfaces in the model, like this:

Code: Select all

IAccessor
    ^
    |
    |
IMutator
    ^
    |
    |
 CObject
The IAccessor interface only has methods to retrieve information. This is the interface for the View.
The IMutator interface has methods to change the data. This is the interface for the Control.
The CObject class then implements the IMutator interface (which extends the IAccessor interface).

The hierarchy in the model is then extended by having a superclass CModel for all the models and extra interfaces for common behaviours such as say IMovable for all objects that are not stationary.

Posted: 16 Oct 2005 21:26
by uzurpator
Ok - I think we should really disregard this thread and build the tree on the fly.

For now it would constitute something like this:

Code: Select all

God             Satan
 |               |
 |-Server        |-Client
 \-Network       \-Network
God - main program that will boot server and network threads and kill them when necessary
Satan - main program that will boot client and network threadsa and kill them when necessary

Server - for now this class has a main loop that does all the magic, in times to come it will also control all the data the game processes
Network - classes that will talk to each other to remain in touch :)
Client - class doing the displaying of client data

For now we should concentrate on making both Server and Client exchange data

BTW - I name classes by their level in the tree:

God
Seraph
Cherub
Angel
Saint
Blessed

Satan
Daemon
Devil
Imp
Sinner
Damned

Call me creative.

Posted: 24 Oct 2005 10:25
by DominionSpy
uzurpator wrote:Ok - I think we should really disregard this thread and build the tree on the fly.

For now it would constitute something like this:

Code: Select all

God             Satan
 |               |
 |-Server        |-Client
 \-Network       \-Network
I don't see what's wrong with this:

Code: Select all

Server       Client
 |             |
 |-Data        |-Renderer
 \-Network     \-Nework
uzurpator wrote:Call me creative.
I'd rather call you insane :P. Why do we need to name the classes by thier level any way?

Posted: 24 Oct 2005 12:20
by uzurpator
DominionSpy wrote:

Code: Select all

Server       Client
 |             |
 |-Data        |-Renderer
 \-Network     \-Nework
Nothing wrong with them. Altho in this naming client and server are not classes. They are basicly (note - I omitted network<->server communication data structures - altho we can embed them in the classes)

god.cpp

Code: Select all

#include "server.h"

int main(void)
{
server *server;
server = new server(params);
network *network;
network = new network(params);
server->StartThread(params);
network->StartThread(params);
return 0;
}
So I think that naming them as they were classes is kind of a stretch. Altho come to think of it - this structure would probably be better :)

Code: Select all

BootProg       BootProg
 \-Server       \-Client
   |               |
   |-Data          |-Renderer
   \-Network       \-Nework
bootprog.cpp

Code: Select all

#include "server.h"

int main(void)
{
server *server;
server = new server(params);
server->StartThread(params);
return 0;
}
uzurpator wrote:Call me creative.
I'd rather call you insane :P. Why do we need to name the classes by thier level any way?
I didn't say _we_ I said I use those. They are just to explain to the others the weird terminology I might use :) Disregard them :D

And yes - I am insane :D