Warning, /games/libkdegames/src/private/kgame/DESIGN is written in an unsupported language. File is not indexed.
0001 This document tries to describe the design of KGame - the KDE multiplayer 0002 library. 0003 This document has been written by: 0004 Andreas Beckermann <b_mann@gmx.de> 0005 M. Heni <kde at heni-online.de> 0006 Burkhard Lehner <Burkhard.Lehner@gmx.de> 0007 0008 This document is published under the terms of the GNU FDL 0009 0010 !!! 0011 Note that this is the initial version of this document and has not yet been 0012 approved by all core developers (and is far from being complete) 0013 AB: please remove this comments as soon as all KGame hackers have read the 0014 document 0015 !!! 0016 0017 Please refer the API documentation of every KGame class if you want up tp date 0018 information. 0019 0020 0021 0. Contents 0022 ----------- 0023 0024 1. DEFINITIONS 0025 1.1 Message Server 0026 1.2 Client or Message Client 0027 1.3 Master 0028 1.4 Admin 0029 1.5 Server 0030 1.6 Player 0031 0032 2. Game Negotiation (M.Heni 20.05.2001) 0033 0034 AB: 3.x is obsolete! 0035 3. Game Properties (Andreas Beckermann 28.07.2001) ( not yet completed ) 0036 3.1 Using KGameProperty 0037 3.2 Custom Classes 0038 3.3 Concepts 0039 0040 4. KGameIO (Andreas Beckermann 10.08.2001) 0041 0042 --------------------------------------------------------------------- 0043 1. DEFINITIONS 0044 -------------- 0045 0046 First we have to clear some words. The main expressions used in KGame which 0047 need a definition are 0048 0049 1.1 Message Server 0050 1.2 Client or Message Client 0051 1.3 Master 0052 1.4 Admin 0053 1.5 Server 0054 1.6 Player 0055 0056 The most important and confusing ones are Master, Admin and Server. We make 0057 quite big differences between those inside KGame. 0058 0059 1.1 Message Server: 0060 ------------------- 0061 A game has always exactly one object of this class, for local games as well as 0062 for network games. For network games, this object can be on one of the users 0063 processes (usually inside KGame), or it can also be on an independent computer, 0064 that has no idea about what game is played on it. 0065 0066 A KMessageClient object can connect to it. Its main purpose is transmitting 0067 messages between KMessageClient objects. 0068 0069 The Message Server is the main communication object. It is represented by the 0070 class KMessageServer. Note that there is also a "Master" and a "Server" which 0071 both differ heavily from the Message Server! 0072 0073 1.2 Client, Message Client: 0074 --------------------------- 0075 Each process that wants to take part in the game must have a 0076 KMessageClient object, that is connected to the Message Server. KGame creates 0077 this object and connects it to the Messager Server, so that you usually don't 0078 need to create these of your own. Even in a local game (no network) there 0079 must be a message server and one message client connected to it. This is usually 0080 done by the KGame object itself. 0081 0082 Each message client has a unique ID number (a positive integer value, not zero). 0083 The KMessageClient object, which does the communication with the Message Server 0084 is called "Message Client" and to simplify the use we call any KGame object (or 0085 even the game process) that is connected to a game (i.e. even the Master) just 0086 "Client". 0087 0088 The main purpose of a Client is to connect to a Master (i.e. to a game) and to 0089 communicate with it. A client has always a KGame object. 0090 0091 1.3 Master: 0092 ----------- 0093 The process that contains the Message Server is called "Master". In any local 0094 game this is the game process. The Message Server is started by KGame using 0095 KGame::setMaster(true) which is automatically done on startup. The Message 0096 Server is deleted automatically as soon as you connect to another Master. 0097 So in most cases there is exactly one KGame object / Client which is Master. But 0098 in one case there can be no KGame object / Client that is Master - if the 0099 Message Server is started as an own process. This "Message-Server-only" process 0100 is called "Master" then, although there is no KGame object which is Master. See 0101 also the definition of Admin! 0102 0103 1.4 Admin: 0104 ---------- 0105 One (and only one) of the Clients is the Admin. He can configure the Message 0106 Server and the game in general in several ways. He can limit the maximum number 0107 of connected message clients and can drop the connection to some other clients, 0108 as well as he can configure game specific settings (like max/min players, start 0109 money, ...). The Admin also initializes newly connected Clients. If the Admin 0110 himself disconnects, another Client becomes Admin (The Admin can himself elect 0111 some other Client to become Admin. He himself loses that Admin status then). 0112 An Admin is *always* a KGame object. The Admin is usually the same as the Master, 0113 but if the Master is an own process (i.e. the Message Server has been started 0114 outside KGame) then Master and Admin differ. An Admin *must* be a KGame object 0115 while the Master doesn't have to be. 0116 0117 1.5 Server: 0118 ----------- 0119 The definition of Server differs quite much from the definition of Master. 0120 A Master just accepts connections and forwards messages. The Server on the other 0121 side checks these messages, calculates results and sends the results to the 0122 Clients. That means the Server does all game calculations and doesn't directly 0123 forward the messages from one Clients to all other Clients. 0124 KGamer makes it possible to write multiplayer games even without a Server. All 0125 Clients just send their moves to the Master which forwards them to all Clients. 0126 Now all Clients calculate the result. 0127 E.g. in a poker game a player selects two of five cards to be exchanges and 0128 clicks on "draw" then the client sends the message "Exchange Card-1 and Card-2" 0129 to the Master. A no-Server solution forwards this to all Clients, and these 0130 Clients exchange the cards of the player. Note that in a no-Server solution 0131 (you can also see it as a "every-Client-is-a-Server solution") all Clients must 0132 have the same random seed and must be of the same version, i.e. the result must 0133 be the same on all Clients. 0134 In a Server-Solution on the other hand the Master forwards the Message 0135 ("Exchange Card-1 and Card-2") to the Server only. This Server now calculates 0136 the result, and sends the new cards back to the Client. 0137 Both concepts have advantages and disadvantages. It is on you - the game 0138 developer - to decide which way is better for you. 0139 E.g. the Server-Solution makes it easier for you to write games. The version 0140 must not necessarily be the same, you have one central computer which does the 0141 calculations. The No-Server-Solution on the other hand decreases network 0142 traffic as the Clients just send their moves and all Clients can calculate the 0143 reactions. I'm sure there are a lot of advantages/disadvantages more for both 0144 concepts. 0145 0146 1.6 Player: 0147 ----------- 0148 A KPlayer object is always connected to a KGame object and represents a 0149 player that participates the game. In a network game, every KPlayer object is 0150 duplicated on every other KGame object connected to the message server with 0151 virtual KPlayer objects. So at every time in the game, every KGame object has 0152 the same number of KPlayer objects. 0153 0154 0155 2. Game negotiation 0156 ------------------- 0157 Upon connection of a client the admin and the client try to negotiate 0158 the game setup. Basically this means the game of the admin is transferred 0159 (saved) on the client. However, the client's players are added to the game 0160 as far as possible. If the addition of the client's players would add more 0161 players than allowed some players are inactivated. Which players are 0162 inactivated depends on their networkPriority(). This procedure allows 0163 easy replacement of players in a constant number game (e.g. chess). If 0164 this feature is of no interest simply keep the priorities equal (all 0) 0165 and the client will only add only players if the number of players is 0166 less or equal the maximum player number. 0167 0168 The following is the negotiation procedure as started by the connection 0169 of a client. It is initiated in the negotiateNetworkGame() virtual function 0170 of KGame: 0171 0172 admin: client: 0173 ------------ ------------ 0174 IdSetupGame 0175 QINT16 Library 0176 Version 0177 QINT32 Application 0178 cookie 0179 IdSetupGameContinue; 0180 QValueList<int> player id's 0181 QValueList<int> network priority's 0182 0183 IdGameLoad 0184 all game data 0185 0186 IdGameReactivate 0187 QValueList<int> id's 0188 0189 IdSyncRandom 0190 int randomseed 0191 0192 0193 3. Game Properties 0194 ------------------ 0195 A very hard task in a network game is consistency. You have to achieve that all 0196 properties of the game and of all players have the same value on all clients 0197 every time. This is because 0198 a) the user might be confused if he sees "Player has $0" on client A but 0199 "Player has $10" on client B and 0200 b) Often game handling depends on those values, e.g. if the example above 0201 happens the computer might quit the game for the Player on client A because 0202 he/she doesn't have enough money. But the game continues on client B. 0203 Another not that easy task is the network protocol itself. You have to write 0204 several send() and receive() functions which apply changed values of properties 0205 to the local property. 0206 0207 KGameProperty is designed to do all of this for you. KGameProperty is 0208 implemented as a template so you can use it theoretically for every type of data 0209 - even for your self defined classes. 0210 0211 0212 3.1 Using KGameProperty 0213 ----------------------- 0214 It is basically very easy to use a KGameProperty. You first create your own 0215 class containing the property, e.g: 0216 class MyGame : public KGame 0217 { 0218 [...] 0219 protected: 0220 KGamePropertyInt money; 0221 KGamePropertyQString name; 0222 KGameProperty<AntotherClass> myProperty; 0223 }; 0224 KGamePropertyInt is just a typedef for KGameProperty<int> - just like 0225 KGamePropertyQString. Now you need to register the properties in the constructor 0226 of the class to the KGamePropertyHandler: 0227 MyGame::MyGame() : KGame(myCookie) 0228 { 0229 money.registerData(KGamePropertyBase::IdUser+1, dataHandler(), "Money"); 0230 name.registerData(KGamePropertyBase::IdUser+2, this, "Name"); 0231 myProperty.registerData(KGamePropertyBase::IdUser+3, dataHandler(), "MyProperty"); 0232 } 0233 -> You need to specify a *unique* ID. This ID must be greater than 0234 KGamePropertyBase::IdUser. IDs below this are reserved for KGame. Probably this 0235 will be changed so that you cannot use IDs below IdUser in the future. Then you 0236 have to specify the dataHandler(). You can also use a KGame or KPlayer pointer. 0237 This will automatically use KGame::dataHandler() or KPlayer::dataHandler(). 0238 Finally you *can* provide a name for the property. 0239 Note that if you use pointers to create the properties dynamically they are 0240 *not* deleted automatically! You MUST delete them yourself! 0241 Now you can use the KGameProperty like every variable else. See also Section 0242 "3.3 Concepts" for restrictions in use. 0243 0244 3.2 Custom Classes 0245 ------------------ 0246 To make custom classes possible you have to implement several operators for your 0247 them: you need at least << and >> for QDataStream as well as "==" for your own 0248 class. To overload the "<<" you would e.g. do something like this: 0249 QDataStream& operator<<(QDataStream& stream, MyData& data) 0250 { 0251 int type = data.type; 0252 QString name = data.name; 0253 stream << type << name; 0254 return stream; 0255 } 0256 So you basically just have to split your class to several basic types and stream 0257 them. 0258 0259 3.3 Concepts 0260 ------------ 0261 You can use KGameProperty basically in two completely different ways. You can 0262 also use a mixture of both but this is not recommended. The default behaviour 0263 and therefore also the recommended is the "clean" way: 0264 a) Always Consistent. This means that a KGameProperty has always the same value 0265 on *every* client. This is achieved by using KGameProperty::send() whenever you 0266 want to change the value using "=". You can still use changeValue() or 0267 setLocal() but send() will be the default. If you use send() then the value of 0268 the property does *NOT* change immediately. It is just sent to the 0269 KMessageServer which forwards the value to all clients. As soon as the new value 0270 is received from the message server the KGamePropertyHandler (a collection class 0271 for KGameProperty) calls KGameProperty::load() and changes the value of the 0272 property. So the game first has to go into the event loop, where the message is 0273 received. This means to you that you cannot do this: 0274 myIntProperty = 10; 0275 int value = myIntProperty; 0276 As myIntPoperty still has the old value when "value = myIntProperty" is called. 0277 This might seem to be quite complex, but 0278 KGamePropertyHandler::signalPropertyChanged() is emitted whenever a new value is 0279 assigned so you can connect to this and work immediately with the new value. 0280 You gain the certainty that the value is the same on every client every time. 0281 That will safe you a lot of time debugging! 0282 Another way is the "dirty" way: 0283 b) Not Always Consistent. Sometimes you really *want* to do something like 0284 myIntProperty = 10; 0285 int value = myIntProperty; 0286 but this is not possible with the default behaviour. If you call 0287 KGameProperty::setAlwaysConsistent(false) in the constructor (right after 0288 registerData()) you get another behaviour. "=" means changeValue() now. 0289 changeValue() also uses send() to change the value but additionally calls 0290 setLocal() to create a local copy of the property. This copy now has the value 0291 you supplied with "=" and is deleted again as soon as any value from the network 0292 is received. 0293 0294 4. KGameIO 0295 ---------- 0296 The class KGameIO is used to let the players communicate with the server. You 0297 can plug as many KGameIO objects into a player as you want, e.g. you can plug a 0298 KGameMouseIO and a KGameKeyIO into a player so that you can control the player 0299 with the mouse and the keyboard - e.g. in a breakout game. 0300 You can probably see the advantage: as most of the control stuff is common in a 0301 lot of games you can use the same IO class in many different games with very 0302 small adjustments. 0303 You could also put all the IO stuff directly into your KPlayer object, like 0304 sendBet(int money) for a poker game. But there is a major disadvantage and I'm 0305 very sure you don't want to use a KPlayer object for your IO stuff as soon as 0306 you know which disadvantage: 0307 KGameIO is designed to be able to switch between different IOs "on the fly". So 0308 you might have a KGamePlayerIO, derived from KGameIO, for your game. But now 0309 this player (who "owns"/uses the KGamePlayerIO) leaves the game (e.g. because he 0310 was a remote player). So now the game would be over for every player as one 0311 player is now out of order. But with KGameIO you can just let any of the 0312 remaining clients create a KGameComputerIO and plug this into the player. So the 0313 player now is controlled by the computer and the game can continue. 0314 0315 Think about it! You don't have to care about removing players when a player 0316 leaves as you can just replace it! The same works the other way round: imagine a 0317 game with 10 player (e.g. 5 human and 5 computer players) that has already 0318 started. You cannot add any further players without restarting. So if there are 0319 any additional player you can just call KPlayer::removeGameIO() which removes 0320 the IO of a computer player and then call KPlayer::addGameIO() for the same 0321 player which adds a GameIO for new human player. That's all! 0322 0323 To achieve this you just have to make sure that you make *all* of your IO 0324 operations through a KGameIO! So instead of using MyPlayer::sendBet(int money) 0325 you should use something like MyIO::sendBet(). The amount of money would 0326 probably be calculated by the game IO itself. 0327 0328 0329