summaryrefslogtreecommitdiffstats
path: root/libkdegames/kgame
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitc90c389a8a8d9d8661e9772ec4144c5cf2039f23 (patch)
tree6d8391395bce9eaea4ad78958617edb20c6a7573 /libkdegames/kgame
downloadtdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.tar.gz
tdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegames@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'libkdegames/kgame')
-rw-r--r--libkdegames/kgame/COMPAT55
-rw-r--r--libkdegames/kgame/DESIGN407
-rw-r--r--libkdegames/kgame/Makefile.am29
-rw-r--r--libkdegames/kgame/README.LIB12
-rw-r--r--libkdegames/kgame/TODO41
-rw-r--r--libkdegames/kgame/dialogs/Makefile.am17
-rw-r--r--libkdegames/kgame/dialogs/kgameconnectdialog.cpp278
-rw-r--r--libkdegames/kgame/dialogs/kgameconnectdialog.h169
-rw-r--r--libkdegames/kgame/dialogs/kgamedebugdialog.cpp548
-rw-r--r--libkdegames/kgame/dialogs/kgamedebugdialog.h149
-rw-r--r--libkdegames/kgame/dialogs/kgamedialog.cpp347
-rw-r--r--libkdegames/kgame/dialogs/kgamedialog.h320
-rw-r--r--libkdegames/kgame/dialogs/kgamedialogconfig.cpp773
-rw-r--r--libkdegames/kgame/dialogs/kgamedialogconfig.h362
-rw-r--r--libkdegames/kgame/dialogs/kgameerrordialog.cpp129
-rw-r--r--libkdegames/kgame/dialogs/kgameerrordialog.h113
-rw-r--r--libkdegames/kgame/kgame.cpp1475
-rw-r--r--libkdegames/kgame/kgame.h932
-rw-r--r--libkdegames/kgame/kgamechat.cpp341
-rw-r--r--libkdegames/kgame/kgamechat.h223
-rw-r--r--libkdegames/kgame/kgameerror.cpp80
-rw-r--r--libkdegames/kgame/kgameerror.h59
-rw-r--r--libkdegames/kgame/kgameio.cpp539
-rw-r--r--libkdegames/kgame/kgameio.h566
-rw-r--r--libkdegames/kgame/kgamemessage.cpp156
-rw-r--r--libkdegames/kgame/kgamemessage.h173
-rw-r--r--libkdegames/kgame/kgamenetwork.cpp516
-rw-r--r--libkdegames/kgame/kgamenetwork.h431
-rw-r--r--libkdegames/kgame/kgameprocess.cpp158
-rw-r--r--libkdegames/kgame/kgameprocess.h242
-rw-r--r--libkdegames/kgame/kgameproperty.cpp211
-rw-r--r--libkdegames/kgame/kgameproperty.h848
-rw-r--r--libkdegames/kgame/kgamepropertyarray.h309
-rw-r--r--libkdegames/kgame/kgamepropertyhandler.cpp407
-rw-r--r--libkdegames/kgame/kgamepropertyhandler.h353
-rw-r--r--libkdegames/kgame/kgamepropertylist.h258
-rw-r--r--libkdegames/kgame/kgamesequence.cpp125
-rw-r--r--libkdegames/kgame/kgamesequence.h87
-rw-r--r--libkdegames/kgame/kgameversion.h54
-rw-r--r--libkdegames/kgame/kmessageclient.cpp373
-rw-r--r--libkdegames/kgame/kmessageclient.h422
-rw-r--r--libkdegames/kgame/kmessageio.cpp482
-rw-r--r--libkdegames/kgame/kmessageio.h416
-rw-r--r--libkdegames/kgame/kmessageserver.cpp515
-rw-r--r--libkdegames/kgame/kmessageserver.h492
-rw-r--r--libkdegames/kgame/kmessageserver.pngbin0 -> 7791 bytes
-rw-r--r--libkdegames/kgame/kplayer.cpp446
-rw-r--r--libkdegames/kgame/kplayer.h471
-rw-r--r--libkdegames/kgame/libkdegames.html187
-rw-r--r--libkdegames/kgame/messages.txt93
-rw-r--r--libkdegames/kgame/scenario0.pngbin0 -> 2413 bytes
-rw-r--r--libkdegames/kgame/scenario1.pngbin0 -> 8074 bytes
-rw-r--r--libkdegames/kgame/scenario2.pngbin0 -> 7398 bytes
53 files changed, 16189 insertions, 0 deletions
diff --git a/libkdegames/kgame/COMPAT b/libkdegames/kgame/COMPAT
new file mode 100644
index 00000000..146d3a88
--- /dev/null
+++ b/libkdegames/kgame/COMPAT
@@ -0,0 +1,55 @@
+06.09.2001: replace the signal signalCreatePlayer by the virtual function
+ createPlayer. It has the same arguments but the return value
+ is the new player
+06.09.2001: the KGameConfig dialog changes the parameter initConfigs from bool
+ to long. Use the ConfigOptions to specify what options you want
+ to have enabled. Default is all
+06.09.2001: some int->Q_UINT32 in sender, receiver and player parameters. maybe
+ more will follow.
+06.09.2001: KGameIO::signalPrepareMove(..., bool&) ->
+ KGameIO::signalPrepareMove(..., bool*): don't know why this was
+ necessary but it didn't work anymore...
+16.09.2001: KGamePropertyHandler uses bool* for the sent parameter now. This is
+ because QT3 obviously doesn't honor referneces in signals/slots.
+ This might even be a QT bug. Bad situation - we use references
+ everywhere in KGame... hope nothing else is affecterd by this
+ problem (signalPrepareMove was fixed already by me)
+18.09.2001: bool* for Key/Mouseevents and IOAdded in kgameio.h too
+19.09.2001: Kgame:nextPlayer retunrs the KPlayer *nextplayer instead of bool
+19.09.2001: gameOver() renamed to checkGameOver() !!!!!
+18.09.2001: Question: Should the signal signalPlayerInput(QDataStream &,KPlayer *))
+ be made a virtual function?
+ MH: This is done now. As this is a central function your programs will
+ not run anymore. Fix: rename your slot which is connected to the above
+ signal to playerInput() and return TRUE in it. This will make it 100%
+ compatible to the old version. I think this chagne is necessary especially
+ as a signal is of no use here as you cannot read twice from the same stream.
+ Therefore there can be only one function processing the input. If you really
+ need a signal, you can of course simply emit it in the overwritten playerInput
+ function
+20.09.2001 playerInputFinished(void->KPlayer *)
+--------------------- KGAME_ALPHA_1 ---------------------
+06.10.2001 adding KGameNetwork::signalAdminStatusChanged - needed for
+ KGameDialog
+06.10.2001 KGame::loadGame() doesn't call setPolicy() anymore!
+08.10.2001 KGamePropertyList now honor policies! Use setPolicy(PolicyDirty) to
+ get the old behavior!
+ The behavior of KGamePropertyArray may have changed in this turn,
+ too!
+ The API stays the same.
+11.10.2001 KGameDialogGeneralConfig now doesn't provide setMin/maxPlayers()
+ anymore. The game should manage this internally. layout() is
+ obsolete as well
+18.10.2001 KPlayer::signalNetworkData contained QDataStream& instead of const
+ QByteArray& parameter (oops!). This is fixed now. All apps which
+ used this signal must be changed.
+18.10.2001 KGame::sendProperty(), KGame::sendPlayerProperty(),
+ KPlayer::sendProperty() and related functions contain a "int msgid"
+ parameter. This is the id() of the property handler. This parameter
+ enables us to easily add any number of property handler to a game
+ just by connecting it to existing send slots and call
+ processMessage() in slotNetworkData()
+03.11.2001 KPlayer::signalNetworkData now emits msgid-KGameMessage::IdUser just
+ like KGame::signalNetworkData does
+06.11.2001 KGameDialog has some small improvements - easier and IMHO better
+ constructor code. Most code should be compatible :-)
diff --git a/libkdegames/kgame/DESIGN b/libkdegames/kgame/DESIGN
new file mode 100644
index 00000000..b1c48146
--- /dev/null
+++ b/libkdegames/kgame/DESIGN
@@ -0,0 +1,407 @@
+This document tries to describe the design of KGame - the KDE multiplayer
+library.
+This document has been written by:
+ Andreas Beckermann <[email protected]>
+ M. Heni <[email protected]>
+ Burkhard Lehner <[email protected]>
+
+This document is published under the terms of the GNU FDL
+
+!!!
+Note that this is the initial version of this document and has not yet been
+aproved by all core developers (and is far from being complete)
+AB: please remove this comments as soon as all KGame hackers have read the
+document
+!!!
+
+Please refer the API documentation of every KGame class if you want up tp date
+information.
+
+
+0. Contents
+-----------
+
+1. DEFINITIONS
+1.1 Message Server
+1.2 Client or Message Client
+1.3 Master
+1.4 Admin
+1.5 Server
+1.6 Player
+
+2. Game Negotiation (M.Heni 20.05.2001)
+
+AB: 3.x is obsolete!
+3. Game Properties (Andreas Beckermann 28.07.2001) ( not yet completed )
+3.1 Using KGameProperty
+3.2 Custom Classes
+3.3 Concepts
+
+4. KGameIO (Andreas Beckermann 10.08.2001)
+
+5. Debugging (Andreas Beckermann 06.10.2001) TODO!
+5.1 KGameDebugDialog
+5.1.1 Debug KGame
+5.1.3 Debug Messages
+
+---------------------------------------------------------------------
+1. DEFINITIONS
+--------------
+
+First we have to clear some words. The main expressions used in KGame which
+need a definition are
+
+1.1 Message Server
+1.2 Client or Message Client
+1.3 Master
+1.4 Admin
+1.5 Server
+1.6 Player
+
+The most important and confusing ones are Master, Admin and Server. We make
+quite big differerences between those inside KGame.
+
+1.1 Message Server:
+-------------------
+A game has always exactly one object of this class, for local games as well as
+for network games. For network games, this object can be on one of the users
+processes (usually inside KGame), or it can also be on an independant computer,
+that has no idea about what game is played on it.
+
+A KMessageClient object can connect to it. It's main purpose is transmitting
+messages between KMessageClient objects.
+
+The Message Server is the main communication object. It is represented by the
+class KMessageServer. Note that there is also a "Master" and a "Server" which
+both differ heavily from the Message Server!
+
+1.2 Client, Message Client:
+---------------------------
+Each process that wants to take part in the game must have a
+KMessageClient object, that is connected to the Message Server. KGame creates
+this object and connects it to the Messager Server, so that you usually don't
+need to create these of your own. Even in a local game (no network) there
+must be a message server and one message client connected to it. This is usually
+done by the KGame object itself.
+
+Each message client has a unique ID number (a positive integer value, not zero).
+The KMessageClient object, which does the communication with the Message Server
+is called "Message Client" and to simplify the use we call any KGame object (or
+even the game process) that is connected to a game (i.e. even the Master) just
+"Client".
+
+The main purpose of a Client is to connect to a Master (i.e. to a game) and to
+communicate with it. A client has always a KGame object.
+
+1.3 Master:
+-----------
+The process that contains the Message Server is called "Master". In any local
+game this is the game process. The Message Server is started by KGame using
+KGame::setMaster(true) which is automatically done on startup. The Message
+Server is deleted automatically as soon as you connect to another Master.
+So in most cases there is exactly one KGame object / Client which is Master. But
+in one case there can be no KGame object / Client that is Master - if the
+Message Server is started as an own process. This "Message-Server-only" process
+is called "Master" then, although there is no KGame object which is Master. See
+also the definition of Admin!
+
+1.4 Admin:
+----------
+One (and only one) of the Clients is the Admin. He can configure the Message
+Server and the game in general in several ways. He can limit the maximum number
+of connected message clients and can drop the connection to some other clients,
+as well as he can configure game specific ssettings (like max/min players, start
+money, ...). The Admin also initializes newly connected Clients. If the Admin
+himself disconnects, another Client becomes Admin (The Admin can himself elect
+some other Client to become Admin. He himself loses that Admin status then).
+An Admin is *alway* a KGame object. The Admin is usually the same as the Master,
+but if the Master is an own process (i.e. the Message Server has been started
+outside KGame) then Master and Admin differ. An Admin *must* be a KGame object
+while the Master doesn't have to be.
+
+1.5 Server:
+-----------
+The definition of Server differs quite much from the definition of Master.
+A Master just accepts connections and forwards messages. The Server on the other
+side checks these messages, calculates results and sends the results to the
+Clients. That means the Server does all game calculations and doesn't directly
+forward the messages from one Clients to all other Clients.
+KGamer makes it possible to write multiplayer games even without a Server. All
+Clients just send their moves to the Master which forwards them to all Clients.
+Now all Clients calculate the result.
+E.g. in a poker game a player selects two of five cards to be exchanges and
+clicks on "draw" then the client sends the message "Exchange Card-1 and Card-2"
+to the Master. A no-Server solution forwards this to all Clients, and these
+Clients exchange the cards of the player. Note that in a no-Server solution
+(you can also see it as a "every-Client-is-a-Server solution") all Clients must
+have the same random seed and must be of the same version, i.e. the result must
+be the same on all Clients.
+In a Server-Solution on the other hand the Master forwards the Message
+("Exchange Card-1 and Card-2") to the Server only. This Server now calculates
+the result, and sends the new cards back to the Client.
+Both concepts have advantages and disadvantages. It is on you - the game
+developer - to decide which way is better for you.
+E.g. the Server-Solution makes it easier for you to write games. The version
+must not necessarily be the same, you have one central computer which does the
+calcultations. The No-Server-Solution on the other hand decreases network
+traffik as the Clients just send their moves and all Clients can calculate the
+reactions. I'm sure there are a lot of advantages/disadvantages more for both
+concepts.
+
+1.6 Player:
+-----------
+A KPlayer object is always connected to a KGame object and represents a
+player that participates the game. In a network game, every KPlayer object is
+duplicated on every other KGame object connected to the message server with
+virtual KPlayer objects. So at every time in the game, every KGame object has
+the same number of KPlayer objects.
+
+
+2. Game negotiation
+-------------------
+Upon connection of a client the admin and the client try to negotiate
+the game setup. Basically this means the game of the admin is transferred
+(saved) on the client. However, the client's players are added to the game
+as far as possible. If the addition of the client's players would add more
+players than allowed some players are inactivated. Which players are
+inactivated depends on their networkPriority(). This procedure allows
+easy replacement of players in a constant number game (e.g. chess). If
+this feature is of no interest simply keep the priorities equal (all 0)
+and the client will only add only players if the number of players is
+less or equal the maximum player number.
+
+The following is the negotiation procedure as started by the connection
+of a client. It is initiated in the negotiateNetworkGame() virtual function
+of KGame:
+
+admin: client:
+------------ ------------
+IdSetupGame
+ QINT16 Library
+ Version
+ QINT32 Application
+ cookie
+ IdSetupGameContinue;
+ QValueList<int> player id's
+ QValueList<int> network priority's
+
+IdGameLoad
+ all game data
+
+IdGameReactivate
+ QValueList<int> id's
+
+IdSyncRandom
+ int randomseed
+
+
+3. Game Properties
+------------------
+A very hard task in a network game is consistency. You have to achieve that all
+properties of the game and of all players have the same value on all clients
+every time. This is because
+a) the user might be confused if he sees "Player has $0" on client A but
+"Player has $10" on client B and
+b) Often game handling depends on those values, e.g. if the example above
+happens the computer might quit the game for the Player on client A because
+he/she doesn't have enough money. But the game continues on client B.
+Another not that easy task is the network protocol itself. You have to write
+several send() and receive() functions which apply changed values of properties
+to the local property.
+
+KGameProperty is designed to do all of this for you. KGameProperty is
+implemented as a template so you can use it theoretically for every type of data
+- even for your self defined classes.
+
+
+3.1 Using KGameProperty
+-----------------------
+It is basically very easy to use a KGameProperty. You first create your own
+class containing the property, e.g:
+class MyGame : public KGame
+{
+[...]
+protected:
+ KGamePropertyInt money;
+ KGamePropertyQString name;
+ KGameProperty<AntotherClass> myProperty;
+};
+KGamePropertyInt is just a typedef for KGameProperty<int> - just like
+KGamePropertyQString. Now you need to register the properties in the constructor
+of the class to the KGamePropertyHandler:
+MyGame::MyGame() : KGame(myCookie)
+{
+ money.registerData(KGamePropertyBase::IdUser+1, dataHandler(), "Money");
+ name.registerData(KGamePropertyBase::IdUser+2, this, "Name");
+ myProperty.registerData(KGamePropertyBase::IdUser+3, dataHandler(), "MyProperty");
+}
+-> You need to specify a *unique* ID. This ID must be greater than
+KGamePropertyBase::IdUser. IDs below this are reserved for KGame. Probably this
+will be changed so that you cannot use IDs below IdUser in the future. Then you
+have to specify the dataHandler(). You can also use a KGame or KPlayer pointer.
+This will automatically use KGame::dataHandler() or KPlayer::dataHandler().
+Finally you *can* provide a name for the property. This will be used for
+debugging in KGameDebugDialog. If you want to save some memory you can leave
+this out.
+Note that if you use pointers to create the properties dynamically they are
+*not* deleted automatically! You MUST delete them yourself!
+Now you can use the KGameProperty like every variable else. See also Section
+"3.3 Concepts" for restrictions in use.
+
+3.2 Custom Classes
+------------------
+To make custom classes possible you have to implement several operators for your
+them: you need at least << and >> for QDataStream as well as "==" for your own
+class. To overload the "<<" you would e.g. do something like this:
+QDataStream& operator<<(QDataStream& stream, MyData& data)
+{
+ int type = data.type;
+ QString name = data.name;
+ stream << type << name;
+ return stream;
+}
+So you basically just have to split your class to several basic types and stream
+them.
+
+3.3 Concepts
+------------
+You can use KGameProperty basically in two completely different ways. You can
+also use a mixture of both but this is not recommended. The default behaviour
+and therefore also the recommended is the "clean" way:
+a) Always Consistent. This means that a KGameProperty has always the same value
+on *every* client. This is achieved by using KGameProperty::send() whenever you
+want to change the value using "=". You can still use changeValue() or
+setLocal() but send() will be the default. If you use send() then the value of
+the property does *NOT* change immediately. It is just sent to the
+KMessageServer which forwards the value to all clients. As soon as the new value
+is received from the message server the KGamePropertyHandler (a collection class
+for KGameProperty) calls KGameProperty::load() and changes the value of the
+property. So the game first has to go into the event loop, where the message is
+received. This means to you that you cannot do this:
+myIntProperty = 10;
+int value = myIntProperty;
+As myIntPoperty still has the old value when "value = myIntProperty" is called.
+This might seem to be quite complex, but
+KGamePropertyHandler::signalPropertyChanged() is emitted whenever a new value is
+assigned so you can connect to this and work immediately with the new value.
+You gain the certainty that the value is the same on every client every time.
+That will safe you a lot of time debugging!
+Another way is the "dirty" way:
+b) Not Always Consistent. Sometimes you really *want* to do something like
+myIntProperty = 10;
+int value = myIntProperty;
+but this is not possible with the default behaviour. If you call
+KGameProperty::setAlwaysConsistent(false) in the constructor (right after
+registerData()) you get another behaviour. "=" means changeValue() now.
+changeValue() also uses send() to change the value but additionally calls
+setLocal() to create a local copy of the property. This copy now has the value
+you supplied with "=" and is deleted again as soon as any value from the network
+is received.
+
+4. KGameIO
+----------
+The class KGameIO is used to let the players communicate with the server. You
+can plug as many KGameIO objects into a player as you want, e.g. you can plug a
+KGameMouseIO and a KGameKeyIO into a player so that you can control the player
+with the mouse and the keyboard - e.g. in a breakout game.
+You can probably see the advantage: as most of the control stuff is common in a
+lot of games you can use the same IO class in many different games with very
+small adjustments.
+You could also put all the IO stuff directly into your KPlayer object, like
+sendBet(int money) for a poker game. But there is a major disadvantage and I'm
+very sure you don't want to use a KPlayer object for your IO stuff as soon as
+you know which disadvantage:
+KGameIO is designed to be able to switch between different IOs "on the fly". So
+you might have a KGamePlayerIO, derived from KGameIO, for your game. But now
+this player (who "owns"/uses the KGamePlayerIO) leaves the game (e.g. because he
+was a remote player). So now the game would be over for every player as one
+player is now out of order. But with KGameIO you can just let any of the
+remaining clients create a KGameComputerIO and plug this into the player. So the
+player now is controlled by the computer and the game can continue.
+
+Think about it! You don't have to care about removing players when a player
+leaves as you can just replace it! The same works the other way round: imagine a
+game with 10 player (e.g. 5 human and 5 computer players) that has already
+started. You cannot add any further players without restarting. So if there are
+any additional player you can just call KPlayer::removeGameIO() which removes
+the IO of a computer player and then call KPlayer::addGameIO() for the same
+player which adds a GameIO for new human player. That's all!
+
+To achieve this you just have to make sure that you make *all* of your IO
+operations through a KGameIO! So instead of using MyPlayer::sendBet(int money)
+you should use something like MyIO::sendBet(). The amount of money would
+probably be calculated by the game IO itself.
+
+
+
+5. Debugging
+------------
+The general debugging concept (if there is one at all) or general debugging
+hints are not yet written. Feel free to do so
+
+5.1 KGameDebugDialog
+--------------------
+A nice way of debugging a KGame based game is the KGameDebugDialog. Basically
+all you have to do is to add something like "Debug" to your game's menu and add
+a slot like
+slotDebug()
+{
+ KGameDebugDialog* dialog = new KGameDebugDialog(mGame, this);
+ connect(dialog, SIGNAL(finished()), dialog, SLOT(slotDelayedDestruct()));
+ dialog->show();
+}
+that's it.
+You can now click on that menu entry and you get a non-modal dialog where you
+can start to debug :-)
+The dialog consist of several pages. You can easily add your own using
+KDialogBase::addVBoxPage() (for example).
+
+5.1.1 Debug KGame
+-----------------
+The first page, "Debug KGame" shows on the left most or even all status values of
+KGame. That contains e.g. minPlayers(), isAdmin(), gameStatus(), ...
+The right side is probably the more important one. It lists *all* KGameProperties
+which have been inserted to this KGame object (only to this KGame object - not
+the ones that have been added to the players!). Most of the status variables of
+the left side are here again as they are implemented as KGameProperty. You can
+see the name of the property (together with its ID), its value and the policy
+this property uses. Note that "unknwon" will be displayed as name of the
+property if you haven't supplied one. See KGamePropertyBase::registerData() for
+info. You probably always want to supply a name for the property to debug it
+easily. In the future there will be something like
+KGamePropertyHandler::setDebug() so that you can switch off debugging and save
+the memory of the names in a release version.
+For as long as you use standard types for your properties (int, long, bool,
+...) you should always be able to see the value of the property. If you just see
+"unknown" then this type has not been implemented. You can connect to the signal
+KGamePropertyHandler::signalRequestValue() and supply a QString with the value
+yourself. If you do so for a standard type please also submit a bug report!
+
+Currently the dialog does *not* update automatically! So you alway have to click
+the "update" button when you want a current value. There are several reasons for
+this (and one of them is that i'm too lazy to implement the update ;)). E.g.
+often (very often) a property is just in the background - stores e.g. the
+available money in a game. But you don't want it to update whenever the value
+changes (a player receives/pays money) but only when the value on the screen
+changes.
+
+5.1.2 Debug Players
+-------------------
+This page consists of three widgets. On the very left there is a list of all
+players in the game. Only the IDs are displayed to save space. If you click one
+the other widgets are filled with content. These widgets are quite much the same
+as the ones in "Debug KGame" - the left shows the value of the functions and the
+right one displays all KProperties of a player. Not much to say here - except:
+See "Debug KGame".
+
+If you change to another player the value are also updated.
+
+5.1.3 Debug Messages
+--------------------
+This page is probably not as important as the other ones. It displays *every*
+message that is sent through the KGame object. As a KGameProperry also send
+messages you probably get a lot of them...
+You can exclude message IDs from being displayed (e.g. all game properties).
+You can also change the sorting of the list to see all messages of a certain ID.
+The default is to sort by time (which is displayed on the left side).
+
diff --git a/libkdegames/kgame/Makefile.am b/libkdegames/kgame/Makefile.am
new file mode 100644
index 00000000..e0117780
--- /dev/null
+++ b/libkdegames/kgame/Makefile.am
@@ -0,0 +1,29 @@
+
+noinst_LTLIBRARIES = libkgame.la
+
+# compile-order doesn't matter here but maybe we will split these section soon
+
+KGAME = kgame.cpp kplayer.cpp kgamenetwork.cpp kgameproperty.cpp \
+ kgamemessage.cpp kgameio.cpp kgameprocess.cpp kgamechat.cpp \
+ kgamepropertyhandler.cpp kgameerror.cpp kgamesequence.cpp
+KGAME_H = kgame.h kplayer.h kgamenetwork.h kgameproperty.h kgamemessage.h \
+ kgameio.h kgameprocess.h kgamepropertyarray.h \
+ kgamepropertylist.h kgamechat.h kgamepropertyhandler.h \
+ kgameerror.h kgamesequence.h kgameversion.h
+
+KMESSAGE = kmessageio.cpp kmessageserver.cpp kmessageclient.cpp
+KMESSAGE_H = kmessageio.h kmessageserver.h kmessageclient.h
+
+libkgameincludedir=$(includedir)/kgame
+libkgame_la_SOURCES = $(KMESSAGE) $(KGAME)
+
+libkgameinclude_HEADERS = $(KMESSAGE_H) $(KGAME_H)
+
+INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes)
+METASOURCES = AUTO
+
+SUBDIRS = . dialogs
+
+messages:
+# $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot
+
diff --git a/libkdegames/kgame/README.LIB b/libkdegames/kgame/README.LIB
new file mode 100644
index 00000000..512edbac
--- /dev/null
+++ b/libkdegames/kgame/README.LIB
@@ -0,0 +1,12 @@
+some thoughts and comments about the lib - usually for KGame hackers
+
+- setMin/MaxPlayers() etc. use KGameProperty::changeValue() which is slightly
+ unclean but as these functions can only called by the ADMIN it doesn't matter.
+- AB: KGamePropertyList && KGamePropertyArray:
+ for PolicyClean||PolicyDirty the values are streamed into a QDataStream as usual
+ for PolicyDirty||PolicyLocal the values are streamed as well but
+ additionally command() is called immediately. The values are read from
+ the stream there. This is some kind of performance loss as it would be
+ faster *not* to stream it but imediately call e.g. insert(). But it will
+ probably save a *lot* of bugs!
+
diff --git a/libkdegames/kgame/TODO b/libkdegames/kgame/TODO
new file mode 100644
index 00000000..2f100b8a
--- /dev/null
+++ b/libkdegames/kgame/TODO
@@ -0,0 +1,41 @@
+- 28.02.2001: Direct computer player for kpoker like games support needs to be
+ improved. UPDATE (01/10/06): but what is needed there?
+- 05.03.2001: Documentation. I am thinking of an explaination of the
+ class + methods and example code for the "key" methods. A sample
+ implementation in a small game (like the current kdenonbeta/kgame
+ but with a real sense - mabye use the QT tic-tac-toe example?)
+ would be very great (this could be stuff of a tutorial instead of
+ KGame documentation)
+ MH: Even better idea
+- 03.06.2001: can KGameNetwork::sendSystemMessage be made protected (maybe using
+ friends)? sendSystenMessage AND sendMessage is very confusing to
+ the user...
+- 03.06.2001: can we translate the group of a KPlayer? Probably not as there are
+ no international connections possible then... maybe a group id?
+- 05.06.2001: KGameDialog::saveConfig(KConfig*) might be useful (as well as
+ KGameDialog::loadConfig(KConfig*). Should set an own group in the
+ config file (setGroup("KGameDialog")). Problem: shalll network
+ settings be saved? Could be used for startup configuration (i.e.
+ load the config of the previous game) otherwise.
+- 21.06.2001: KPlayerPropertyArray does not yet support at() and operator[]
+ assignments. Need to check whether the method from QBitArray
+ can be applied
+- 02.04.2001: VERY DANGEROUS: property1=property2 does NOT assign the values, e.g. int
+ but assignes the whole property, i.e. you have then two properties with
+ the same id and everything is wrong
+ 01/09/09: FIXED! (AB) TODO: check if this behavior also appears in
+ KGamePropertyList and KGamePropertyArray. Althogh this should not
+ be the case
+- 23.09.2001: does the virtual destructor make sense for KGamePropertyBase?
+- 29.09.2001: GGZ integration. I (Andi) already volunteered for this - it's just
+ here so that I don't forget it
+- 06.10.2001: add KGamePropertyHandler::setDebug(false) to clear all debug names
+ (and to not accept new names) of KGameProperty. Will save some
+ memory
+- 06.10.2001: If one kicks a player (in KGameDialog) the client should be kicked
+ as well. Perhaps always disconnect a client when all players from
+ it have disappeared?
+- 07.10.2001: display (List) or (Array) for KGameProperty[List|Array] in
+ KGameDebugDialog as value
+- 08.10.2001: KGamePropertyList|KGamePropertyArray must be ported to new QT3 API
+ (e.g. erase instead of remove, ...)
diff --git a/libkdegames/kgame/dialogs/Makefile.am b/libkdegames/kgame/dialogs/Makefile.am
new file mode 100644
index 00000000..1b9de53c
--- /dev/null
+++ b/libkdegames/kgame/dialogs/Makefile.am
@@ -0,0 +1,17 @@
+
+noinst_LTLIBRARIES = libkgamedialogs.la
+
+# compile-order doesn't matter here but maybe we will split these section soon
+
+
+libkgamedialogs_la_SOURCES = kgamedialog.cpp kgameconnectdialog.cpp kgameerrordialog.cpp kgamedebugdialog.cpp kgamedialogconfig.cpp
+
+libkgamedialogsincludedir=$(includedir)/kgame
+libkgamedialogsinclude_HEADERS = kgamedialog.h kgameconnectdialog.h kgameerrordialog.h kgamedebugdialog.h kgamedialogconfig.h
+
+INCLUDES = -I$(top_srcdir)/libkdegames -I$(top_srcdir)/libkdegames/kgame $(all_includes)
+METASOURCES = AUTO
+
+messages:
+# $(XGETTEXT) `find . -name \*.h -o -name \*.cpp -o -name \*.cc` -o $(podir)/libkdegames.pot
+
diff --git a/libkdegames/kgame/dialogs/kgameconnectdialog.cpp b/libkdegames/kgame/dialogs/kgameconnectdialog.cpp
new file mode 100644
index 00000000..4d2d90e0
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgameconnectdialog.cpp
@@ -0,0 +1,278 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+#include "kgameconnectdialog.h"
+
+#include <knuminput.h>
+#include <klocale.h>
+
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qvbuttongroup.h>
+#include <qlayout.h>
+#include <qradiobutton.h>
+#include <qlabel.h>
+#include <dnssd/servicebrowser.h>
+#include <qpushbutton.h>
+#include <qgrid.h>
+
+class KGameConnectWidgetPrivate
+{
+ public:
+ KGameConnectWidgetPrivate()
+ {
+ mPort = 0;
+ mHost = 0;
+ mButtonGroup = 0;
+ mBrowser = 0;
+ }
+
+ KIntNumInput* mPort;
+ QLineEdit* mHost; //KLineEdit?
+ QVButtonGroup* mButtonGroup;
+ QComboBox *mClientName;
+ QLabel *mClientNameLabel;
+ DNSSD::ServiceBrowser *mBrowser;
+ QLabel *mServerNameLabel;
+ QLineEdit *mServerName;
+ QString mType;
+};
+
+KGameConnectWidget::KGameConnectWidget(QWidget* parent) : QWidget(parent)
+{
+ d = new KGameConnectWidgetPrivate;
+
+ QVBoxLayout* vb = new QVBoxLayout(this, KDialog::spacingHint());
+ d->mButtonGroup = new QVButtonGroup(this);
+ vb->addWidget(d->mButtonGroup);
+ connect(d->mButtonGroup, SIGNAL(clicked(int)), this, SLOT(slotTypeChanged(int)));
+ (void)new QRadioButton(i18n("Create a network game"), d->mButtonGroup);
+ (void)new QRadioButton(i18n("Join a network game"), d->mButtonGroup);
+
+ QGrid* g = new QGrid(2, this);
+ vb->addWidget(g);
+ g->setSpacing(KDialog::spacingHint());
+ d->mServerNameLabel = new QLabel(i18n("Game name:"), g);
+ d->mServerName = new QLineEdit(g);
+ d->mClientNameLabel = new QLabel(i18n("Network games:"), g);
+ d->mClientName = new QComboBox(g);
+ connect(d->mClientName,SIGNAL(activated(int)),SLOT(slotGameSelected(int)));
+ (void)new QLabel(i18n("Port to connect to:"), g);
+ d->mPort = new KIntNumInput(g);
+ (void)new QLabel(i18n("Host to connect to:"), g);
+ d->mHost = new QLineEdit(g);
+
+ QPushButton *button=new QPushButton(i18n("&Start Network"), this);
+ connect(button, SIGNAL(clicked()), this, SIGNAL(signalNetworkSetup()));
+ vb->addWidget(button);
+ // Hide until type is set
+ d->mClientName->hide();
+ d->mClientNameLabel->hide();
+ d->mServerName->hide();
+ d->mServerNameLabel->hide();
+}
+
+void KGameConnectWidget::showDnssdControls()
+{
+ if (!d->mBrowser) return;
+ if (d->mHost->isEnabled()) { // client
+ d->mClientName->show();
+ d->mClientNameLabel->show();
+ d->mServerName->hide();
+ d->mServerNameLabel->hide();
+ slotGameSelected(d->mClientName->currentItem());
+ } else {
+ d->mClientName->hide();
+ d->mClientNameLabel->hide();
+ d->mServerName->show();
+ d->mServerNameLabel->show();
+ }
+}
+
+void KGameConnectWidget::setType(const QString& type)
+{
+ d->mType = type;
+ delete d->mBrowser;
+ d->mBrowser = new DNSSD::ServiceBrowser(type);
+ connect(d->mBrowser,SIGNAL(finished()),SLOT(slotGamesFound()));
+ d->mBrowser->startBrowse();
+ showDnssdControls();
+}
+
+void KGameConnectWidget::slotGamesFound()
+{
+ bool autoselect=false;
+ if (!d->mClientName->count()) autoselect=true;
+ d->mClientName->clear();
+ QStringList names;
+ QValueList<DNSSD::RemoteService::Ptr>::ConstIterator itEnd = d->mBrowser->services().end();
+ for (QValueList<DNSSD::RemoteService::Ptr>::ConstIterator it = d->mBrowser->services().begin();
+ it!=itEnd; ++it) names << (*it)->serviceName();
+ d->mClientName->insertStringList(names);
+ if (autoselect && d->mClientName->count()) slotGameSelected(0);
+}
+
+void KGameConnectWidget::setName(const QString& name)
+{
+ d->mServerName->setText(name);
+}
+
+QString KGameConnectWidget::gameName() const
+{
+ return d->mServerName->text();
+}
+
+QString KGameConnectWidget::type() const
+{
+ return d->mType;
+}
+
+void KGameConnectWidget::slotGameSelected(int nr)
+{
+ if (nr>=(d->mBrowser->services().count()) || nr<0) return;
+ if (!d->mHost->isEnabled()) return; // this is server mode, do not overwrite host and port controls
+ DNSSD::RemoteService::Ptr srv = d->mBrowser->services()[nr];
+ if (!srv->isResolved() && !srv->resolve()) return;
+ d->mHost->setText(srv->hostName());
+ d->mPort->setValue(srv->port());
+}
+KGameConnectWidget::~KGameConnectWidget()
+{
+ delete d->mBrowser;
+ delete d;
+}
+
+QString KGameConnectWidget::host() const
+{
+ if (d->mHost->isEnabled()) {
+ return d->mHost->text();
+ } else {
+ return QString::null;
+ }
+}
+
+unsigned short int KGameConnectWidget::port() const
+{
+ return d->mPort->value();
+}
+
+void KGameConnectWidget::setHost(const QString& host)
+{
+ d->mHost->setText(host);
+}
+
+void KGameConnectWidget::setPort(unsigned short int port)
+{
+ d->mPort->setValue(port);
+}
+
+void KGameConnectWidget::setDefault(int state)
+{
+ d->mButtonGroup->setButton(state);
+ slotTypeChanged(state);
+}
+
+void KGameConnectWidget::slotTypeChanged(int t)
+{
+ if (t == 0) {
+ d->mHost->setEnabled(false);
+ } else if (t == 1) {
+ d->mHost->setEnabled(true);
+ }
+ showDnssdControls();
+ emit signalServerTypeChanged(t);
+}
+
+class KGameConnectDialogPrivate
+{
+ public:
+ KGameConnectDialogPrivate()
+ {
+ mConnect = 0;
+ }
+
+ KGameConnectWidget* mConnect;
+};
+
+// buttonmask =Ok|Cancel
+KGameConnectDialog::KGameConnectDialog(QWidget* parent,int buttonmask) : KDialogBase(Plain,
+ i18n("Network Game"),buttonmask , Ok, parent, 0, true, buttonmask!=0)
+{
+ d = new KGameConnectDialogPrivate;
+ QVBoxLayout* vb = new QVBoxLayout(plainPage(), spacingHint());
+ d->mConnect = new KGameConnectWidget(plainPage());
+ vb->addWidget(d->mConnect);
+}
+
+KGameConnectDialog::~KGameConnectDialog()
+{
+ delete d;
+}
+
+int KGameConnectDialog::initConnection( unsigned short int& port,
+ QString& host, QWidget* parent, bool server)
+{
+ KGameConnectDialog d(parent);
+ d.setHost(host);
+ d.setPort(port);
+ if (server) {
+ d.setDefault(0);
+ } else {
+ d.setDefault(1);
+ }
+
+ int result = d.exec();
+ if (result == QDialog::Accepted) {
+ host = d.host();
+ port = d.port();
+ }
+ return result;
+}
+
+QString KGameConnectDialog::host() const
+{
+ return d->mConnect->host();
+}
+
+unsigned short int KGameConnectDialog::port() const
+{
+ return d->mConnect->port();
+}
+
+void KGameConnectDialog::setHost(const QString& host)
+{
+ d->mConnect->setHost(host);
+}
+
+void KGameConnectDialog::setPort(unsigned short int port)
+{
+ d->mConnect->setPort(port);
+}
+
+void KGameConnectDialog::setDefault(int state)
+{
+ d->mConnect->setDefault(state);
+}
+
+
+
+#include "kgameconnectdialog.moc"
+
diff --git a/libkdegames/kgame/dialogs/kgameconnectdialog.h b/libkdegames/kgame/dialogs/kgameconnectdialog.h
new file mode 100644
index 00000000..acbf21d2
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgameconnectdialog.h
@@ -0,0 +1,169 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMECONNECTDIALOG_H__
+#define __KGAMECONNECTDIALOG_H__
+
+#include <kdialogbase.h>
+
+class KGameConnectDialogPrivate;
+class KGameConnectWidgetPrivate;
+
+class KGameConnectWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ KGameConnectWidget(QWidget* parent);
+ virtual ~KGameConnectWidget();
+
+ /**
+ * @param host The host to connect to by default
+ **/
+ void setHost(const QString& host);
+
+ /**
+ * @return The host to connect to or QString::null if the user wants to
+ * be the MASTER
+ **/
+ QString host() const;
+
+ /**
+ * @param port The port that will be shown by default
+ **/
+ void setPort(unsigned short int port);
+
+ /**
+ * @return The port to connect to / to listen
+ **/
+ unsigned short int port() const;
+
+ /**
+ * Specifies which state is the default (0 = server game; 1 = join game)
+ * @param state The default state. 0 For a server game, 1 to join a game
+ **/
+ void setDefault(int state);
+
+ /**
+ * Sets DNS-SD service type, both for publishing and browsing
+ * @param type Service type (something like _kwin4._tcp).
+ * It should be unique for application.
+ * @since 3.4
+ **/
+ void setType(const QString& type);
+
+ /**
+ * @return service type
+ */
+ QString type() const;
+
+ /**
+ * Set game name for publishing.
+ * @param name Game name. Important only for server mode. If not
+ * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name.
+ */
+ void setName(const QString& name);
+
+ /**
+ * @return game name.
+ */
+ QString gameName() const;
+
+protected slots:
+ /**
+ * The type has changed, ie the user switched between creating or
+ * joining.
+ **/
+ void slotTypeChanged(int);
+ void slotGamesFound();
+ void slotGameSelected(int);
+
+signals:
+ void signalNetworkSetup();
+ void signalServerTypeChanged(int);
+
+private:
+ void showDnssdControls();
+ KGameConnectWidgetPrivate* d;
+
+};
+
+/**
+ * @short Dialog to ask for host and port
+ *
+ * This Dialog is used to create a game. You call initConnection(port,
+ * QString::null, parent, true) to create a network game (as a server)
+ * or initConnection(port, host, parent) to join a network game.
+ *
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KGameConnectDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KGameConnectDialog(QWidget* parent = 0,int buttonmask=Ok|Cancel);
+ virtual ~KGameConnectDialog();
+
+ /**
+ * Shows a dialog to either connect to an existing game or to create a
+ * server game, depending on user's choice.
+ * @param port The port the user wants to connect to.
+ * @param host The host the user wants to connect to. Will be
+ * QString::null if server game is chosen
+ * @param parent The parent of the dialog
+ * @param server True to create a network game per default, false to
+ * join a game by default
+ **/
+ static int initConnection(unsigned short int& port, QString& host, QWidget* parent, bool server = false);
+
+ /**
+ * @param host The host to connect to by default
+ **/
+ void setHost(const QString& host);
+
+ /**
+ * @return The host to connect to or QString::null if the user wants to
+ * be the MASTER
+ **/
+ QString host() const;
+
+ /**
+ * @param port The port that will be shown by default
+ **/
+ void setPort(unsigned short int port);
+
+ /**
+ * @return The port to connect to / to listen
+ **/
+ unsigned short int port() const;
+
+ /**
+ * Specifies which state is the default (0 = server game; 1 = join game)
+ * @param state The default state. 0 For a server game, 1 to join a game
+ **/
+ void setDefault(int state);
+
+signals:
+ void signalNetworkSetup();
+
+private:
+ KGameConnectDialogPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/dialogs/kgamedebugdialog.cpp b/libkdegames/kgame/dialogs/kgamedebugdialog.cpp
new file mode 100644
index 00000000..b112b04c
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedebugdialog.cpp
@@ -0,0 +1,548 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kgamedebugdialog.h"
+
+#include "kgamemessage.h"
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgamepropertyhandler.h"
+
+#include <klistview.h>
+#include <klistbox.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include <qlayout.h>
+#include <qstring.h>
+#include <qintdict.h>
+#include <qlabel.h>
+#include <qdatetime.h>
+
+#include <typeinfo>
+
+
+class KGameDebugDialogPrivate
+{
+public:
+ KGameDebugDialogPrivate()
+ {
+ mGame = 0;
+
+ mGamePage = 0;
+ mGameProperties = 0;
+ mGameAddress = 0;
+ mGameId = 0;
+ mGameCookie = 0;
+ mGameMaster = 0;
+ mGameAdmin = 0;
+ mGameOffering = 0;
+ mGameStatus = 0;
+ mGameRunning = 0;
+ mGameMaxPlayers = 0;
+ mGameMinPlayers = 0;
+ mGamePlayerCount = 0;
+
+ mPlayerPage = 0;
+ mPlayerList = 0;
+ mPlayerProperties = 0;
+ mPlayerAddress = 0;
+ mPlayerId = 0;
+ mPlayerName = 0;
+ mPlayerGroup = 0;
+ mPlayerUserId = 0;
+ mPlayerMyTurn = 0;
+ mPlayerAsyncInput= 0;
+ mPlayerKGameAddress = 0;
+ mPlayerVirtual = 0;
+ mPlayerActive = 0;
+ mPlayerRtti = 0;
+ mPlayerNetworkPriority = 0;
+
+ mMessagePage = 0;
+ mMessageList = 0;
+ mHideIdList = 0;
+ }
+
+ const KGame* mGame;
+
+ QFrame* mGamePage;
+ KListView* mGameProperties;
+ QListViewItem* mGameAddress;
+ QListViewItem* mGameId;
+ QListViewItem* mGameCookie;
+ QListViewItem* mGameMaster;
+ QListViewItem* mGameAdmin;
+ QListViewItem* mGameOffering;
+ QListViewItem* mGameStatus;
+ QListViewItem* mGameRunning;
+ QListViewItem* mGameMaxPlayers;
+ QListViewItem* mGameMinPlayers;
+ QListViewItem* mGamePlayerCount;
+
+ QFrame* mPlayerPage;
+ KListBox* mPlayerList;
+ KListView* mPlayerProperties;
+ QListViewItem* mPlayerAddress;
+ QListViewItem* mPlayerId;
+ QListViewItem* mPlayerName;
+ QListViewItem* mPlayerGroup;
+ QListViewItem* mPlayerUserId;
+ QListViewItem* mPlayerMyTurn;
+ QListViewItem* mPlayerAsyncInput;
+ QListViewItem* mPlayerKGameAddress;
+ QListViewItem* mPlayerVirtual;
+ QListViewItem* mPlayerActive;
+ QListViewItem* mPlayerRtti;
+ QListViewItem* mPlayerNetworkPriority;
+
+ QFrame* mMessagePage;
+ KListView* mMessageList;
+ KListBox* mHideIdList;
+};
+
+KGameDebugDialog::KGameDebugDialog(KGame* g, QWidget* parent, bool modal) :
+ KDialogBase(Tabbed, i18n("KGame Debug Dialog"), Close, Close,
+ parent, 0, modal, true)
+{
+ d = new KGameDebugDialogPrivate;
+
+ initGamePage();
+ initPlayerPage();
+ initMessagePage();
+
+ setKGame(g);
+}
+
+KGameDebugDialog::~KGameDebugDialog()
+{
+ delete d;
+}
+
+void KGameDebugDialog::initGamePage()
+{
+ d->mGamePage = addPage(i18n("Debug &KGame"));
+ QVBoxLayout* topLayout = new QVBoxLayout(d->mGamePage, marginHint(), spacingHint());
+ QHBoxLayout* layout = new QHBoxLayout(topLayout);
+
+ KListView* v = new KListView(d->mGamePage);
+ v->addColumn(i18n("Data"));
+ v->addColumn(i18n("Value"));
+ layout->addWidget(v);
+
+ d->mGameProperties = new KListView(d->mGamePage);
+ d->mGameProperties->addColumn(i18n("Property"));
+ d->mGameProperties->addColumn(i18n("Value"));
+ d->mGameProperties->addColumn(i18n("Policy"));
+ layout->addWidget(d->mGameProperties);
+
+ QPushButton* b = new QPushButton(i18n("Update"), d->mGamePage);
+ connect(b, SIGNAL(pressed()), this, SLOT(slotUpdateGameData()));
+ topLayout->addWidget(b);
+
+// game data
+ d->mGameAddress = new QListViewItem(v, i18n("KGame Pointer"));
+ d->mGameId = new QListViewItem(v, i18n("Game ID"));
+ d->mGameCookie = new QListViewItem(v, i18n("Game Cookie"));
+ d->mGameMaster = new QListViewItem(v, i18n("Is Master"));
+ d->mGameAdmin = new QListViewItem(v, i18n("Is Admin"));
+ d->mGameOffering = new QListViewItem(v, i18n("Is Offering Connections"));
+ d->mGameStatus = new QListViewItem(v, i18n("Game Status"));
+ d->mGameRunning = new QListViewItem(v, i18n("Game is Running"));
+ d->mGameMaxPlayers = new QListViewItem(v, i18n("Maximal Players"));
+ d->mGameMinPlayers = new QListViewItem(v, i18n("Minimal Players"));
+ d->mGamePlayerCount = new QListViewItem(v, i18n("Players"));
+}
+
+void KGameDebugDialog::initPlayerPage()
+{
+ d->mPlayerPage = addPage(i18n("Debug &Players"));
+ QVBoxLayout* topLayout = new QVBoxLayout(d->mPlayerPage, marginHint(), spacingHint());
+ QHBoxLayout* layout = new QHBoxLayout(topLayout);
+
+ //TODO: connect to the KGame signals for joined/removed players!!!
+ QVBoxLayout* listLayout = new QVBoxLayout(layout);
+ QLabel* listLabel = new QLabel(i18n("Available Players"), d->mPlayerPage);
+ listLayout->addWidget(listLabel);
+ d->mPlayerList = new KListBox(d->mPlayerPage);
+ connect(d->mPlayerList, SIGNAL(executed(QListBoxItem*)), this, SLOT(slotUpdatePlayerData(QListBoxItem*)));
+ listLayout->addWidget(d->mPlayerList);
+ d->mPlayerList->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding));
+
+ KListView* v = new KListView(d->mPlayerPage);
+ layout->addWidget(v);
+ v->addColumn(i18n("Data"));
+ v->addColumn(i18n("Value"));
+
+ d->mPlayerProperties = new KListView(d->mPlayerPage);
+ d->mPlayerProperties->addColumn(i18n("Property"));
+ d->mPlayerProperties->addColumn(i18n("Value"));
+ d->mPlayerProperties->addColumn(i18n("Policy"));
+ layout->addWidget(d->mPlayerProperties);
+
+ QPushButton* b = new QPushButton(i18n("Update"), d->mPlayerPage);
+ connect(b, SIGNAL(pressed()), this, SLOT(slotUpdatePlayerList()));
+ topLayout->addWidget(b);
+
+ d->mPlayerAddress = new QListViewItem(v, i18n("Player Pointer"));
+ d->mPlayerId = new QListViewItem(v, i18n("Player ID"));
+ d->mPlayerName = new QListViewItem(v, i18n("Player Name"));
+ d->mPlayerGroup = new QListViewItem(v, i18n("Player Group"));
+ d->mPlayerUserId = new QListViewItem(v, i18n("Player User ID"));
+ d->mPlayerMyTurn = new QListViewItem(v, i18n("My Turn"));
+ d->mPlayerAsyncInput = new QListViewItem(v, i18n("Async Input"));
+ d->mPlayerKGameAddress = new QListViewItem(v, i18n("KGame Address"));
+ d->mPlayerVirtual = new QListViewItem(v, i18n("Player is Virtual"));
+ d->mPlayerActive = new QListViewItem(v, i18n("Player is Active"));
+ d->mPlayerRtti = new QListViewItem(v, i18n("RTTI"));
+ d->mPlayerNetworkPriority = new QListViewItem(v, i18n("Network Priority"));
+}
+
+void KGameDebugDialog::initMessagePage()
+{
+ d->mMessagePage = addPage(i18n("Debug &Messages"));
+ QGridLayout* layout = new QGridLayout(d->mMessagePage, 11, 7, marginHint(), spacingHint());
+ d->mMessageList = new KListView(d->mMessagePage);
+ layout->addMultiCellWidget(d->mMessageList, 0, 9, 0, 3);
+ d->mMessageList->addColumn(i18n("Time"));
+ d->mMessageList->addColumn(i18n("ID"));
+ d->mMessageList->addColumn(i18n("Receiver"));
+ d->mMessageList->addColumn(i18n("Sender"));
+ d->mMessageList->addColumn(i18n("ID - Text"));
+
+ QPushButton* hide = new QPushButton(i18n("&>>"), d->mMessagePage);
+ connect(hide, SIGNAL(pressed()), this, SLOT(slotHideId()));
+ layout->addWidget(hide, 4, 4);
+
+ QPushButton* show = new QPushButton(i18n("&<<"), d->mMessagePage);
+ connect(show, SIGNAL(pressed()), this, SLOT(slotShowId()));
+ layout->addWidget(show, 6, 4);
+
+ QLabel* l = new QLabel(i18n("Do not show IDs:"), d->mMessagePage);
+ layout->addMultiCellWidget(l, 0, 0, 5, 6);
+ d->mHideIdList = new KListBox(d->mMessagePage);
+ layout->addMultiCellWidget(d->mHideIdList, 1, 8, 5, 6);
+
+ QPushButton* clear = new KPushButton(KStdGuiItem::clear(), d->mMessagePage);
+ connect(clear, SIGNAL(pressed()), this, SLOT(slotClearMessages()));
+ layout->addMultiCellWidget(clear, 10, 10, 0, 6);
+ //TODO: "show all but..." and "show nothing but..."
+}
+
+void KGameDebugDialog::clearPlayerData()
+{
+ d->mPlayerAddress->setText(1, "");
+ d->mPlayerId->setText(1, "");
+ d->mPlayerName->setText(1, "");
+ d->mPlayerGroup->setText(1, "");
+ d->mPlayerUserId->setText(1, "");
+ d->mPlayerMyTurn->setText(1, "");
+ d->mPlayerAsyncInput->setText(1, "");
+ d->mPlayerKGameAddress->setText(1, "");
+ d->mPlayerVirtual->setText(1, "");
+ d->mPlayerActive->setText(1, "");
+ d->mPlayerRtti->setText(1, "");
+ d->mPlayerNetworkPriority->setText(1, "");
+
+ d->mPlayerProperties->clear();
+}
+
+void KGameDebugDialog::clearGameData()
+{
+ d->mGameAddress->setText(1, "");
+ d->mGameId->setText(1, "");
+ d->mGameCookie->setText(1, "");
+ d->mGameMaster->setText(1, "");
+ d->mGameAdmin->setText(1, "");
+ d->mGameOffering->setText(1, "");
+ d->mGameStatus->setText(1, "");
+ d->mGameRunning->setText(1, "");
+ d->mGameMaxPlayers->setText(1, "");
+ d->mGameMinPlayers->setText(1, "");
+
+ d->mGameProperties->clear();
+}
+
+void KGameDebugDialog::slotUpdatePlayerData()
+{
+ if (!d->mGame || d->mPlayerList->currentItem() == -1) {
+ return;
+ }
+ slotUpdatePlayerData(d->mPlayerList->item(d->mPlayerList->currentItem()));
+}
+
+void KGameDebugDialog::slotUpdatePlayerList()
+{
+ QListBoxItem* i = d->mPlayerList->firstItem();
+ for (; i; i = d->mPlayerList->firstItem()) {
+ removePlayer(i);
+ }
+
+ QPtrList<KPlayer> list = *d->mGame->playerList();
+ for (KPlayer* p = list.first(); p; p = list.next()) {
+ addPlayer(p);
+ }
+}
+
+void KGameDebugDialog::slotUpdateGameData()
+{
+ if (!d->mGame) {
+ d->mGameAddress->setText(1, i18n("NULL pointer"));
+ return;
+}
+
+ clearGameData();
+
+ QString buf;
+ buf.sprintf("%p", d->mGame);
+ d->mGameAddress->setText(1, buf);
+ d->mGameId->setText(1, QString::number(d->mGame->gameId()));
+ d->mGameCookie->setText(1, QString::number(d->mGame->cookie()));
+ d->mGameMaster->setText(1, d->mGame->isMaster() ? i18n("True") : i18n("False"));
+ d->mGameAdmin->setText(1, d->mGame->isAdmin() ? i18n("True") : i18n("False"));
+ d->mGameOffering->setText(1, d->mGame->isOfferingConnections() ? i18n("True") : i18n("False"));
+ d->mGameStatus->setText(1, QString::number(d->mGame->gameStatus()));
+ d->mGameRunning->setText(1, d->mGame->isRunning() ? i18n("True") : i18n("False"));
+ d->mGameMaxPlayers->setText(1, QString::number(d->mGame->maxPlayers()));
+ d->mGameMinPlayers->setText(1, QString::number(d->mGame->minPlayers()));
+ d->mGamePlayerCount->setText(1, QString::number(d->mGame->playerCount()));
+
+//TODO ios
+
+ KGamePropertyHandler* handler = d->mGame->dataHandler();
+ QIntDictIterator<KGamePropertyBase> it(handler->dict());
+ while (it.current()) {
+ QString policy;
+ switch (it.current()->policy()) {
+ case KGamePropertyBase::PolicyClean:
+ policy = i18n("Clean");
+ break;
+ case KGamePropertyBase::PolicyDirty:
+ policy = i18n("Dirty");
+ break;
+ case KGamePropertyBase::PolicyLocal:
+ policy = i18n("Local");
+ break;
+ case KGamePropertyBase::PolicyUndefined:
+ default:
+ policy = i18n("Undefined");
+ break;
+ }
+ (void) new QListViewItem(d->mGameProperties,
+ handler->propertyName(it.current()->id()),
+ handler->propertyValue(it.current()),
+ policy);
+// kdDebug(11001) << k_funcinfo << ": checking for all game properties: found property name " << name << endl;
+ ++it;
+ }
+}
+
+void KGameDebugDialog::slotUpdatePlayerData(QListBoxItem* item)
+{
+ if (!item || !d->mGame) {
+ return;
+ }
+
+ KPlayer* p = d->mGame->findPlayer(item->text().toInt());
+
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": cannot find player" << endl;
+ return;
+ }
+
+ clearPlayerData();
+
+ QString buf;
+ buf.sprintf("%p", p);
+ d->mPlayerAddress->setText(1, buf);
+ d->mPlayerId->setText(1, QString::number(p->id()));
+ d->mPlayerName->setText(1, p->name());
+ d->mPlayerGroup->setText(1, p->group());
+ d->mPlayerUserId->setText(1, QString::number(p->userId()));
+ d->mPlayerMyTurn->setText(1, p->myTurn() ? i18n("True") : i18n("False"));
+ d->mPlayerAsyncInput->setText(1, p->asyncInput() ? i18n("True") : i18n("False"));
+ buf.sprintf("%p", p->game());
+ d->mPlayerKGameAddress->setText(1, buf);
+ d->mPlayerVirtual->setText(1, p->isVirtual() ? i18n("True") : i18n("False"));
+ d->mPlayerActive->setText(1, p->isActive() ? i18n("True") : i18n("False"));
+ d->mPlayerRtti->setText(1, QString::number(p->rtti()));
+ d->mPlayerNetworkPriority->setText(1, QString::number(p->networkPriority()));
+
+//TODO ios
+
+// Properties
+ KGamePropertyHandler * handler = p->dataHandler();
+ QIntDictIterator<KGamePropertyBase> it((handler->dict()));
+ while (it.current()) {
+ QString policy;
+ switch (it.current()->policy()) {
+ case KGamePropertyBase::PolicyClean:
+ policy = i18n("Clean");
+ break;
+ case KGamePropertyBase::PolicyDirty:
+ policy = i18n("Dirty");
+ break;
+ case KGamePropertyBase::PolicyLocal:
+ policy = i18n("Local");
+ break;
+ case KGamePropertyBase::PolicyUndefined:
+ default:
+ policy = i18n("Undefined");
+ break;
+ }
+ (void)new QListViewItem(d->mPlayerProperties,
+ handler->propertyName(it.current()->id()),
+ handler->propertyValue(it.current()),
+ policy);
+ ++it;
+ }
+}
+
+void KGameDebugDialog::clearPages()
+{
+ clearPlayerData();
+ clearGameData();
+ d->mPlayerList->clear();
+ slotClearMessages();
+}
+
+void KGameDebugDialog::setKGame(const KGame* g)
+{
+ slotUnsetKGame();
+ d->mGame = g;
+ if (g) {
+ //TODO: connect to the KGame signals for joined/removed players!!!
+ connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame()));
+// connect();
+
+ QPtrList<KPlayer> list = *d->mGame->playerList();
+ for (KPlayer* p = list.first(); p; p = list.next()) {
+ addPlayer(p);
+ }
+
+ slotUpdateGameData();
+
+ connect(d->mGame, SIGNAL(signalMessageUpdate(int, Q_UINT32, Q_UINT32)), this, SLOT(slotMessageUpdate(int, Q_UINT32, Q_UINT32)));
+ }
+}
+
+void KGameDebugDialog::slotUnsetKGame()
+{
+ if (d->mGame) {
+ disconnect(d->mGame, 0, this, 0);
+ }
+ d->mGame = 0;
+ clearPages();
+}
+
+void KGameDebugDialog::addPlayer(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << "trying to add NULL player" << endl;
+ return;
+ }
+
+ (void) new QListBoxText(d->mPlayerList, QString::number(p->id()));
+ //TODO connect to signals, like deleted/removed, ...
+}
+
+void KGameDebugDialog::removePlayer(QListBoxItem* i)
+{
+ if (!i || !d->mGame) {
+ return;
+ }
+ KPlayer* p = d->mGame->findPlayer(i->text().toInt());
+ if (!p) {
+ return;
+ }
+ disconnect(p, 0, this, 0);
+ if (i->isSelected()) {
+ clearPlayerData();
+ }
+ delete i;
+}
+
+void KGameDebugDialog::slotMessageUpdate(int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ if (!showId(msgid)) {
+ return;
+ }
+ QString msgidText = KGameMessage::messageId2Text(msgid);
+ if (msgidText.isNull()) {
+ if (msgid > KGameMessage::IdUser) {
+ emit signalRequestIdName(msgid-KGameMessage::IdUser, true, msgidText);
+ } else {
+ emit signalRequestIdName(msgid, false, msgidText);
+ }
+ if (msgidText.isNull()) {
+ msgidText = i18n("Unknown");
+ }
+ }
+ (void) new QListViewItem( d->mMessageList, QTime::currentTime().toString(),
+ QString::number(msgid), QString::number(receiver),
+ QString::number(sender), msgidText);
+}
+
+void KGameDebugDialog::slotClearMessages()
+{
+ d->mMessageList->clear();
+}
+
+void KGameDebugDialog::slotShowId()
+{
+/* QListBoxItem* i = d->mHideIdList->firstItem();
+ for (; i; i = i->next()) {
+ if (i->selected()) {
+ d->mHideIdList->removeItem(i->);
+ }
+ }*/
+ if (!d->mHideIdList->currentItem()) {
+ return;
+ }
+ d->mHideIdList->removeItem(d->mHideIdList->currentItem());
+}
+
+void KGameDebugDialog::slotHideId()
+{
+ if (!d->mMessageList->currentItem()) {
+ return;
+ }
+ int msgid = d->mMessageList->currentItem()->text(1).toInt();
+ if (!showId(msgid)) {
+ return;
+ }
+ (void)new QListBoxText(d->mHideIdList, QString::number(msgid));
+}
+
+bool KGameDebugDialog::showId(int msgid)
+{
+ QListBoxItem* i = d->mHideIdList->firstItem();
+ for (; i; i = i->next()) {
+ if (i->text().toInt() == msgid) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+#include "kgamedebugdialog.moc"
diff --git a/libkdegames/kgame/dialogs/kgamedebugdialog.h b/libkdegames/kgame/dialogs/kgamedebugdialog.h
new file mode 100644
index 00000000..65afc92a
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedebugdialog.h
@@ -0,0 +1,149 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEDEBUGDIALOG_H__
+#define __KGAMEDEBUGDIALOG_H__
+
+#include <kdialogbase.h>
+#include <kdemacros.h>
+
+class KGame;
+class KGameIO;
+class KPlayer;
+class KGamePropertyBase;
+
+class KGameDebugDialogPrivate;
+
+class KDE_EXPORT KGameDebugDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KGameDebugDialog(KGame* g, QWidget* parent, bool modal = false);
+ ~KGameDebugDialog();
+
+ /**
+ * Automatically connects the KGame object to all error dependant slots.
+ * Create a KGameErrorDialog object, call this function and forget
+ * everything.
+ * @param g The KGame which will emit the erorrs (or not ;-) )
+ **/
+ void setKGame(const KGame* g);
+
+public slots:
+ /**
+ * Unsets a @ref KGame which has been set using @ref setKGame before.
+ * This is called automatically when the @ref KGame object is destroyed
+ * and you normally don't have to call this yourself.
+ *
+ * Note that @ref setKGame also unsets an already existing @ref KGame
+ * object if exising.
+ **/
+ void slotUnsetKGame();
+
+ /**
+ * Update the data of the @ref KGame object
+ **/
+ void slotUpdateGameData();
+
+ /**
+ * Update the properties of the currently selected player
+ **/
+ void slotUpdatePlayerData();
+
+ /**
+ * Updates the list of players and calls @ref clearPlayerData. Note that
+ * after this call NO player is selected anymore.
+ **/
+ void slotUpdatePlayerList();
+
+ void slotClearMessages();
+
+signals:
+ /**
+ * This signal is emitted when the "debug messages" page couldn't find
+ * the name of a message id. This is usually the case for user-defined
+ * messages. KGameDebugDialog asks you to give the msgid a name.
+ * @param messageid The ID of the message. As given to @ref
+ * KGame::sendMessage
+ * @param userid User defined msgIds are internally increased by
+ * @ref KGameMessage::IdUser. You don't have to care about this but if
+ * this signal is emitted with userid=false (shouldn't happen) then the
+ * name of an internal message as defined in @ref
+ * KGameMessage::GameMessageIds couldn't be found.
+ * @param name The name of the msgid. You have to fill this!
+ **/
+ void signalRequestIdName(int messageid, bool userid, QString& name);
+
+protected:
+ void clearPages();
+
+ /**
+ * Clear the data of the player view. Note that the player list is NOT
+ * cleared.
+ **/
+ void clearPlayerData();
+
+ /**
+ * Clear the data view of the @ref KGame object
+ **/
+ void clearGameData();
+
+ /**
+ * Add a new player to the player list
+ **/
+ void addPlayer(KPlayer* p);
+
+ /**
+ * Remove a player from the list
+ **/
+ void removePlayer(QListBoxItem* item);
+
+ /**
+ * @return Whether messages with this msgid shall be displayed or not
+ **/
+ bool showId(int msgid);
+
+protected slots:
+ /**
+ * Update the data of the player specified in item
+ * @param item The @ref QListBoxItem of the player to be updated. Note
+ * that the text of this item MUST be the ID of the player
+ **/
+ void slotUpdatePlayerData(QListBoxItem* item);
+
+ void slotShowId();
+ void slotHideId();
+
+ /**
+ * A message has been received - see @ref KGame::signalMessageUpdate
+ **/
+ void slotMessageUpdate(int msgid, Q_UINT32 receiver, Q_UINT32 sender);
+
+private:
+ void initGamePage();
+ void initPlayerPage();
+ void initMessagePage();
+
+private:
+ KGameDebugDialogPrivate* d;
+};
+
+
+#endif
diff --git a/libkdegames/kgame/dialogs/kgamedialog.cpp b/libkdegames/kgame/dialogs/kgamedialog.cpp
new file mode 100644
index 00000000..dc564b8e
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedialog.cpp
@@ -0,0 +1,347 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qlayout.h>
+#include <qvbox.h>
+
+#include <klocale.h>
+
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgamedialogconfig.h"
+
+#include "kgamedialog.h"
+
+#include "kgamedialog.moc"
+
+class KGameDialogPrivate
+{
+public:
+ KGameDialogPrivate()
+ {
+ mGamePage = 0;
+ mNetworkPage = 0;
+ mMsgServerPage = 0;
+ mTopLayout = 0;
+
+ mNetworkConfig = 0;
+ mGameConfig = 0;
+
+ mOwner = 0;
+ mGame = 0;
+ }
+
+ QVBox* mGamePage;
+ QVBox* mNetworkPage;
+ QVBox* mMsgServerPage;// unused here?
+ QVBoxLayout* mTopLayout;
+ KGameDialogNetworkConfig* mNetworkConfig;
+ KGameDialogGeneralConfig* mGameConfig;
+
+// a list of all config widgets added to this dialog
+ QPtrList<KGameDialogConfig> mConfigWidgets;
+
+// just pointers:
+ KPlayer* mOwner;
+ KGame* mGame;
+};
+
+KGameDialog::KGameDialog(KGame* g, KPlayer* owner, const QString& title,
+ QWidget* parent, bool modal)
+ : KDialogBase(Tabbed, title, Ok|Default|Apply,
+ Ok, parent, 0, modal, true)
+{
+ init(g, owner);
+}
+
+KGameDialog::KGameDialog(KGame* g, KPlayer* owner, const QString& title,
+ QWidget* parent, long initConfigs, int chatMsgId, bool modal)
+ : KDialogBase(Tabbed, title, Ok|Default|Apply,
+ Ok, parent, 0, modal, true)
+{
+ init(g, owner);
+ if ((ConfigOptions)initConfigs!=NoConfig) {
+ initDefaultDialog((ConfigOptions)initConfigs, chatMsgId);
+ }
+}
+
+void KGameDialog::init(KGame* g, KPlayer* owner)
+{
+//AB: do we need a "Cancel" Button? currently removed
+
+// kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ d = new KGameDialogPrivate;
+
+ setOwner(owner);
+ setKGame(g);
+ if (g) {
+ setAdmin(g->isAdmin());
+ } else {
+ setAdmin(false);
+ }
+}
+
+void KGameDialog::initDefaultDialog(ConfigOptions initConfigs, int chatMsgId)
+{
+ if (initConfigs & GameConfig) {
+ kdDebug() << "add gameconf" << endl;
+ addGameConfig(new KGameDialogGeneralConfig(0));
+ }
+ if (initConfigs & NetworkConfig) {
+ addNetworkConfig(new KGameDialogNetworkConfig(0));
+ }
+ if (initConfigs & (MsgServerConfig) ) {
+ addMsgServerConfig(new KGameDialogMsgServerConfig(0));
+ }
+ if (initConfigs & ChatConfig) {
+ KGameDialogChatConfig * c = new KGameDialogChatConfig(chatMsgId, 0);
+ if (d->mGamePage) {
+ addChatWidget(c, d->mGamePage);
+ } else {
+ addConfigPage(c, i18n("&Chat"));
+ }
+ }
+ if (initConfigs & BanPlayerConfig) {
+ // add the connection management system - ie the widget where the ADMIN can
+ // kick players out
+ if (d->mNetworkPage) {
+ // put it on the network page
+ addConnectionList(new KGameDialogConnectionConfig(0), d->mNetworkPage);
+ } else {
+ // if no network page available put it on an own page
+ addConfigPage(new KGameDialogConnectionConfig(0), i18n("C&onnections"));
+ }
+ }
+}
+
+KGameDialog::~KGameDialog()
+{
+// kdDebug(11001) << "DESTRUCT KGameDialog" << this << endl;
+ d->mConfigWidgets.setAutoDelete(true);
+ d->mConfigWidgets.clear();
+ delete d;
+}
+
+void KGameDialog::addGameConfig(KGameDialogGeneralConfig* conf)
+{
+ if (!conf) {
+ return;
+ }
+ d->mGameConfig = conf;
+ d->mGamePage = addConfigPage(d->mGameConfig, i18n("&Game"));
+}
+
+void KGameDialog::addNetworkConfig(KGameDialogNetworkConfig* netConf)
+{
+ if (!netConf) {
+ return;
+ }
+ d->mNetworkConfig = netConf;
+ d->mNetworkPage = addConfigPage(netConf, i18n("&Network"));
+}
+
+void KGameDialog::addMsgServerConfig(KGameDialogMsgServerConfig* msgConf)
+{
+ if (!msgConf) {
+ return;
+ }
+ d->mMsgServerPage = addConfigPage(msgConf, i18n("&Message Server"));
+}
+
+void KGameDialog::addChatWidget(KGameDialogChatConfig* chat, QVBox* parent)
+{
+ if (!chat) {
+ return;
+ }
+ if (!parent) {
+ parent = d->mGamePage;
+ }
+ if (!parent) {
+ kdError(11001) << "cannot add chat widget without page" << endl;
+ return;
+ }
+ addConfigWidget(chat, parent);
+}
+
+void KGameDialog::addConnectionList(KGameDialogConnectionConfig* c, QVBox* parent)
+{
+ if (!c) {
+ return;
+ }
+ if (!parent) {
+ parent = d->mNetworkPage;
+ }
+ if (!parent) {
+ kdError(11001) << "Cannot add connection list without page" << endl;
+ return;
+ }
+ addConfigWidget(c, parent);
+}
+
+QVBox *KGameDialog::configPage(ConfigOptions which)
+{
+ QVBox *box = 0;
+ switch(which)
+ {
+ case NetworkConfig:
+ box = d->mNetworkPage;
+ break;
+ case GameConfig:
+ box = d->mGamePage;
+ break;
+ case MsgServerConfig:
+ box = d->mMsgServerPage;
+ break;
+ default:
+ kdError(11001) << k_funcinfo << ": Parameter " << which << " not supported" << endl;
+ }
+ return box;
+}
+
+QVBox* KGameDialog::addConfigPage(KGameDialogConfig* widget, const QString& title)
+{
+ if (!widget) {
+ kdError(11001) << "Cannot add NULL config widget" << endl;
+ return 0;
+ }
+ QVBox* page = addVBoxPage(title);
+ addConfigWidget(widget, page);
+ return page;
+}
+
+void KGameDialog::addConfigWidget(KGameDialogConfig* widget, QWidget* parent)
+{
+ if (!widget) {
+ kdError(11001) << "Cannot add NULL config widget" << endl;
+ return;
+ }
+ if (!parent) {
+ kdError(11001) << "Cannot reparent to NULL widget" << endl;
+ return;
+ }
+// kdDebug(11001) << "reparenting widget" << endl;
+ widget->reparent(parent, QPoint(0,0));
+ d->mConfigWidgets.append(widget);
+ connect(widget, SIGNAL(destroyed(QObject*)), this, SLOT(slotRemoveConfigWidget(QObject*)));
+ if (!d->mGame) {
+ kdWarning(11001) << "No game has been set!" << endl;
+ } else {
+ widget->setKGame(d->mGame);
+ widget->setAdmin(d->mGame->isAdmin());
+ }
+ if (!d->mOwner) {
+ kdWarning(11001) << "No player has been set!" << endl;
+ } else {
+ widget->setOwner(d->mOwner);
+ }
+ widget->show();
+}
+
+KGameDialogGeneralConfig* KGameDialog::gameConfig() const
+{ return d->mGameConfig; }
+KGameDialogNetworkConfig* KGameDialog::networkConfig() const
+{ return d->mNetworkConfig; }
+
+void KGameDialog::slotApply()
+{
+ submitToKGame();
+}
+
+void KGameDialog::slotDefault()
+{
+ if (!d->mGame) {
+ return;
+ }
+
+//TODO *only* call setKGame/setOwner for the *current* page!!
+ setKGame(d->mGame);
+ setOwner(d->mOwner);
+}
+
+void KGameDialog::slotOk()
+{
+ slotApply();
+ QDialog::accept();
+}
+
+void KGameDialog::setOwner(KPlayer* owner)
+{
+//AB: note: NULL player is ok!
+ d->mOwner = owner;
+ for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) {
+ if (d->mConfigWidgets.at(i)) {
+ d->mConfigWidgets.at(i)->setOwner(d->mOwner);
+ //TODO: hide playerName in KGameDialogGeneralConfig
+ } else {
+ kdError(11001) << "NULL widget??" << endl;
+ }
+ }
+}
+
+void KGameDialog::setKGame(KGame* g)
+{
+ if (d->mGame) {
+ disconnect(d->mGame, 0, this, 0);
+ }
+ d->mGame = g;
+ for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) {
+ d->mConfigWidgets.at(i)->setKGame(d->mGame);
+ }
+ if (d->mGame) {
+ setAdmin(d->mGame->isAdmin());
+ connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame()));
+ connect(d->mGame, SIGNAL(signalAdminStatusChanged(bool)),
+ this, SLOT(setAdmin(bool)));
+ }
+}
+
+void KGameDialog::setAdmin(bool admin)
+{
+ for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) {
+ d->mConfigWidgets.at(i)->setAdmin(admin);
+ }
+}
+
+void KGameDialog::slotUnsetKGame() // called when KGame is destroyed
+{ setKGame(0); }
+
+void KGameDialog::submitToKGame()
+{
+ if (!d->mGame) {
+ kdError(11001) << k_funcinfo << ": no game has been set" << endl;
+ return;
+ }
+ if (!d->mOwner) {
+ kdError(11001) << k_funcinfo << ": no player has been set" << endl;
+ return;
+ }
+
+ for (int unsigned i = 0; i < d->mConfigWidgets.count(); i++) {
+// kdDebug(11001) << "submit to kgame " << i << endl;
+ d->mConfigWidgets.at(i)->submitToKGame(d->mGame, d->mOwner);
+// kdDebug(11001) << "done: submit to kgame " << i << endl;
+ }
+}
+
+void KGameDialog::slotRemoveConfigWidget(QObject* configWidget)
+{
+ d->mConfigWidgets.removeRef((KGameDialogConfig*)configWidget);
+}
+
diff --git a/libkdegames/kgame/dialogs/kgamedialog.h b/libkdegames/kgame/dialogs/kgamedialog.h
new file mode 100644
index 00000000..f7a0c6e5
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedialog.h
@@ -0,0 +1,320 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// NAMING
+// please follow these naming rules if you add/change classes:
+// the main dialog is named KGameDialog and the base config widget
+// KGameDialogConfig. All config widgets are named KGameDialogXYZConfig (where
+// XYZ = the name of the config widget, like "general" or "network") and are
+// inherited from KGameDialogConfig.
+
+#ifndef __KGAMEDIALOG_H__
+#define __KGAMEDIALOG_H__
+
+#include <kdialogbase.h>
+#include <kdemacros.h>
+class QGridLayout;
+class QVBoxLayout;
+class QListBoxItem;
+
+class KGame;
+class KPlayer;
+class KGamePropertyBase;
+
+class KGameDialogConfig;
+class KGameDialogGeneralConfig;
+class KGameDialogNetworkConfig;
+class KGameDialogMsgServerConfig;
+class KGameDialogChatConfig;
+class KGameDialogConnectionConfig;
+
+class KGameDialogPrivate;
+/**
+ * TODO: rewrite entire documentation. Nearly nothing is valid anymore.
+ * The main configuration dialog for KGame. Here all players meat each other,
+ * every player can see how many players connected (and their names) and the
+ * ADMIN can even "kick" players out. You can talk to each other (using
+ * KGameChat and the ADMIN can define the maxPlayers/minPlayers as well as the
+ * number of computer players.
+ *
+ *
+ * AB: setDefaultXYZ is obsolete!!
+ * You will usually create an instance of KGameDialog or any derived class and
+ * call setDefaultXYZ methods. Example (maybe
+ * obsoleted parameters - docu is currently changing very fast):
+ * \code
+ * KGameDialog dlg(kgame, i18n("New Game"), localPlayer, this, true,
+ * ID_CHAT);
+ * dlg.setDefaultNetworkInfo(port, host); // AB: obsolete!
+ * dlg.exec();
+ * \endcode
+ * This will create a default modal dialog with the title "New Game". You don't
+ * have to do more than this.
+ *
+ * @short Main configuration dialog for KGame
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KDE_EXPORT KGameDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+
+ enum ConfigOptions
+ {
+ NoConfig = 0,
+ ChatConfig = 1,
+ GameConfig = 2,
+ NetworkConfig = 4,
+ MsgServerConfig = 8,
+ BanPlayerConfig = 16,
+ AllConfig = 0xffff
+ };
+
+ /**
+ * Create an empty KGameDialog. You can add widgets using
+ * addConfigPage.
+ * @param g The KGame object of this game
+ * @param owner The KPlayer object who is responsible for this
+ * dialog, aka "the local player"
+ * @param title The title of the dialog - see KDialog::setCaption
+ * @param parent The parent of the dialog
+ * @param modal Whether the dialog is modal or not
+ **/
+ KGameDialog(KGame* g, KPlayer* owner, const QString& title,
+ QWidget* parent, bool modal = false);
+
+ /**
+ * Create a KGameDialog with the standard configuration widgets. This
+ * creates the following widgets:
+ * <ul>
+ * <li> KGameDialogGeneralConfig
+ * <li> KGameDialogNetworkConfig
+ * <li> KGameDialogMsgServerConfig
+ * <li> KGameDialogChatConfig
+ * <li> KGameDialogConnectionConfig
+ * </ul>
+ * If you want to use your own implementations (or none) of the widgets
+ * above you should subclass KGameDialog. Use addGameConfig,
+ * addNetworkConfig, addMsgConfig, addChatWidget and
+ * addConnectionList in this case.
+ *
+ * If you want to add further configuration widget you can simply use
+ * addConfigPage
+ * @param g The KGame object of this game
+ * @param owner The KPlayer object who is responsible for this
+ * dialog, aka "the local player"
+ * @param title The title of the dialog - see KDialog::setCaption
+ * @param parent The parent of the dialog
+ * @param modal Whether the dialog is modal or not
+ * @param initConfigs whether the default KGameDialogConfig widgets
+ * shall be created using initDefaultDialog. Use false if you want
+ * to use custom widgets.
+ * @param chatMsgId The ID of Chat messages. See KGameChat. Unused
+ * if initConfigs = false
+ **/
+ KGameDialog(KGame* g, KPlayer* owner, const QString& title,
+ QWidget* parent, long initConfigs = AllConfig,
+ int chatMsgId = 15432, bool modal = false);
+
+ virtual ~KGameDialog();
+
+
+ /**
+ * Change the owner of the dialog. This will be used as the fromPlayer in
+ * KGameChat and will receive the entered player name.
+ * @param owner The owner of the dialog. It must already be added to the
+ * KGame object!
+ *
+ * Calls the KGameDialogConfig::setOwner implementation of all
+ * widgets that have been added by addConfigWidget
+ * @param owner The new owner player of this dialog must already be
+ * added to the KGame object. Can even be NULL (then no player
+ * configuration is made)
+ **/
+ void setOwner(KPlayer* owner);
+
+ /**
+ * Change the KGame object this dialog is used for.
+ *
+ * Calls the KGameDialogConfig::setKGame implementation of all
+ * widgets that have been added by addConfigWidget
+ * @param g The new KGame object
+ **/
+ void setKGame(KGame* g);
+
+ /**
+ * This will submit all configuration data to the KGame object.
+ * Automatically called by slotApply and slotOk
+ * There is no need to replace this unless you
+ * want to add widgets which are not derived from those classes
+ **/
+ virtual void submitToKGame();
+
+ /**
+ * Adds a KGameChat to the dialog. If no parent is specified the
+ * game page will be used.
+ * @param chat The chat widget
+ * @param parent The parent of the chat widget. This MUST be an
+ * already added config widget. Note that the game page will be used
+ * if parent is 0.
+ **/
+ void addChatWidget(KGameDialogChatConfig* chat, QVBox* parent = 0);
+
+ /**
+ * Add a connection list to the dialog. The list consists of a
+ * KLisBox containing all players in the current game (see
+ * KGame::playerList). The admin can "ban" players, ie kick them out of
+ * the game.
+ *
+ * This is another not-really-config-config-widget. It just displays the
+ * connections and lets you ban players.
+ * @param c The KGameDialogConnectionConfig object
+ * @param parent The parent of the widget. If 0 the networkConfig
+ * page is used.
+ **/
+ void addConnectionList(KGameDialogConnectionConfig* c, QVBox* parent = 0);
+
+ /**
+ * Add a new page to the dialog. The page will contain you new config
+ * widget and will have your provided title.
+ *
+ * The widget will be reparented to this dialog. This also calls
+ * KGameDialogConfig::setKGame and KGameDialogConfig::setOwner.
+ * @param widget The new config widget
+ * @param title The title of the newly added page.
+ * @return The newly added page which contains your config widget.
+ **/
+ QVBox* addConfigPage(KGameDialogConfig* widget, const QString& title);
+
+ /**
+ * @return The QVBox of the given key, The key is from ConfigOptions
+ * Note that not all are supported yet
+ **/
+ QVBox *configPage(ConfigOptions which);
+
+ /**
+ * @return The default netowrk config. Note that this always returns 0 if
+ * you did not specify NetworkConfig in the constructor!
+ **/
+ KGameDialogNetworkConfig* networkConfig() const;
+
+ /**
+ * @return The default game config. Note that this always returns 0 if
+ * you did not specify GameConfig in the constructor!
+ **/
+ KGameDialogGeneralConfig* gameConfig() const;
+
+ /**
+ * Add a config widget to the specified parent. Usually you call
+ * addConfigPage for one widget and addConfigWidget for another to add
+ * it to the same page. Just use the returned page of
+ * addConfigPage.
+ **/
+ void addConfigWidget(KGameDialogConfig* widget, QWidget* parent);
+
+ /**
+ * Used to add the main network config widget in a new page. Use this to
+ * make networkConfig return something useful.
+ **/
+ void addNetworkConfig(KGameDialogNetworkConfig* netConf);
+
+ /**
+ * Add the main game config widget in a new page. Use this to make
+ * gameConfig return something useful.
+ **/
+ void addGameConfig(KGameDialogGeneralConfig* conf);
+
+ /**
+ * Used to add the message server config widget in a new page.
+ **/
+ void addMsgServerConfig(KGameDialogMsgServerConfig* conf);
+
+protected:
+
+ /**
+ * This is used to create a dialog containing all the default widgets.
+ *
+ * You may want to use this if you just want to use your own
+ * configuration widgets which inherit the standard ones.
+ *
+ * Note that if one of the widgets is NULL the default implementation
+ * will be used! (except the chat widget - you need to create it
+ * yourself as you have to provide a message id)
+ * @param initConfigs The widgets to be created
+ * @param chatMsgId The msgid for the chat config (only if specified in
+ * initConfigs) - see KGameDialogChatConfig
+ **/
+ void initDefaultDialog(ConfigOptions initConfigs, int chatMsgId = 15432);
+
+ /**
+ * Go through all config widgets and call their
+ * KGameDialogConfig::setKGame and KGameDialogConfig::setOwner implementation
+ *
+ * This function could be private and probably will be very soon.
+ * Don't use it yourself
+ **/
+ void configureConfigWidgets();
+
+protected slots:
+ /**
+ * Called when the user clicks on Ok. Calls slotApply and
+ * QDialog::accept()
+ **/
+ virtual void slotOk();
+
+ /**
+ * Just calls submitToKGame()
+ **/
+ virtual void slotApply();
+
+ /**
+ * Sets the default values for the configuration widgets. Set these
+ * values by (e.g.) setDefaultMaxPlayers()
+ * @deprecated
+ **/
+ virtual void slotDefault();
+
+ /**
+ * Called when the KGame object is destroyed. Calls setKGame(0) so
+ * that all widgets can disconnect their slots and so on.
+ **/
+ void slotUnsetKGame();
+
+ /**
+ * Called when the ADMIN status of this KGame client changes. See
+ * KGameNetwork::signalAdminStatusChanged
+ * @param isAdmin TRUE if this client is now the ADMIN otherwise FALSE
+ **/
+ void setAdmin(bool isAdmin);
+
+ /**
+ * Remove a config widget from the widget list.
+ * @see QObject::destroyed
+ **/
+ void slotRemoveConfigWidget(QObject* configWidget);
+
+private:
+ void init(KGame*, KPlayer*);
+
+private:
+ KGameDialogPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/dialogs/kgamedialogconfig.cpp b/libkdegames/kgame/dialogs/kgamedialogconfig.cpp
new file mode 100644
index 00000000..27f91cd1
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedialogconfig.cpp
@@ -0,0 +1,773 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kgamedialogconfig.h"
+
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgamechat.h"
+#include "kgameconnectdialog.h"
+
+#include <klocale.h>
+#include <knuminput.h>
+#include <kdialog.h>
+#include <klistbox.h>
+#include <kmessagebox.h>
+
+#include <qlayout.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qlineedit.h>
+#include <qvbox.h>
+#include <qptrdict.h>
+
+#include "kgamedialogconfig.moc"
+
+class KGameDialogConfigPrivate
+{
+public:
+ KGameDialogConfigPrivate()
+ {
+ mOwner = 0;
+ mGame = 0;
+
+ mAdmin = false;
+ }
+
+ bool mAdmin;
+ KGame* mGame;
+ KPlayer* mOwner;
+};
+
+KGameDialogConfig::KGameDialogConfig(QWidget* parent) : QWidget(parent)
+{
+ d = new KGameDialogConfigPrivate;
+}
+
+KGameDialogConfig::~KGameDialogConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogConfig::setKGame(KGame* g)
+{
+ d->mGame = g;
+}
+
+void KGameDialogConfig::setOwner(KPlayer* p)
+{
+ d->mOwner = p;
+}
+
+void KGameDialogConfig::setAdmin(bool a)
+{
+ d->mAdmin = a;
+}
+
+KGame* KGameDialogConfig::game() const
+{ return d->mGame; }
+bool KGameDialogConfig::admin() const
+{ return d->mAdmin; }
+KPlayer* KGameDialogConfig::owner() const
+{ return d->mOwner; }
+
+/////////////////////////// KGameDialogNetworkConfig /////////////////////////
+class KGameDialogNetworkConfigPrivate
+{
+public:
+ KGameDialogNetworkConfigPrivate()
+ {
+ mInitConnection = 0;
+ mNetworkLabel = 0;
+ mDisconnectButton = 0;
+ mConnect = 0;
+ mDefaultServer=true;
+
+ }
+
+ // QPushButton* mInitConnection;
+ QHGroupBox* mInitConnection;
+ QLabel* mNetworkLabel;
+ QPushButton *mDisconnectButton;
+
+ bool mDefaultServer;
+ QString mDefaultHost;
+ unsigned short int mDefaultPort;
+ KGameConnectWidget *mConnect;
+};
+
+
+KGameDialogNetworkConfig::KGameDialogNetworkConfig(QWidget* parent)
+ : KGameDialogConfig(parent)
+{
+// kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ d = new KGameDialogNetworkConfigPrivate();
+
+ QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint(), "toplayout");
+
+ QHBoxLayout *hb = new QHBoxLayout(topLayout, KDialog::spacingHint());
+
+ d->mNetworkLabel = new QLabel(this);
+ hb->addWidget(d->mNetworkLabel);
+
+ d->mDisconnectButton=new QPushButton(i18n("Disconnect"),this);
+ connect(d->mDisconnectButton, SIGNAL(clicked()), this, SLOT(slotExitConnection()));
+ hb->addWidget(d->mDisconnectButton);
+
+ d->mInitConnection = new QHGroupBox(i18n("Network Configuration"), this);
+ topLayout->addWidget(d->mInitConnection);
+
+ d->mConnect = new KGameConnectWidget(d->mInitConnection);
+ connect(d->mConnect, SIGNAL(signalNetworkSetup()), this, SLOT(slotInitConnection()));
+ connect(d->mConnect, SIGNAL(signalServerTypeChanged(int)),
+ this, SIGNAL(signalServerTypeChanged(int)));
+
+ // Needs to be AFTER the creation of the dialogs
+ setConnected(false);
+ setDefaultNetworkInfo("localhost", 7654,true);
+}
+
+KGameDialogNetworkConfig::~KGameDialogNetworkConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogNetworkConfig::slotExitConnection()
+{
+ kdDebug(11001) << k_funcinfo << " !!!!!!!!!!!!!!!!!!!!!!!" << endl;
+ if (game()) game()->disconnect();
+ setConnected(false,false);
+}
+
+void KGameDialogNetworkConfig::slotInitConnection()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ bool connected = false;
+ bool master = true;
+ unsigned short int port = d->mConnect->port();
+ QString host = d->mConnect->host();
+
+ if (host.isNull()) {
+ master = true;
+ if (game()) {
+ game()->setDiscoveryInfo(d->mConnect->type(),d->mConnect->gameName());
+ connected = game()->offerConnections(port);
+ }
+ } else {
+ master = false;
+ if (game()) {
+ connected = game()->connectToServer(host, port);
+ }
+ // We need to learn about failed connections
+ if (game()) {
+ connect(game(), SIGNAL(signalConnectionBroken()),
+ this, SLOT(slotConnectionBroken()));
+ }
+ }
+ setConnected(connected, master);
+}
+
+void KGameDialogNetworkConfig::slotConnectionBroken()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ setConnected(false,false);
+ KMessageBox::error(this, i18n("Cannot connect to the network"));
+}
+
+void KGameDialogNetworkConfig::setConnected(bool connected, bool master)
+{
+ if (!connected) {
+ d->mNetworkLabel->setText(i18n("Network status: No Network"));
+ d->mInitConnection->setEnabled(true);
+ d->mDisconnectButton->setEnabled(false);
+ return;
+ }
+ if (master) {
+ d->mNetworkLabel->setText(i18n("Network status: You are MASTER"));
+ } else {
+ d->mNetworkLabel->setText(i18n("Network status: You are connected"));
+ }
+ d->mInitConnection->setEnabled(false);
+ d->mDisconnectButton->setEnabled(true);
+}
+
+void KGameDialogNetworkConfig::submitToKGame(KGame* , KPlayer* )
+{
+}
+
+void KGameDialogNetworkConfig::setKGame(KGame* g)
+{
+ KGameDialogConfig::setKGame(g);
+ if (!game()) {
+ setConnected(false);
+ return;
+ }
+ setConnected(game()->isNetwork(), game()->isMaster());
+}
+
+void KGameDialogNetworkConfig::setDefaultNetworkInfo(const QString& host, unsigned short int port,bool server)
+{
+ d->mDefaultPort = port;
+ d->mDefaultHost = host;
+ d->mDefaultServer = server;
+
+ d->mConnect->setHost(host);
+ d->mConnect->setPort(port);
+ if (server) {
+ d->mConnect->setDefault(0);
+ } else {
+ d->mConnect->setDefault(1);
+ }
+}
+
+void KGameDialogNetworkConfig::setDiscoveryInfo(const QString& type, const QString& name)
+{
+ d->mConnect->setType(type);
+ d->mConnect->setName(name);
+}
+
+/////////////////////////// KGameDialogGeneralConfig /////////////////////////
+class KGameDialogGeneralConfigPrivate
+{
+public:
+ KGameDialogGeneralConfigPrivate()
+ {
+ mTopLayout = 0;
+ mName = 0;
+ }
+
+ QLineEdit* mName;
+
+ QVBoxLayout* mTopLayout;
+};
+
+KGameDialogGeneralConfig::KGameDialogGeneralConfig(QWidget* parent, bool initializeGUI)
+ : KGameDialogConfig(parent)
+{
+// kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ d = new KGameDialogGeneralConfigPrivate;
+
+ if (initializeGUI) {
+ d->mTopLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ d->mTopLayout->setAutoAdd(true);
+
+ QWidget* nameWidget = new QWidget(this);
+ QHBoxLayout* l = new QHBoxLayout(nameWidget);
+ QLabel* nameLabel = new QLabel(i18n("Your name:"), nameWidget);
+ l->addWidget(nameLabel);
+ d->mName = new QLineEdit(nameWidget);
+ l->addWidget(d->mName);
+ }
+}
+
+KGameDialogGeneralConfig::~KGameDialogGeneralConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogGeneralConfig::setPlayerName(const QString& name)
+{
+ if (d->mName) {
+ d->mName->setText(name);
+ }
+}
+
+QString KGameDialogGeneralConfig::playerName() const
+{
+ return d->mName ? d->mName->text() : QString::null;
+}
+
+void KGameDialogGeneralConfig::setOwner(KPlayer* p)
+{
+ if (owner()) {
+ owner()->disconnect(this);
+ }
+ KGameDialogConfig::setOwner(p);
+ if (!owner()) {
+ // can this config be used at all?
+ // maybe call hide()
+ return;
+ }
+ connect(owner(), SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)),
+ this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*)));
+ setPlayerName(p->name());
+ //TODO: connect signalPropertyChanged and check for playername changes!
+}
+
+void KGameDialogGeneralConfig::setKGame(KGame* g)
+{
+ KGameDialogConfig::setKGame(g);
+ if (!g) {
+ // TODO
+ // can this config be used at all?
+ // maybe call hide()
+ return;
+ }
+}
+
+void KGameDialogGeneralConfig::setAdmin(bool admin)
+{
+ KGameDialogConfig::setAdmin(admin);
+// enable/disable widgets
+
+}
+
+void KGameDialogGeneralConfig::submitToKGame(KGame* g, KPlayer* p)
+{
+//FIXME
+ if (p) {
+ p->setName(playerName());
+ }
+ if (g) {
+ }
+}
+
+void KGameDialogGeneralConfig::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* p)
+{
+ if (!prop || !p || p != owner()) {
+ return;
+ }
+ switch (prop->id()) {
+ case KGamePropertyBase::IdName:
+ setPlayerName(p->name());
+ break;
+ default:
+ break;
+ }
+}
+
+class KGameDialogMsgServerConfigPrivate
+{
+public:
+ KGameDialogMsgServerConfigPrivate()
+ {
+ senderLayout = 0;
+ localLayout = 0;
+
+ changeMaxClients = 0;
+ changeAdmin= 0;
+ removeClient= 0;
+ noAdmin = 0;
+
+ noMaster = 0;
+ }
+
+ QVBoxLayout* senderLayout;
+ QHBoxLayout* localLayout;
+
+ QPushButton* changeMaxClients;
+ QPushButton* changeAdmin;
+ QPushButton* removeClient;
+ QLabel* noAdmin;
+
+ QLabel* noMaster;
+};
+
+
+// TODO: change ADMIN ID, remove CLIENTS, change MAXCLIENTS
+// we do everything here with QPushButtons as we want to wait a moment before
+// continuing - the message must be sent over network first
+KGameDialogMsgServerConfig::KGameDialogMsgServerConfig(QWidget* parent)
+ : KGameDialogConfig(parent)
+{
+ d = new KGameDialogMsgServerConfigPrivate;
+
+ QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ d->senderLayout = new QVBoxLayout(topLayout);
+ d->localLayout = new QHBoxLayout(topLayout);
+}
+
+KGameDialogMsgServerConfig::~KGameDialogMsgServerConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogMsgServerConfig::setKGame(KGame* g)
+{
+ KGameDialogConfig::setKGame(g);
+ //TODO display the ID of the admin if we aren't
+ // connect(g, SIGNAL(signalAdminChanged(int)), this, SLOT(slotChangeIsAdmin(int)));//TODO
+ if (!game()) {
+ // we cannot do anything without a KGame object!
+ setAdmin(false);
+ return;
+ }
+ setAdmin(game()->isAdmin());
+ setHasMsgServer(game()->messageServer());
+}
+
+
+void KGameDialogMsgServerConfig::slotChangeMaxClients()
+{
+ if (!game()) {
+ kdError(11001) << k_funcinfo << ": no valid game object available!" << endl;
+ return;
+ }
+ if (!game()->isAdmin()) {
+ kdError(11001) << k_funcinfo << ": only ADMIN is allowed to call this!" << endl;
+ return;
+ }
+ int max;
+// edit->setText(QString::number()); // current max clients! //TODO
+
+ QDialog* dialog = new QDialog();
+ dialog->setCaption(i18n("Maximal Number of Clients"));
+ QHBoxLayout* l = new QHBoxLayout(dialog, KDialog::marginHint(), KDialog::spacingHint());
+ l->setAutoAdd(true);
+
+ (void) new QLabel(i18n("Maximal number of clients (-1 = infinite):"), dialog);
+ QLineEdit* edit = new QLineEdit(dialog);//TODO: use KIntNumInput
+// edit->setText(QString::number(max)); // current max clients! //TODO
+ if (dialog->exec() == QDialog::Accepted) {
+ bool ok;
+ max = edit->text().toInt(&ok);
+ if (ok) {
+ game()->setMaxClients(max);
+ }
+ }
+
+}
+
+void KGameDialogMsgServerConfig::slotRemoveClient()
+{
+}
+
+void KGameDialogMsgServerConfig::slotChangeAdmin()
+{
+ if (!game()) {
+ kdError(11001) << k_funcinfo << ": no valid game object available!" << endl;
+ return;
+ }
+ if (!admin()) {
+ kdError(11001) << k_funcinfo << ": only ADMIN is allowed to call this!" << endl;
+ return;
+ }
+ //TODO
+ Q_UINT32 newAdmin = 0;
+// newAdmin = ;
+ game()->electAdmin(newAdmin);
+}
+
+void KGameDialogMsgServerConfig::removeClient(Q_UINT32 /*id*/)
+{
+//TODO
+}
+
+void KGameDialogMsgServerConfig::setAdmin(bool a)
+{
+ if (admin() == a) {
+ // no need to do anything
+ return;
+ }
+ KGameDialogConfig::setAdmin(a);
+ if (admin()) {
+ if (d->noAdmin) {
+ delete d->noAdmin;
+ d->noAdmin = 0;
+ }
+ d->changeMaxClients = new QPushButton(i18n("Change Maximal Number of Clients"), this);
+ connect(d->changeMaxClients, SIGNAL(pressed()), this, SLOT(slotChangeMaxClients()));
+ d->changeAdmin = new QPushButton(i18n("Change Admin"), this);
+ connect(d->changeAdmin, SIGNAL(pressed()), this, SLOT(slotChangeAdmin()));
+ d->removeClient = new QPushButton(i18n("Remove Client with All Players"), this);
+ connect(d->removeClient, SIGNAL(pressed()), this, SLOT(slotRemoveClient()));
+ d->senderLayout->addWidget(d->changeMaxClients);
+ d->senderLayout->addWidget(d->changeAdmin);
+ d->senderLayout->addWidget(d->removeClient);
+ } else {
+ if (d->changeMaxClients) {
+ delete d->changeMaxClients;
+ d->changeMaxClients = 0;
+ }
+ if (d->changeAdmin) {
+ delete d->changeAdmin;
+ d->changeAdmin = 0;
+ }
+ if (d->removeClient) {
+ delete d->removeClient;
+ d->removeClient = 0;
+ }
+ d->noAdmin = new QLabel(i18n("Only the admin can configure the message server!"), this);
+ d->senderLayout->addWidget(d->noAdmin);
+ }
+}
+
+
+void KGameDialogMsgServerConfig::setHasMsgServer(bool has)
+{
+ if (!has) {
+ // delete all inputs
+ if (!d->noMaster) {
+ d->noMaster = new QLabel(i18n("You don't own the message server"), this);
+ d->localLayout->addWidget(d->noMaster);
+ }
+ return;
+ }
+ if (d->noMaster) {
+ delete d->noMaster;
+ d->noMaster = 0;
+ }
+ //TODO
+ // list all connections, data (max clients) and so on
+ // cannot be done above (together with QPushButtons) as it is possible that
+ // this client is ADMIN but not MASTER (i.e. doesn't own the messageserver)
+}
+
+
+class KGameDialogChatConfigPrivate
+{
+public:
+ KGameDialogChatConfigPrivate()
+ {
+ mChat = 0;
+ }
+
+ KGameChat* mChat;
+};
+
+KGameDialogChatConfig::KGameDialogChatConfig(int chatMsgId, QWidget* parent)
+ : KGameDialogConfig(parent)
+{
+ d = new KGameDialogChatConfigPrivate;
+ QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ topLayout->setAutoAdd(true);
+ QHGroupBox* b = new QHGroupBox(i18n("Chat"), this);
+ d->mChat = new KGameChat(0, chatMsgId, b);
+}
+
+KGameDialogChatConfig::~KGameDialogChatConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameDialogChatConfig::setKGame(KGame* g)
+{
+ KGameDialogConfig::setKGame(g);
+ d->mChat->setKGame(game());
+ if (!game()) {
+ hide();
+ } else {
+ show();
+ }
+}
+
+void KGameDialogChatConfig::setOwner(KPlayer* p)
+{
+ KGameDialogConfig::setOwner(p);
+ if (!owner()) {
+ hide();
+ return;
+ }
+ d->mChat->setFromPlayer(owner());
+ show();
+}
+
+
+
+class KGameDialogConnectionConfigPrivate
+{
+public:
+ KGameDialogConnectionConfigPrivate()
+ {
+ mPlayerBox = 0;
+ }
+
+ QPtrDict<KPlayer> mItem2Player;
+ KListBox* mPlayerBox;
+};
+
+KGameDialogConnectionConfig::KGameDialogConnectionConfig(QWidget* parent)
+ : KGameDialogConfig(parent)
+{
+ //TODO: prevent player to ban himself
+ d = new KGameDialogConnectionConfigPrivate;
+ QVBoxLayout* topLayout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ topLayout->setAutoAdd(true);
+ QHGroupBox* b = new QHGroupBox(i18n("Connected Players"), this);
+ d->mPlayerBox = new KListBox(b);
+ setMinimumHeight(100);
+}
+
+KGameDialogConnectionConfig::~KGameDialogConnectionConfig()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ // d->mIem2Player.clear();
+ delete d;
+}
+
+void KGameDialogConnectionConfig::setKGame(KGame* g)
+{
+ if (game()) {
+ disconnect(game(), 0, this, 0);
+ }
+ KGameDialogConfig::setKGame(g);
+ slotClearPlayers();
+ if (game()) {
+// react to changes in KGame::playerList()
+ connect(game(), SIGNAL(signalPlayerJoinedGame(KPlayer*)),
+ this, SLOT(slotPlayerJoinedGame(KPlayer*)));
+ connect(game(), SIGNAL(signalPlayerLeftGame(KPlayer*)),
+ this, SLOT(slotPlayerLeftGame(KPlayer*)));
+
+ KGame::KGamePlayerList l = *game()->playerList();
+ for (KPlayer* p = l.first(); p; p = l.next()) {
+ slotPlayerJoinedGame(p);
+ }
+ }
+}
+
+void KGameDialogConnectionConfig::setOwner(KPlayer* p)
+{
+ KGameDialogConfig::setOwner(p);
+}
+
+void KGameDialogConnectionConfig::setAdmin(bool a)
+{
+ if (!game()) {// not possible... in theory
+ return;
+ }
+ if (admin()) {
+ disconnect(game(), SIGNAL(executed(QListBoxItem*)), this, 0);
+ }
+ KGameDialogConfig::setAdmin(a);
+ if (admin()) {
+ connect(d->mPlayerBox, SIGNAL(executed(QListBoxItem*)), this,
+ SLOT(slotKickPlayerOut(QListBoxItem*)));
+ }
+}
+
+QListBoxItem* KGameDialogConnectionConfig::item(KPlayer* p) const
+{
+ QPtrDictIterator<KPlayer> it(d->mItem2Player);
+ while (it.current()) {
+ if (it.current() == p) {
+ return (QListBoxItem*)it.currentKey();
+ }
+ ++it;
+ }
+ return 0;
+}
+
+void KGameDialogConnectionConfig::slotClearPlayers()
+{
+ QPtrDictIterator<KPlayer> it(d->mItem2Player);
+ while (it.current()) {
+ slotPlayerLeftGame(it.current());
+ ++it;
+ }
+
+ if (d->mItem2Player.count() > 0) {
+ kdWarning(11001) << k_funcinfo << ": itemList wasn't cleared properly" << endl;
+ d->mItem2Player.clear();
+ }
+ if (d->mPlayerBox->count() > 0) {
+ kdWarning(11001) << k_funcinfo << ": listBox wasn't cleared properly" << endl;
+ d->mPlayerBox->clear();
+ }
+
+}
+
+void KGameDialogConnectionConfig::slotPlayerJoinedGame(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": Cannot add NULL player" << endl;
+ }
+ if (d->mItem2Player[p]) {
+ kdError(11001) << k_funcinfo << ": attempt to double add player" << endl;
+ return;
+ }
+ kdDebug(11001) << k_funcinfo << ": add player " << p->id() << endl;
+ QListBoxText* t = new QListBoxText(p->name());
+ d->mItem2Player.insert(t, p);
+ d->mPlayerBox->insertItem(t);
+
+ connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)),
+ this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*)));
+
+}
+
+void KGameDialogConnectionConfig::slotPlayerLeftGame(KPlayer* p)
+{
+ // disconnect first
+ this->disconnect(p);
+ if (!item(p)) {
+ kdError(11001) << k_funcinfo << ": cannot find " << p->id()
+ << " in list" << endl;
+ return;
+ }
+ d->mPlayerBox->removeItem(d->mPlayerBox->index(item(p)));
+
+}
+
+void KGameDialogConnectionConfig::slotKickPlayerOut(QListBoxItem* item)
+{
+ kdDebug(11001) << "kick player out" << endl;
+ KPlayer* p = d->mItem2Player[item];
+ if (!p) {
+ kdError(11001) << "invalid item selected - no player found" << endl;
+ return;
+ }
+ if (!game()) {
+ kdWarning(11001) << "no game set" << endl;
+ return;
+ }
+ if (!admin()) {
+ kdDebug(11001) << "Only the ADMIN can kick players" << endl;
+ return;
+ }
+ if (p == owner()) { // you wanna ban the ADMIN ??
+ kdDebug(11001) << "you cannot kick the ADMIN" << endl;
+ return;
+ }
+
+ if (KMessageBox::questionYesNo(this, i18n("Do you want to ban player \"%1\" from the game?").arg(
+ p->name()), QString::null, i18n("Ban Player"), i18n("Do Not Ban")) == KMessageBox::Yes) {
+ kdDebug(11001) << "will remove player " << p << endl;
+ game()->removePlayer(p);
+// d->mPlayerBox->removeItem(d->mPlayerBox->index(item)); // should be done by signalPlayerLeftGame
+ } else {
+ kdDebug(11001) << "will NOT remove player " << p << endl;
+ }
+}
+
+void KGameDialogConnectionConfig::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* player)
+{
+ if(prop->id() == KGamePropertyBase::IdName) {
+ QListBoxText* old = 0;
+ QPtrDictIterator<KPlayer> it(d->mItem2Player);
+ while (it.current() && !old) {
+ if (it.current() == player) {
+ old = (QListBoxText*)it.currentKey();
+ }
+ ++it;
+ }
+ QListBoxText* t = new QListBoxText(player->name());
+ d->mPlayerBox->changeItem(t, d->mPlayerBox->index(old));
+ d->mItem2Player.remove(old);
+ d->mItem2Player.insert(t, player);
+ }
+}
+
diff --git a/libkdegames/kgame/dialogs/kgamedialogconfig.h b/libkdegames/kgame/dialogs/kgamedialogconfig.h
new file mode 100644
index 00000000..b7d30b23
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgamedialogconfig.h
@@ -0,0 +1,362 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// NAMING
+// please follow these naming rules if you add/change classes:
+// the main dialog is named KGameDialog and the base config widget
+// KGameDialogConfig. All config widgets are named KGameDialogXYZConfig (where
+// XYZ = the name of the config widget, like "general" or "network") and are
+// inherited from KGameDialogConfig.
+
+#ifndef __KGAMEDIALOGCONFIG_H__
+#define __KGAMEDIALOGCONFIG_H__
+
+#include <qwidget.h>
+#include <kdemacros.h>
+
+class QGridLayout;
+class QVBoxLayout;
+class QListBoxItem;
+
+class KGame;
+class KPlayer;
+class KGamePropertyBase;
+
+class KGameDialogConfigPrivate;
+/**
+ * Base class for configuration widgets.
+ *
+ * You can inherit from this and implement @ref submitToKGame, @ref
+ * setOwner and @ref setKGame to create your personal @ref KGame configuration widget :-)
+ * @short Base class for configuration widgets
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KDE_EXPORT KGameDialogConfig : public QWidget
+{
+ Q_OBJECT
+public:
+ KGameDialogConfig(QWidget* parent = 0);
+ virtual ~KGameDialogConfig();
+
+ /**
+ * Called by @ref KGameDialog to submit all settings to the KGame
+ * Object.
+ * You have to replace this if you add your own widgets!
+ * @param g A pointer to your KGame.
+ * @param p A pointer to the player owning this dialog
+ **/
+ virtual void submitToKGame(KGame* g, KPlayer* p) = 0;
+
+ /**
+ * The owner player of the dialog has been changed. The default
+ * changes the pointer for owner so don't forget to call the
+ * default implementation if you overwrite this!
+ *
+ * You can use this e.g. to change a line edit widget containing the
+ * player name.
+ *
+ * Note: even NULL players are allowed!
+ * @param p The new owner player of the dialog
+ **/
+ virtual void setOwner(KPlayer* p);
+
+ /**
+ * The KGame object of the dialog has been changed. The default
+ * implementation changes the pointer for game so don't forget to
+ * call the default implementation if you overwrite this!
+ *
+ * You can use this e.g. to re-read the min/max player settings.
+ * @param g The KGame object
+ **/
+ virtual void setKGame(KGame* g);
+
+ /**
+ * The admin status has been changed.
+ * If the KGame object of this config widget is the
+ * admin the user is allowed to configure it. Otherwise most
+ * widgets will have to be disabled. Note that you don't necessarily
+ * need to deactivate all widget - e.g. the player name must be
+ * configured by the player. Mainly the KGame configuration can be done
+ * by the admin only.
+ *
+ * By default this does nothing. Changes the value for admin so
+ * don't forget to call the default implementation in derived classes!
+ * @param admin Whether the KGame object of this dialog can be
+ * configured
+ **/
+ virtual void setAdmin(bool admin);
+
+ /**
+ * A pointer to the KGame object that has been set by @ref setKGame.
+ *
+ * Note that NULL is allowed!
+ * @return The KGame object assigned to this dialog
+ **/
+ KGame* game() const;
+
+ /**
+ * A pointer to the KPlayer object that has been set by @ref
+ * setOwner.
+ *
+ * Note that NULL is allowed!
+ * @return The owner of the dialog
+ **/
+ KPlayer* owner() const;
+
+ /**
+ * @return True if the owner is ADMIN otherwise FALSE. See also
+ * @ref setAdmin
+ **/
+ bool admin() const;
+
+protected:
+
+private:
+ KGameDialogConfigPrivate* d;
+};
+
+/**
+ * The main game configuration widget.
+ *
+ * It currently contains a line edit for the name of the player only. You can
+ * add widgets by using the KGameDialogGeneralConfig as parent parameter as it
+ * uses QLayout::autoAdd == true.
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KGameDialogGeneralConfigPrivate;
+class KGameDialogGeneralConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct a KGameDialogGeneralConfig. Currently it contains a line
+ * edit widget to change the player name only.
+ *
+ * If you just want to add more widgets you can just create your widgets
+ * with the KGameDialogGeneralConfig as parent as it uses
+ * QLayout::setAutoAdd(true).
+ *
+ * @param parent Parent widget for this dialog.
+ * @param initializeGUI If you really don't want to use the
+ * predefined widget and/or layout use FALSE here. Note that then none
+ * of the predefined widgets (currently only the name of the player)
+ * will exist anymore.
+ *
+ **/
+ KGameDialogGeneralConfig(QWidget* parent = 0, bool initializeGUI = true);
+ virtual ~KGameDialogGeneralConfig();
+
+ /**
+ * Called by @ref KGameDialog to submit all settings to the KGame
+ * Object.
+ * You have to replace this if you add your own widgets!
+ * @param g A pointer to your KGame.
+ * @param p A pointer to the player owning this dialog
+ **/
+ virtual void submitToKGame(KGame* g, KPlayer* p);
+
+ /**
+ * Change the owner of the config widget.
+ *
+ * Changes the playername in the line edit
+ * @param p The new owner player
+ **/
+ virtual void setOwner(KPlayer* p);
+
+ /**
+ * See @ref KGameDialogConfig::setKGame
+ *
+ * Sets the default values of all KGame related predefined widgets
+ * (currently none)
+ **/
+ virtual void setKGame(KGame* g);
+
+ /**
+ * See @ref KGameDialogConfig::setAdmin
+ *
+ * This deactivates the min/max player widgets
+ **/
+ virtual void setAdmin(bool admin);
+
+protected slots:
+ void slotPropertyChanged(KGamePropertyBase*, KPlayer*);
+
+protected:
+ void setPlayerName(const QString& name);
+
+ QString playerName() const;
+
+private:
+ KGameDialogGeneralConfigPrivate* d;
+};
+
+class KGameDialogNetworkConfigPrivate;
+class KDE_EXPORT KGameDialogNetworkConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ KGameDialogNetworkConfig(QWidget* parent = 0);
+ virtual ~KGameDialogNetworkConfig();
+
+
+ void disableInitConnection();
+
+ /**
+ * Called by @ref KGameDialog to submit all settings to the KGame
+ * Object.
+ * You have to replace this if you add your own widgets!
+ * @param g A pointer to your KGame.
+ * @param p A pointer to the player owning this dialog
+ **/
+ virtual void submitToKGame(KGame* g, KPlayer* p);
+
+ virtual void setKGame(KGame* g);
+
+ /**
+ * This sets the default port and host used in @ref KGameConnectDialog.
+ * The user will be able to change these defaults!
+ *
+ * If you don't call this then host "localhost" and port "0" is used.
+ * You are strongly encouraged to change at least the port!
+ * @param port The default port to connect to / listen on
+ * @param host The default host to connect to
+ **/
+ void setDefaultNetworkInfo(const QString& host, unsigned short int port,bool server=true);
+
+ /**
+ * Set service type that will be published or browsed for and game name that will be displayed in
+ * server browser. Without this publishing and discovery of LAN servers will not be enabled.
+ * @param name Game name. Important only for server mode. If not
+ * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name.
+ * @param type Service type (something like _kwin4._tcp). It should be unique for application.
+ * @since 3.4
+ **/
+ void setDiscoveryInfo(const QString& type, const QString& name=QString::null);
+
+signals:
+ /**
+ * This signal is emmited if the user changes the server type (client/server)
+ * in the network configuration dialog.
+ *
+ * @param t - type type (0/1) of the connection
+ **/
+ void signalServerTypeChanged(int);
+
+
+protected:
+ void setConnected(bool connected, bool master = false);
+
+protected slots:
+ void slotInitConnection();
+ void slotExitConnection();
+ void slotConnectionBroken();
+
+
+private:
+ KGameDialogNetworkConfigPrivate* d;
+};
+
+class KGameDialogMsgServerConfigPrivate;
+class KGameDialogMsgServerConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ KGameDialogMsgServerConfig(QWidget* parent = 0);
+ virtual ~KGameDialogMsgServerConfig();
+
+ virtual void submitToKGame(KGame*, KPlayer*) {}
+
+ void setHasMsgServer(bool);
+
+ virtual void setKGame(KGame* g);
+ virtual void setAdmin(bool);
+
+protected slots:
+ void slotChangeMaxClients();
+ void slotChangeAdmin();
+ void slotRemoveClient();
+
+protected:
+ void removeClient(Q_UINT32 id);
+
+private:
+ KGameDialogMsgServerConfigPrivate* d;
+};
+
+class KGameDialogChatConfigPrivate;
+/**
+ * This is not really a configuration widget but rather a simple chat widget.
+ * This widget does nothing but just providing a @ref KGameChat object.
+ * @short A chat widget inside a @ref KGameDialog
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KGameDialogChatConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ KGameDialogChatConfig(int chatMsgId, QWidget* parent = 0);
+ virtual ~KGameDialogChatConfig();
+
+ virtual void setKGame(KGame* g);
+ virtual void setOwner(KPlayer* p);
+
+ virtual void submitToKGame(KGame* g, KPlayer* p) { Q_UNUSED(g); Q_UNUSED(p); }
+
+private:
+ KGameDialogChatConfigPrivate* d;
+};
+
+/**
+ * @short Lists all connected players and gives the ability to kick them off the
+ * game
+ **/
+class KGameDialogConnectionConfigPrivate;
+class KGameDialogConnectionConfig : public KGameDialogConfig
+{
+ Q_OBJECT
+public:
+ KGameDialogConnectionConfig(QWidget* parent = 0);
+ virtual ~KGameDialogConnectionConfig();
+
+ virtual void setKGame(KGame* g);
+ virtual void setOwner(KPlayer* p);
+ virtual void setAdmin(bool admin);
+
+ virtual void submitToKGame(KGame* g, KPlayer* p) { Q_UNUSED(g); Q_UNUSED(p); }
+
+protected:
+ /**
+ * @param p A player
+ * @return The QListBoxItem that belongs to the player @p p
+ **/
+ QListBoxItem* item(KPlayer* p) const;
+
+protected slots:
+ void slotKickPlayerOut(QListBoxItem* item);
+ void slotPropertyChanged(KGamePropertyBase* prop, KPlayer* p);
+ void slotPlayerLeftGame(KPlayer* p);
+ void slotPlayerJoinedGame(KPlayer* p);
+ void slotClearPlayers();
+
+private:
+ KGameDialogConnectionConfigPrivate* d;
+
+};
+#endif
diff --git a/libkdegames/kgame/dialogs/kgameerrordialog.cpp b/libkdegames/kgame/dialogs/kgameerrordialog.cpp
new file mode 100644
index 00000000..1750892c
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgameerrordialog.cpp
@@ -0,0 +1,129 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "kgame.h"
+
+#include "kgameerrordialog.h"
+
+class KGameErrorDialogPrivate
+{
+public:
+ KGameErrorDialogPrivate()
+ {
+ mGame = 0;
+ }
+
+ const KGame* mGame;
+};
+
+KGameErrorDialog::KGameErrorDialog(QWidget* parent) : QObject(parent)
+{
+ d = new KGameErrorDialogPrivate;
+}
+
+KGameErrorDialog::~KGameErrorDialog()
+{
+ delete d;
+}
+
+void KGameErrorDialog::setKGame(const KGame* g)
+{
+ slotUnsetKGame();
+ d->mGame = g;
+
+ connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame()));
+
+// the error signals:
+ connect(d->mGame, SIGNAL(signalNetworkErrorMessage(int, QString)),
+ this, SLOT(slotError(int, QString)));
+ connect(d->mGame, SIGNAL(signalConnectionBroken()),
+ this, SLOT(slotServerConnectionLost()));
+ connect(d->mGame, SIGNAL(signalClientDisconnected(Q_UINT32,bool)),
+ this, SLOT(slotClientConnectionLost(Q_UINT32,bool)));
+}
+
+void KGameErrorDialog::slotUnsetKGame()
+{
+ if (d->mGame) {
+ disconnect(d->mGame, 0, this, 0);
+ }
+ d->mGame = 0;
+}
+
+void KGameErrorDialog::error(const QString& errorText, QWidget* parent)
+{ KMessageBox::error(parent, errorText); }
+
+void KGameErrorDialog::slotServerConnectionLost()
+{
+// TODO: add IP/port of the server
+ QString message = i18n("Connection to the server has been lost!");
+ error(message, (QWidget*)parent());
+}
+
+void KGameErrorDialog::slotClientConnectionLost(Q_UINT32 /*id*/,bool)
+{
+//TODO: add IP/port of the client
+ QString message;
+// if (c) {
+// message = i18n("Connection to client has been lost!\nID: %1\nIP: %2").arg(c->id()).arg(c->IP());
+// } else {
+// message = i18n("Connection to client has been lost!");
+// }
+ message = i18n("Connection to client has been lost!");
+ error(message, (QWidget*)parent());
+}
+
+void KGameErrorDialog::slotError(int errorNo, QString text)
+{
+ QString message = i18n("Received a network error!\nError number: %1\nError message: %2").arg(errorNo).arg(text);
+ error(message, (QWidget*)parent());
+}
+
+void KGameErrorDialog::connectionError(QString s)
+{
+ QString message;
+ if (s.isNull()) {
+ message = i18n("No connection could be created.");
+ } else {
+ message = i18n("No connection could be created.\nThe error message was:\n%1").arg(s);
+ }
+ error(message, (QWidget*)parent());
+}
+
+
+
+// should become the real dialog - currently we just use messageboxes
+// -> maybe unused forever
+KGameErrorMessageDialog::KGameErrorMessageDialog(QWidget* parent)
+ : KDialogBase(Plain, i18n("Error"), Ok, Ok, parent, 0, true, true)
+{
+}
+
+KGameErrorMessageDialog::~KGameErrorMessageDialog()
+{
+}
+
+
+
+#include "kgameerrordialog.moc"
diff --git a/libkdegames/kgame/dialogs/kgameerrordialog.h b/libkdegames/kgame/dialogs/kgameerrordialog.h
new file mode 100644
index 00000000..c1dbd1ca
--- /dev/null
+++ b/libkdegames/kgame/dialogs/kgameerrordialog.h
@@ -0,0 +1,113 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEERRORDIALOG_H__
+#define __KGAMEERRORDIALOG_H__
+
+#include <kdialogbase.h>
+
+class KGame;
+class KGameErrorDialogPrivate;
+
+/**
+ * Use error(), warning() and information() to display the information about a
+ * network game. Maybe a better solution is to use KMessageBoxes
+ * You can connect to the public slots, too - they will call the static
+ * functions, so that you can always have a KGameErrorDialog object lying around
+ * without losing much memory (a KGameErrorMessageDialog Object will be
+ * created)
+ * @short Error handling for KGame
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KGameErrorDialog : public QObject
+{
+ Q_OBJECT
+public:
+ KGameErrorDialog(QWidget* parent);
+ ~KGameErrorDialog();
+
+ /**
+ * Automatically connects the KGame object to all error dependant slots.
+ * Create a KGameErrorDialog object, call this function and forget
+ * everything.
+ * @param g The KGame which will emit the erorrs (or not ;-) )
+ **/
+ void setKGame(const KGame* g);
+
+ /**
+ * KGame couldn't establish a connection. Use this if
+ * KGame::initConnection returns false
+ * @param s A string that describes the error further (like port is
+ * already in use). Will be ignored if QString::null
+ **/
+ void connectionError(QString s = QString::null);
+
+public slots:
+ void slotError(int error, QString text);
+
+ /**
+ * The connection to the @ref KMessageServer has been lost
+ *
+ * See @ref KGameNetwork::signalConnectionBroken
+ **/
+ void slotServerConnectionLost();
+
+ /**
+ * The connection to a client has been lost by accident
+ *
+ * See @ref KGameNetwork::signalClientDisconnected
+ **/
+ void slotClientConnectionLost(Q_UINT32 clientID,bool broken);
+
+ /**
+ * Unsets a @ref KGame which has been set using @ref setKGame before.
+ * This is called automatically when the @ref KGame object is destroyed
+ * and you normally don't have to call this yourself.
+ *
+ * Note that @ref setKGame also unsets an already existing @ref KGame
+ * object if exising.
+ **/
+ void slotUnsetKGame();
+
+protected:
+ void error(const QString& errorText, QWidget* parent = 0);
+
+private:
+ KGameErrorDialogPrivate* d;
+};
+
+/**
+ * The real class for error messages. KGameErrorDialog uses this to create error
+ * messages (not yet).
+ * Use @ref KGameErrorDialog instead.
+ * @short Internally used by @ref KGameErrorDialog
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KGameErrorMessageDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ KGameErrorMessageDialog(QWidget* parent);
+ ~KGameErrorMessageDialog();
+
+private:
+};
+
+#endif
diff --git a/libkdegames/kgame/kgame.cpp b/libkdegames/kgame/kgame.cpp
new file mode 100644
index 00000000..101dbfcc
--- /dev/null
+++ b/libkdegames/kgame/kgame.cpp
@@ -0,0 +1,1475 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgame.h"
+#include "kgame.moc"
+#include "kgamepropertyhandler.h"
+#include "kgameproperty.h"
+#include "kplayer.h"
+#include "kgameio.h"
+#include "kgameerror.h"
+#include "kgamesequence.h"
+
+#include "kgamemessage.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <qbuffer.h>
+#include <qtimer.h>
+#include <qptrqueue.h>
+#include <qfile.h>
+
+#include <klocale.h>
+#include <krandomsequence.h>
+#include <kdebug.h>
+
+#define KGAME_LOAD_COOKIE 4210
+
+// try to place as much as possible here
+// many things are *not* possible here as KGame has to use some inline function
+class KGamePrivate
+{
+public:
+ KGamePrivate()
+ {
+ mUniquePlayerNumber = 0;
+ mPolicy=KGame::PolicyLocal;
+ mGameSequence = 0;
+ }
+
+ int mUniquePlayerNumber;
+ QPtrQueue<KPlayer> mAddPlayerList;// this is a list of to-be-added players. See addPlayer() docu
+ KRandomSequence* mRandom;
+ KGame::GamePolicy mPolicy;
+ KGameSequence* mGameSequence;
+
+
+ KGamePropertyHandler* mProperties;
+
+ // player lists
+ KGame::KGamePlayerList mPlayerList;
+ KGame::KGamePlayerList mInactivePlayerList;
+
+ //KGamePropertys
+ KGamePropertyInt mMaxPlayer;
+ KGamePropertyUInt mMinPlayer;
+ KGamePropertyInt mGameStatus; // Game running?
+ QValueList<int> mInactiveIdList;
+
+};
+
+// ------------------- GAME CLASS --------------------------
+KGame::KGame(int cookie,QObject* parent) : KGameNetwork(cookie,parent)
+{
+ kdDebug(11001) << k_funcinfo << " - " << this << ", sizeof(KGame)=" << sizeof(KGame) << endl;
+ d = new KGamePrivate;
+
+ d->mProperties = new KGamePropertyHandler(this);
+
+ d->mProperties->registerHandler(KGameMessage::IdGameProperty,
+ this,SLOT(sendProperty(int, QDataStream&, bool* )),
+ SLOT(emitSignal(KGamePropertyBase *)));
+ d->mMaxPlayer.registerData(KGamePropertyBase::IdMaxPlayer, this, i18n("MaxPlayers"));
+ d->mMaxPlayer.setLocal(-1); // Infinite
+ d->mMinPlayer.registerData(KGamePropertyBase::IdMinPlayer, this, i18n("MinPlayers"));
+ d->mMinPlayer.setLocal(0); // Always ok
+ d->mGameStatus.registerData(KGamePropertyBase::IdGameStatus, this, i18n("GameStatus"));
+ d->mGameStatus.setLocal(Init);
+ // d->mUniquePlayerNumber = 0;
+ d->mRandom = new KRandomSequence;
+ d->mRandom->setSeed(0);
+
+ connect(this, SIGNAL(signalClientConnected(Q_UINT32)),
+ this, SLOT(slotClientConnected(Q_UINT32)));
+ connect(this, SIGNAL(signalClientDisconnected(Q_UINT32,bool)),
+ this, SLOT(slotClientDisconnected(Q_UINT32,bool)));
+ connect(this, SIGNAL(signalConnectionBroken()),
+ this, SLOT(slotServerDisconnected()));
+
+ setGameSequence(new KGameSequence());
+
+ // BL: FIXME This signal does no longer exist. When we are merging
+ // MH: super....and how do I find out about the lost conenction now?
+ // KGame and KGameNetwork, this could be improved!
+// connect(this,SIGNAL(signalConnectionLost(KGameClient *)),
+// this,SLOT(slotConnectionLost(KGameClient *)));
+}
+
+KGame::~KGame()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+// Debug();
+ reset();
+ delete d->mGameSequence;
+ delete d->mRandom;
+ delete d;
+ kdDebug(11001) << k_funcinfo << " done" << endl;
+}
+
+bool KGame::reset()
+{
+ deletePlayers();
+ deleteInactivePlayers();
+ return true;
+}
+
+void KGame::deletePlayers()
+{
+// kdDebug(11001) << k_funcinfo << endl;
+ KGamePlayerList tmp = d->mPlayerList; // in case of PolicyClean player=d->mPlayerList.first() is infinite
+ KPlayer *player;
+ while((player=tmp.first()))
+ {
+ delete player; // delete and removes the player
+ tmp.removeFirst();
+ }
+// kdDebug(11001) << k_funcinfo << " done" << endl;
+}
+
+void KGame::deleteInactivePlayers()
+{
+ KPlayer *player;
+ while((player=d->mInactivePlayerList.first()))
+ {
+ //player->setGame(0); // prevent call backs
+ d->mInactivePlayerList.remove(player);
+ delete player;
+ }
+}
+
+bool KGame::load(QString filename,bool reset)
+{
+ if (filename.isNull())
+ {
+ return false;
+ }
+ QFile f(filename);
+ if (!f.open(IO_ReadOnly))
+ {
+ return false;
+ }
+ QDataStream s( &f );
+ load(s,reset);
+ f.close();
+ return true;
+}
+
+bool KGame::load(QDataStream &stream,bool reset)
+{ return loadgame(stream, false,reset); }
+
+bool KGame::loadgame(QDataStream &stream, bool network,bool resetgame)
+{
+ // Load Game Data
+
+ // internal data
+ Q_INT32 c;
+ stream >> c; // cookie
+
+ if (c!=cookie())
+ {
+ kdWarning(11001) << "Trying to load different game version we="<<cookie() << " saved=" << c << endl;
+ bool result=false;
+ emit signalLoadError(stream,network,(int)c,result);
+ return result;
+ }
+ if (resetgame) reset();
+
+ uint i;
+ stream >> i;
+// setPolicy((GamePolicy)i);
+
+ stream >> d->mUniquePlayerNumber;
+
+ if (gameSequence())
+ {
+ gameSequence()->setCurrentPlayer(0); // TODO !!!
+ }
+ int newseed;
+ stream >> newseed;
+ d->mRandom->setSeed(newseed);
+
+ // Switch off the direct emitting of signals while
+ // loading properties. This can cause inconsistencies
+ // otherwise if a property emits and this emit accesses
+ // a property not yet loaded
+ // Note we habe to have this external locking to prevent the games unlocking
+ // to access the players
+ dataHandler()->lockDirectEmit();
+ KPlayer *player;
+ for ( player=playerList()->first(); player != 0; player=playerList()->next() )
+ {
+ player->dataHandler()->lockDirectEmit();
+ // kdDebug(11001) << "Player "<<player->id() << " to indirect emit" <<endl;
+ }
+
+ // Properties
+ dataHandler()->load(stream);
+
+ // If there is additional data to be loaded before players are loaded then do
+ // this here.
+ emit signalLoadPrePlayers(stream);
+
+ // Load Playerobjects
+ uint playercount;
+ stream >> playercount;
+ kdDebug(11001) << "Loading KGame " << playercount << " KPlayer objects " << endl;
+ for (i=0;i<playercount;i++)
+ {
+ KPlayer *newplayer=loadPlayer(stream,network);
+ systemAddPlayer(newplayer);
+ }
+
+ Q_INT16 cookie;
+ stream >> cookie;
+ if (cookie==KGAME_LOAD_COOKIE) {
+ kdDebug(11001) << " Game loaded propertly"<<endl;
+ } else {
+ kdError(11001) << " Game loading error. probably format error"<<endl;
+ }
+
+ // Switch back on the direct emitting of signals and emit the
+ // queued signals.
+ // Note we habe to have this external locking to prevent the games unlocking
+ // to access the players
+ dataHandler()->unlockDirectEmit();
+ for ( player=playerList()->first(); player != 0; player=playerList()->next() )
+ {
+ player->dataHandler()->unlockDirectEmit();
+ // kdDebug(11001) << "Player "<<player->id() << " to direct emit" <<endl;
+ }
+
+ emit signalLoad(stream);
+ return true;
+}
+
+bool KGame::save(QString filename,bool saveplayers)
+{
+ if (filename.isNull())
+ {
+ return false;
+ }
+ QFile f(filename);
+ if (!f.open(IO_WriteOnly))
+ {
+ return false;
+ }
+ QDataStream s( &f );
+ save(s,saveplayers);
+ f.close();
+ return true;
+}
+
+bool KGame::save(QDataStream &stream,bool saveplayers)
+{ return savegame(stream, false,saveplayers); }
+
+bool KGame::savegame(QDataStream &stream,bool /*network*/,bool saveplayers)
+{
+ // Save Game Data
+
+ // internal variables
+ Q_INT32 c=cookie();
+ stream << c;
+
+ uint p=(uint)policy();
+ stream << p;
+ stream << d->mUniquePlayerNumber;
+ int newseed=(int)d->mRandom->getLong(65535);
+ stream << newseed;
+ d->mRandom->setSeed(newseed);
+
+ // Properties
+ dataHandler()->save(stream);
+
+ // Save all data that need to be saved *before* the players are saved
+ emit signalSavePrePlayers(stream);
+
+ if (saveplayers)
+ {
+ savePlayers(stream,playerList());
+ }
+ else
+ {
+ stream << (uint)0; // no players saved
+ }
+
+ stream << (Q_INT16)KGAME_LOAD_COOKIE;
+
+ emit signalSave(stream);
+ return true;
+}
+
+void KGame::savePlayer(QDataStream &stream,KPlayer* p)
+{
+// this could be in KGameMessage as well
+ stream << (Q_INT32)p->rtti();
+ stream << (Q_INT32)p->id();
+ stream << (Q_INT32)p->calcIOValue();
+ p->save(stream);
+}
+
+void KGame::savePlayers(QDataStream &stream, KGamePlayerList *list)
+{
+ if (!list)
+ {
+ list=playerList();
+ }
+
+ Q_INT32 cnt=list->count();
+ kdDebug(11001) << "Saving KGame " << cnt << " KPlayer objects " << endl;
+ stream << cnt;
+ KPlayer *player;
+ for ( player=list->first(); player != 0; player=list->next() )
+ {
+ savePlayer(stream,player);
+ }
+}
+
+KPlayer *KGame::createPlayer(int /*rtti*/,int /*io*/,bool /*isvirtual*/)
+{
+ kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl;
+ return new KPlayer;
+}
+KPlayer *KGame::loadPlayer(QDataStream& stream,bool isvirtual)
+{
+ Q_INT32 rtti,id,iovalue;
+ stream >> rtti >> id >> iovalue;
+ KPlayer *newplayer=findPlayer(id);
+ if (!newplayer)
+ {
+ kdDebug(11001) << k_funcinfo << "Player "<< id << " not found...asking user to create one " << endl;
+ newplayer=createPlayer(rtti,iovalue,isvirtual);
+ //emit signalCreatePlayer(newplayer,rtti,iovalue,isvirtual,this);
+ }
+ /*
+ if (!newplayer)
+ {
+ kdWarning(11001) << " No user defined player created. Creating default KPlayer. This crashes if you have overwritten KPlayer!!!! " << endl;
+ newplayer=new KPlayer;
+ }
+ else
+ {
+ kdDebug(11001) << " USER Player " << newplayer << " done player->rtti=" << newplayer->rtti() << " rtti=" << rtti << endl;
+ }
+ */
+ newplayer->load(stream);
+ if (isvirtual)
+ {
+ newplayer->setVirtual(true);
+ }
+ return newplayer;
+}
+
+// ----------------- Player handling -----------------------
+
+KPlayer * KGame::findPlayer(Q_UINT32 id) const
+{
+ for (QPtrListIterator<KPlayer> it(d->mPlayerList); it.current(); ++it)
+ {
+ if (it.current()->id() == id)
+ {
+ return it.current();
+ }
+ }
+ for (QPtrListIterator<KPlayer> it(d->mInactivePlayerList); it.current(); ++it)
+ {
+ if (it.current()->id() == id)
+ {
+ return it.current();
+ }
+ }
+ return 0;
+}
+
+// it is necessary that addPlayer and systemAddPlayer are called in the same
+// order. Ie if addPlayer(foo) followed by addPlayer(bar) is called, you must
+// not call systemAddPlayer(bar) followed by systemAddPlayer(foo), as the
+// mAddPlayerList would get confused. Should be no problem as long as comServer
+// and the clients are working correctly.
+// BUT: if addPlayer(foo) does not arrive by any reason while addPlayer(bar)
+// does, we would be in trouble...
+void KGame::addPlayer(KPlayer* newplayer)
+{
+ kdDebug(11001) << k_funcinfo << ": " << "; maxPlayers=" << maxPlayers() << " playerCount=" << playerCount() << endl;
+ if (!newplayer)
+ {
+ kdFatal(11001) << "trying to add NULL player in KGame::addPlayer()" << endl;
+ return ;
+ }
+
+ if (maxPlayers() >= 0 && (int)playerCount() >= maxPlayers())
+ {
+ kdWarning(11001) << "cannot add more than " << maxPlayers() << " players - deleting..." << endl;
+ delete newplayer;
+ return;
+ }
+
+ if (newplayer->id() == 0)
+ {
+ d->mUniquePlayerNumber++;
+ newplayer->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber, gameId()));
+ kdDebug(11001) << k_funcinfo << "NEW!!! player " << newplayer << " now has id " << newplayer->id() << endl;
+ }
+ else
+ {
+ // this could happen in games which use their own ID management by certain
+ // reasons. that is NOT recommended
+ kdDebug(11001) << k_funcinfo << "player " << newplayer << " already has an id: " << newplayer->id() << endl;
+ }
+
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ // We distinguis here what policy we have
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemAddPlayer(newplayer);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ savePlayer(stream,newplayer);
+ // Store the player for delayed clean adding
+ if (policy()==PolicyClean)
+ {
+ d->mAddPlayerList.enqueue(newplayer);
+ }
+ sendSystemMessage(stream,(int)KGameMessage::IdAddPlayer, 0);
+ }
+}
+
+void KGame::systemAddPlayer(KPlayer* newplayer)
+{
+ if (!newplayer)
+ {
+ kdFatal(11001) << "trying to add NULL player in KGame::systemAddPlayer()" << endl;
+ return ;
+ }
+ if (newplayer->id() == 0)
+ {
+ kdWarning(11001) << k_funcinfo << "player " << newplayer << " has no ID" << endl;
+ }
+
+ if (findPlayer(newplayer->id()))
+ {
+ kdError(11001) << "ERROR: Double adding player !!!!! NOT GOOD !!!!!! " << newplayer->id() << "...I delete it again" << endl;
+ delete newplayer;
+ }
+ else
+ {
+ kdDebug(11001) << "Trying to add player " << newplayer <<" maxPlayers="<<maxPlayers()<<" playerCount="<<playerCount() << endl;
+ // Add the player to the game
+ d->mPlayerList.append(newplayer);
+ newplayer->setGame(this);
+ kdDebug(11001) << "Player: isVirtual=" << newplayer->isVirtual() << endl;
+ kdDebug(11001) << " id=" << newplayer->id() << " #Players="
+ << d->mPlayerList.count() << " added " << newplayer
+ << " (virtual=" << newplayer->isVirtual() << ")" << endl;
+ emit signalPlayerJoinedGame(newplayer);
+ }
+}
+
+// Called by the KPlayer destructor
+void KGame::playerDeleted(KPlayer *player)
+{
+ kdDebug(11001) << k_funcinfo << ": id (" << player->id() << ") to be removed " << player << endl;
+
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemRemovePlayer(player,false);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ if (!player->isVirtual())
+ {
+ kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<<player->id() << endl;
+ sendSystemMessage(player->id(), KGameMessage::IdRemovePlayer, 0);
+ }
+ }
+}
+
+bool KGame::removePlayer(KPlayer * player, Q_UINT32 receiver)
+{//transmit to all clients, or to receiver only
+ if (!player)
+ {
+ kdFatal(11001) << "trying to remove NULL player in KGame::removePlayer()" << endl;
+ return false;
+ }
+ kdDebug(11001) << k_funcinfo << ": id (" << player->id() << ") to be removed " << player << endl;
+
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemRemovePlayer(player,true);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ kdDebug(11001) << k_funcinfo << ": sending IdRemovePlayer "<<player->id() << endl;
+ sendSystemMessage(player->id(),KGameMessage::IdRemovePlayer, receiver);
+ }
+ return true;
+ // we will receive the message in networkTransmission()
+}
+
+void KGame::systemRemovePlayer(KPlayer* player,bool deleteit)
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ if (!player)
+ {
+ kdWarning(11001) << "cannot remove NULL player" << endl;
+ return;
+ }
+ if (!systemRemove(player,deleteit))
+ {
+ kdWarning(11001) << "player " << player << "(" << player->id() << ") Could not be found!" << endl;
+ }
+
+ if (gameStatus()==(int)Run && playerCount()<minPlayers())
+ {
+ kdWarning(11001) << k_funcinfo ": not enough players, PAUSING game\n" << endl;
+ setGameStatus(Pause);
+ }
+}
+
+bool KGame::systemRemove(KPlayer* p,bool deleteit)
+{
+ if (!p)
+ {
+ kdWarning(11001) << "cannot remove NULL player" << endl;
+ return false;
+ }
+ bool result;
+ kdDebug(11001) << k_funcinfo << ": Player (" << p->id() << ") to be removed " << p << endl;
+
+ if (d->mPlayerList.count() == 0)
+ {
+ result = false;
+ }
+ else
+ {
+ result = d->mPlayerList.remove(p);
+ }
+
+ emit signalPlayerLeftGame(p);
+
+ p->setGame(0);
+ if (deleteit)
+ {
+ delete p;
+ }
+
+ return result;
+}
+
+bool KGame::inactivatePlayer(KPlayer* player)
+{
+ if (!player)
+ {
+ return false;
+ }
+ kdDebug(11001) << "Inactivate player " << player->id() << endl;
+
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemInactivatePlayer(player);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer);
+ }
+
+ return true;
+}
+
+bool KGame::systemInactivatePlayer(KPlayer* player)
+{
+ if (!player || !player->isActive())
+ {
+ return false;
+ }
+ kdDebug(11001) << " Inactivate player " << player->id() << endl;
+
+ int pid=player->id();
+ // Virtual players cannot be deactivated. They will be removed
+ if (player->isVirtual())
+ {
+ systemRemovePlayer(player,true);
+ }
+ else
+ {
+ d->mPlayerList.remove(player);
+ d->mInactivePlayerList.prepend(player);
+ player->setActive(false);
+ }
+ emit signalPlayerLeftGame(player);
+ if (isAdmin())
+ {
+ d->mInactiveIdList.prepend(pid);
+ }
+ return true;
+}
+
+bool KGame::activatePlayer(KPlayer * player)
+{
+ if (!player)
+ {
+ return false;
+ }
+ kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl;
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ systemActivatePlayer(player);
+ }
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer);
+ }
+ return true;
+}
+
+bool KGame::systemActivatePlayer(KPlayer* player)
+{
+ if (!player || player->isActive())
+ {
+ return false;
+ }
+ kdDebug(11001) << k_funcinfo << ": activate " << player->id() << endl;
+
+ d->mInactivePlayerList.remove(player);
+ player->setActive(true);
+ addPlayer(player);
+ if (isAdmin())
+ {
+ d->mInactiveIdList.remove(player->id());
+ }
+ return true;
+}
+
+// -------------------- Properties ---------------------------
+
+void KGame::setMaxPlayers(uint maxnumber)
+{ if (isAdmin()) { d->mMaxPlayer.changeValue(maxnumber); } }
+
+void KGame::setMinPlayers(uint minnumber)
+{ if (isAdmin()) { d->mMinPlayer.changeValue(minnumber); } }
+
+uint KGame::minPlayers() const
+{ return d->mMinPlayer.value(); }
+
+int KGame::maxPlayers() const
+{ return d->mMaxPlayer.value(); }
+
+uint KGame::playerCount() const
+{ return d->mPlayerList.count(); }
+
+int KGame::gameStatus() const
+{ return d->mGameStatus.value(); }
+
+bool KGame::isRunning() const
+{ return d->mGameStatus.value() == Run; }
+
+KGamePropertyHandler* KGame::dataHandler() const
+{ return d->mProperties; }
+
+
+KGame::KGamePlayerList* KGame::inactivePlayerList()
+{ return &d->mInactivePlayerList; }
+
+const KGame::KGamePlayerList* KGame::inactivePlayerList() const
+{ return &d->mInactivePlayerList; }
+
+KGame::KGamePlayerList* KGame::playerList()
+{ return &d->mPlayerList; }
+
+const KGame::KGamePlayerList* KGame::playerList() const
+{ return &d->mPlayerList; }
+
+KRandomSequence* KGame::random() const
+{ return d->mRandom; }
+
+
+bool KGame::sendPlayerInput(QDataStream &msg, KPlayer *player, Q_UINT32 sender)
+{
+ if (!player)
+ {
+ kdError(11001) << k_funcinfo << ": NULL player" << endl;
+ return false;
+ }
+ if (!isRunning())
+ {
+ kdError(11001) << k_funcinfo << ": game not running" << endl;
+ return false;
+ }
+
+ kdDebug(11001) << k_funcinfo << ": transmitting playerInput over network" << endl;
+ sendSystemMessage(msg, (int)KGameMessage::IdPlayerInput, player->id(), sender);
+ return true;
+}
+
+bool KGame::systemPlayerInput(QDataStream &msg, KPlayer *player, Q_UINT32 sender)
+{
+ if (!player)
+ {
+ kdError(11001) << k_funcinfo << ": NULL player" << endl;
+ return false;
+ }
+ if (!isRunning())
+ {
+ kdError(11001) << k_funcinfo << ": game not running" << endl;
+ return false;
+ }
+ kdDebug(11001) << "KGame: Got playerInput from messageServer... sender: " << sender << endl;
+ if (playerInput(msg,player))
+ {
+ playerInputFinished(player);
+ }
+ else
+ {
+ kdDebug(11001) << k_funcinfo<<": switching off player input"<<endl;
+ // TODO: (MH 03-2003): We need an return option from playerInput so that
+ // the player's is not automatically disabled here
+ if (!player->asyncInput())
+ {
+ player->setTurn(false); // in turn based games we have to switch off input now
+ }
+ }
+ return true;
+}
+
+
+KPlayer * KGame::playerInputFinished(KPlayer *player)
+{
+ kdDebug(11001) << k_funcinfo<<"player input finished for "<<player->id()<<endl;
+ // Check for game over and if not allow the next player to move
+ int gameOver = 0;
+ if (gameSequence())
+ {
+ gameSequence()->setCurrentPlayer(player);
+ }
+ // do not call gameSequence()->checkGameOver() to keep backward compatibility!
+ gameOver = checkGameOver(player);
+ if (gameOver!=0)
+ {
+ if (player)
+ {
+ player->setTurn(false);
+ }
+ setGameStatus(End);
+ emit signalGameOver(gameOver,player,this);
+ }
+ else if (!player->asyncInput())
+ {
+ player->setTurn(false); // in turn based games we have to switch off input now
+ if (gameSequence())
+ {
+ QTimer::singleShot(0,this,SLOT(prepareNext()));
+ }
+ }
+ return player;
+}
+
+// Per default we do not do anything
+int KGame::checkGameOver(KPlayer *player)
+{
+ if (gameSequence())
+ {
+ return gameSequence()->checkGameOver(player);
+ }
+ return 0;
+}
+
+void KGame::setGameSequence(KGameSequence* sequence)
+{
+ delete d->mGameSequence;
+ d->mGameSequence = sequence;
+ if (d->mGameSequence)
+ {
+ d->mGameSequence->setGame(this);
+ }
+}
+
+KGameSequence* KGame::gameSequence() const
+{
+ return d->mGameSequence;
+}
+
+void KGame::prepareNext()
+{
+ if (gameSequence())
+ {
+ // we don't call gameSequence->nextPlayer() to keep old code working
+ nextPlayer(gameSequence()->currentPlayer());
+ }
+}
+
+KPlayer *KGame::nextPlayer(KPlayer *last,bool exclusive)
+{
+ if (gameSequence())
+ {
+ return gameSequence()->nextPlayer(last, exclusive);
+ }
+ return 0;
+}
+
+void KGame::setGameStatus(int status)
+{
+ kdDebug(11001) << k_funcinfo << ": GAMESTATUS CHANGED to" << status << endl;
+ if (status==(int)Run && playerCount()<minPlayers())
+ {
+ kdDebug(11001) << k_funcinfo << ": not enough players, pausing game\n" << endl;
+ status=Pause;
+ }
+ d->mGameStatus = status;
+}
+
+void KGame::networkTransmission(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_UINT32 /*clientID*/)
+{//clientID is unused
+ // message targets a playerobject. If we find it we forward the message to the
+ // player. Otherwise we proceed here and hope the best that the user processes
+ // the message
+
+// kdDebug(11001) << k_funcinfo << ": we="<<(int)gameId()<<" id="<<msgid<<" recv=" << receiver << " sender=" << sender << endl;
+
+
+ // *first* notice the game that something has changed - so no return prevents
+ // this
+ emit signalMessageUpdate(msgid, receiver, sender);
+ if (KGameMessage::isPlayer(receiver))
+ {
+ //kdDebug(11001) << "message id " << msgid << " seems to be for a player ("<<active=p->isActive()<<" recv="<< receiver << endl;
+ KPlayer *p=findPlayer(receiver);
+ if (p && p->isActive())
+ {
+ p->networkTransmission(stream,msgid,sender);
+ return;
+ }
+ if (p)
+ {
+ kdDebug(11001) << "player is here but not active" << endl;
+ }
+ else
+ {
+ kdDebug(11001) << "no player found" << endl;
+ }
+ }
+ // If it is not for a player it is meant for us!!!! Otherwise the
+ // gamenetwork would not have passed the message to us!
+
+ // GameProperties processed
+ if (d->mProperties->processMessage(stream, msgid, sender == gameId()))
+ {
+// kdDebug(11001 ) << "KGame: message taken by property - returning" << endl;
+ return ;
+ }
+
+ switch(msgid)
+ {
+ case KGameMessage::IdSetupGame: // Client: First step in setup game
+ {
+ Q_INT16 v;
+ Q_INT32 c;
+ stream >> v >> c;
+ kdDebug(11001) << " ===================> (Client) " << k_funcinfo << ": Got IdSetupGame ================== " << endl;
+ kdDebug(11001) << "our game id is " << gameId() << " Lib version=" << v << " App Cookie=" << c << endl;
+ // Verify identity of the network partners
+ if (c!=cookie())
+ {
+ kdError(11001) << "IdGameSetup: Negotiate Game: cookie mismatch I'am="<<cookie()<<" master="<<c<<endl;
+ sendError(KGameError::Cookie, KGameError::errCookie(cookie(), c));
+ disconnect(); // disconnect from master
+ }
+ else if (v!=KGameMessage::version())
+ {
+ sendError(KGameError::Version, KGameError::errVersion(v));
+ disconnect(); // disconnect from master
+ }
+ else
+ {
+ setupGame(sender);
+ }
+ kdDebug(11001) << "========== (Client) Setup game done\n";
+ }
+ break;
+ case KGameMessage::IdSetupGameContinue: // Master: second step in game setup
+ {
+ kdDebug(11001) << "=====>(Master) " << k_funcinfo << " - IdSetupGameContinue" << endl;
+ setupGameContinue(stream, sender);
+ }
+ break;
+ case KGameMessage::IdActivatePlayer: // Activate Player
+ {
+ int id;
+ stream >> id;
+ kdDebug(11001) << "Got IdActivatePlayer id=" << id << endl;
+ if (sender!=gameId() || policy()!=PolicyDirty)
+ {
+ systemActivatePlayer(findPlayer(id));
+ }
+ }
+ break;
+ case KGameMessage::IdInactivatePlayer: // Inactivate Player
+ {
+ int id;
+ stream >> id;
+ kdDebug(11001) << "Got IdInactivatePlayer id=" << id << endl;
+ if (sender!=gameId() || policy()!=PolicyDirty)
+ {
+ systemInactivatePlayer(findPlayer(id));
+ }
+ }
+ break;
+ case KGameMessage::IdAddPlayer:
+ {
+ kdDebug(11001) << k_funcinfo << ": Got IdAddPlayer" << endl;
+ if (sender!=gameId() || policy()!=PolicyDirty)
+ {
+ KPlayer *newplayer=0;
+ // We sent the message so the player is already available
+ if (sender==gameId())
+ {
+ kdDebug(11001) << "dequeue previously added player" << endl;
+ newplayer = d->mAddPlayerList.dequeue();
+ }
+ else
+ {
+ newplayer=loadPlayer(stream,true);
+ }
+ systemAddPlayer(newplayer);// the final, local, adding
+ //systemAddPlayer(stream);
+ }
+ }
+ break;
+ case KGameMessage::IdRemovePlayer: // Client should delete player id
+ {
+ int id;
+ stream >> id;
+ kdDebug(11001) << k_funcinfo << ": Got IdRemovePlayer " << id << endl;
+ KPlayer *p=findPlayer(id);
+ if (p)
+ {
+ // Otherwise the player is already removed
+ if (sender!=gameId() || policy()!=PolicyDirty)
+ {
+ systemRemovePlayer(p,true);
+ }
+ }
+ else
+ {
+ kdWarning(11001) << k_funcinfo << "Cannot find player " << id << endl;
+ }
+ }
+ break;
+ case KGameMessage::IdGameLoad:
+ {
+ kdDebug(11001) << "====> (Client) " << k_funcinfo << ": Got IdGameLoad" << endl;
+ loadgame(stream,true,false);
+ }
+ break;
+ case KGameMessage::IdGameSetupDone:
+ {
+ int cid;
+ stream >> cid;
+ kdDebug(11001) << "====> (CLIENT) " << k_funcinfo << ": Got IdGameSetupDone for client "
+ << cid << " we are =" << gameId() << endl;
+ sendSystemMessage(gameId(), KGameMessage::IdGameConnected, 0);
+ }
+ break;
+ case KGameMessage::IdGameConnected:
+ {
+ int cid;
+ stream >> cid;
+ kdDebug(11001) << "====> (ALL) " << k_funcinfo << ": Got IdGameConnected for client "<< cid << " we are =" << gameId() << endl;
+ emit signalClientJoinedGame(cid,this);
+ }
+ break;
+
+ case KGameMessage::IdSyncRandom: // Master forces a new random seed on us
+ {
+ int newseed;
+ stream >> newseed;
+ kdDebug(11001) << "CLIENT: setting random seed to " << newseed << endl;
+ d->mRandom->setSeed(newseed);
+ }
+ break;
+ case KGameMessage::IdDisconnect:
+ {
+ // if we disconnect we *always* start a local game.
+ // this could lead into problems if we just change the message server
+ if (sender != gameId())
+ {
+ kdDebug(11001) << "client " << sender << " leaves game" << endl;
+ return;
+ }
+ kdDebug(11001) << "leaving the game" << endl;
+ // start a new local game
+ // no other client is by default connected to this so this call should be
+ // enough
+ setMaster();
+ }
+ break;
+ default:
+ {
+ if (msgid < KGameMessage::IdUser)
+ {
+ kdError(11001) << "incorrect message id " << msgid << " - emit anyway"
+ << endl;
+ }
+ kdDebug(11001) << k_funcinfo << ": User data msgid " << msgid << endl;
+ emit signalNetworkData(msgid - KGameMessage::IdUser,((QBuffer*)stream.device())->readAll(),receiver,sender);
+ }
+ break;
+ }
+
+}
+
+// called by the IdSetupGameContinue Message - MASTER SIDE
+// Here the master needs to decide which players can take part at the game
+// and which will be deactivated
+void KGame::setupGameContinue(QDataStream& stream, Q_UINT32 sender)
+{
+ KPlayer *player;
+ Q_INT32 cnt;
+ int i;
+ stream >> cnt;
+
+ QValueList<int> inactivateIds;
+
+ KGamePlayerList newPlayerList;
+ newPlayerList.setAutoDelete(true);
+ for (i=0;i<cnt;i++)
+ {
+ player=loadPlayer(stream,true);
+ kdDebug(11001) << " Master got player " << player->id() <<" rawgame=" << KGameMessage::rawGameId(player->id()) << " from sender " << sender << endl;
+ if (KGameMessage::rawGameId(player->id()) != sender)
+ {
+ kdError(11001) << "Client tries to add player with wrong game id - cheat possible" << endl;
+ }
+ else
+ {
+ newPlayerList.append(player);
+ kdDebug(11001) << " newplayerlist appended " << player->id() << endl;
+ }
+ }
+
+ newPlayersJoin(playerList(),&newPlayerList,inactivateIds);
+
+
+ kdDebug(11001) << " Master calculates how many players to activate client has cnt=" << cnt << endl;
+ kdDebug(11001) << " The game has " << playerCount() << " active players" << endl;
+ kdDebug(11001) << " The user deactivated "<< inactivateIds.count() << " player already " << endl;
+ kdDebug(11001) << " MaxPlayers for this game is " << maxPlayers() << endl;
+
+ // Do we have too many players? (After the programmer disabled some?)
+ // MH: We cannot use have player here as it CHANGES in the loop
+ // int havePlayers = cnt+playerCount()-inactivateIds.count();
+ kdDebug(11001) << " havePlayers " << cnt+playerCount()-inactivateIds.count() << endl;
+ while (maxPlayers() > 0 && maxPlayers() < (int)(cnt+playerCount() - inactivateIds.count()))
+ {
+ kdDebug(11001) << " Still to deacticvate "
+ << (int)(cnt+playerCount()-inactivateIds.count())-(int)maxPlayers()
+ << endl;
+ KPlayer *currentPlayer=0;
+ int currentPriority=0x7fff; // MAX_UINT (16bit?) to get the maximum of the list
+ // find lowest network priority which is not yet in the newPlayerList
+ // do this for the new players
+ for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() )
+ {
+ // Already in the list
+ if (inactivateIds.find(player->id())!=inactivateIds.end())
+ {
+ continue;
+ }
+ if (player->networkPriority()<currentPriority)
+ {
+ currentPriority=player->networkPriority();
+ currentPlayer=player;
+ }
+ }
+
+ // find lowest network priority which is not yet in the newPlayerList
+ // Do this for the network players
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ // Already in the list
+ if (inactivateIds.find(player->id())!=inactivateIds.end())
+ {
+ continue;
+ }
+ if (player->networkPriority()<currentPriority)
+ {
+ currentPriority=player->networkPriority();
+ currentPlayer=player;
+ }
+ }
+
+ // add it to inactivateIds
+ if (currentPlayer)
+ {
+ kdDebug(11001) << "Marking player " << currentPlayer->id() << " for inactivation" << endl;
+ inactivateIds.append(currentPlayer->id());
+ }
+ else
+ {
+ kdError(11001) << "Couldn't find a player to dectivate..That is not so good..." << endl;
+ break;
+ }
+ }
+
+ kdDebug(11001) << "Alltogether deactivated " << inactivateIds.count() << " players" << endl;
+
+ QValueList<int>::Iterator it;
+ for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it )
+ {
+ int pid=*it;
+ kdDebug(11001) << " pid=" << pid << endl;
+ }
+
+ // Now deactivate the network players from the inactivateId list
+ //QValueList<int>::Iterator it;
+ for ( it = inactivateIds.begin(); it != inactivateIds.end(); ++it )
+ {
+ int pid=*it;
+ if (KGameMessage::rawGameId(pid) == sender)
+ {
+ continue; // client's player
+ }
+ kdDebug(11001) << " -> the network needs to deactivate " << pid <<endl;
+ player=findPlayer(pid);
+ if (player)
+ {
+ // We have to make REALLY sure that the player is gone. With any policy
+ systemInactivatePlayer(player);
+ if (policy()!=PolicyLocal)
+ {
+ sendSystemMessage(player->id(), KGameMessage::IdInactivatePlayer);
+ }
+ }
+ else
+ {
+ kdError(11001) << " We should deactivate a player, but cannot find it...not good." << endl;
+ }
+ }
+
+ // Now send out the player list which the client can activate
+ for ( player=newPlayerList.first(); player != 0; player=newPlayerList.next() )
+ {
+ kdDebug(11001) << " newplayerlist contains " << player->id() << endl;
+ // Only activate what is not in the list
+ if (inactivateIds.find(player->id())!=inactivateIds.end())
+ {
+ continue;
+ }
+ kdDebug(11001) << " -> the client can ******** reactivate ******** " << player->id() << endl;
+ sendSystemMessage(player->id(), KGameMessage::IdActivatePlayer, sender);
+ }
+
+ // Save the game over the network
+ QByteArray bufferS;
+ QDataStream streamS(bufferS,IO_WriteOnly);
+ // Save game over netowrk and save players
+ savegame(streamS,true,true);
+ sendSystemMessage(streamS,KGameMessage::IdGameLoad,sender);
+
+
+ // Only to the client first , as the client will add players
+ sendSystemMessage(sender, KGameMessage::IdGameSetupDone, sender);
+}
+
+// called by the IdSetupGame Message - CLIENT SIDE
+// Client needs to prepare for network transfer
+void KGame::setupGame(Q_UINT32 sender)
+{
+ QByteArray bufferS;
+ QDataStream streamS(bufferS,IO_WriteOnly);
+
+ // Deactivate all players
+ KGamePlayerList mTmpList(d->mPlayerList); // we need copy otherwise the removal crashes
+ Q_INT32 cnt=mTmpList.count();
+ kdDebug(11001) << "Client: playerlistcount=" << d->mPlayerList.count() << " tmplistcout=" << cnt << endl;
+
+ streamS << cnt;
+
+ QPtrListIterator<KPlayer> it(mTmpList);
+ KPlayer *player;
+ while (it.current())
+ {
+ player=it.current();
+ systemInactivatePlayer(player);
+ // Give the new game id to all players (which are inactivated now)
+ player->setId(KGameMessage::createPlayerId(player->id(),gameId()));
+
+ // Save it for the master to decide what to do
+ savePlayer(streamS,player);
+
+ ++it;
+ --cnt;
+ }
+ if (d->mPlayerList.count() > 0 || cnt!=0)
+ {
+ kdFatal(11001) << "KGame::setupGame(): Player list is not empty! or cnt!=0=" <<cnt << endl;
+ }
+
+ sendSystemMessage(streamS,KGameMessage::IdSetupGameContinue,sender);
+}
+
+// unused by KGame
+void KGame::syncRandom()
+{
+ int newseed=(int)d->mRandom->getLong(65535);
+ sendSystemMessage(newseed,KGameMessage::IdSyncRandom); // Broadcast
+ d->mRandom->setSeed(newseed);
+}
+
+void KGame::Debug()
+{
+ KGameNetwork::Debug();
+ kdDebug(11001) << "------------------- KGAME -------------------------" << endl;
+ kdDebug(11001) << "this: " << this << endl;
+ kdDebug(11001) << "uniquePlayer " << d->mUniquePlayerNumber << endl;
+ kdDebug(11001) << "gameStatus " << gameStatus() << endl;
+ kdDebug(11001) << "MaxPlayers : " << maxPlayers() << endl;
+ kdDebug(11001) << "NoOfPlayers : " << playerCount() << endl;
+ kdDebug(11001) << "NoOfInactive: " << d->mInactivePlayerList.count() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+void KGame::slotClientConnected(Q_UINT32 clientID)
+{
+ if (isAdmin())
+ {
+ negotiateNetworkGame(clientID);
+ }
+}
+
+void KGame::slotServerDisconnected() // Client side
+{
+ kdDebug(11001) << "======= SERVER DISCONNECT ======="<<endl;
+ kdDebug(11001) << "+++ (CLIENT)++++++++" << k_funcinfo << ": our GameID="<<gameId() << endl;
+
+ int oldgamestatus=gameStatus();
+
+ KPlayer *player;
+ KGamePlayerList removeList;
+ kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl;
+ kdDebug(11001) << "Inactive Playerlist of client=" << d->mInactivePlayerList.count() << " count" << endl;
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ // TODO: CHECK: id=0, could not connect to server in the first place??
+ if (KGameMessage::rawGameId(player->id()) != gameId() && gameId()!=0)
+ {
+ kdDebug(11001) << "Player " << player->id() << " belongs to a removed game" << endl;
+ removeList.append(player);
+ }
+ }
+
+ for ( player=removeList.first(); player != 0; player=removeList.next() )
+ {
+ bool remove = true;
+ emit signalReplacePlayerIO(player, &remove);
+ if (remove)
+ {
+ kdDebug(11001) << " ---> Removing player " << player->id() << endl;
+ systemRemovePlayer(player,true); // no network necessary
+ }
+ }
+
+ setMaster();
+ kdDebug(11001) << " our game id is after setMaster " << gameId() << endl;
+
+ KGamePlayerList mReList(d->mInactivePlayerList);
+ for ( player=mReList.first(); player != 0; player=mReList.next() )
+ {
+ // TODO ?check for priority? Sequence should be ok
+ if ((int)playerCount()<maxPlayers() || maxPlayers()<0)
+ {
+ systemActivatePlayer(player);
+ }
+ }
+ kdDebug(11001) << " Players activated player-cnt=" << playerCount() << endl;
+
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ int oldid=player->id();
+ d->mUniquePlayerNumber++;
+ player->setId(KGameMessage::createPlayerId(d->mUniquePlayerNumber,gameId()));
+ kdDebug(11001) << "Player id " << oldid <<" changed to " << player->id() << " as we are now local" << endl;
+ }
+ // TODO clear inactive lists ?
+ Debug();
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ player->Debug();
+ }
+ kdDebug(11001) << "+++++++++++" << k_funcinfo << " DONE=" << endl;
+ emit signalClientLeftGame(0,oldgamestatus,this);
+}
+
+void KGame::slotClientDisconnected(Q_UINT32 clientID,bool /*broken*/) // server side
+{
+ kdDebug(11001) << "++++(SERVER)+++++++" << k_funcinfo << " clientId=" << clientID << endl;
+
+ int oldgamestatus=gameStatus();
+
+ KPlayer *player;
+ KGamePlayerList removeList;
+ kdDebug(11001) << "Playerlist of client=" << d->mPlayerList.count() << " count" << endl;
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ if (KGameMessage::rawGameId(player->id())==clientID)
+ {
+ kdDebug(11001) << "Player " << player->id() << " belongs to the removed game" << endl;
+ removeList.append(player);
+ }
+ }
+
+ for ( player=removeList.first(); player != 0; player=removeList.next() )
+ {
+ // try to replace the KGameIO first
+ bool remove = true;
+ emit signalReplacePlayerIO(player, &remove);
+ if (remove) {
+ // otherwise (no new KGameIO) remove the player
+ kdDebug(11001) << " ---> Removing player " << player->id() << endl;
+ removePlayer(player,0);
+ }
+ }
+
+ // Now add inactive players - sequence should be ok
+ // TODO remove players from removed game
+ for (unsigned int idx=0;idx<d->mInactiveIdList.count();idx++)
+ {
+ QValueList<int>::Iterator it1 = d->mInactiveIdList.at(idx);
+ player = findPlayer(*it1);
+ if (((int)playerCount() < maxPlayers() || maxPlayers() < 0) && player && KGameMessage::rawGameId(*it1) != clientID)
+ {
+ activatePlayer(player);
+ }
+ }
+ emit signalClientLeftGame(clientID,oldgamestatus,this);
+}
+
+
+// -------------------- Synchronisation -----------------------
+
+// this initializes a newly connected client.
+// we send the number of players (including type) as well as game status and
+// properties to the client. After the initialization has been completed both
+// clients should have the same status (ie players, properties, etc)
+void KGame::negotiateNetworkGame(Q_UINT32 clientID)
+{
+ kdDebug(11001) << "===========================" << k_funcinfo << ": clientID=" << clientID << " =========================== "<< endl;
+ if (!isAdmin())
+ {
+ kdError(11001) << k_funcinfo << ": Serious WARNING..only gameAdmin should call this" << endl;
+ return ;
+ }
+
+ QByteArray buffer;
+ QDataStream streamGS(buffer,IO_WriteOnly);
+
+ // write Game setup specific data
+ //streamGS << (Q_INT32)maxPlayers();
+ //streamGS << (Q_INT32)minPlayers();
+
+ // send to the newly connected client *only*
+ Q_INT16 v=KGameMessage::version();
+ Q_INT32 c=cookie();
+ streamGS << v << c;
+ sendSystemMessage(streamGS, KGameMessage::IdSetupGame, clientID);
+}
+
+bool KGame::sendGroupMessage(const QByteArray &msg, int msgid, Q_UINT32 sender, const QString& group)
+{
+// AB: group must not be i18n'ed!! we should better use an id for group and use
+// a groupName() for the name // FIXME
+ KPlayer *player;
+ for ( player=d->mPlayerList.first(); player != 0; player=d->mPlayerList.next() )
+ {
+ if (player && player->group()==group)
+ {
+ sendMessage(msg,msgid,player->id(), sender);
+ }
+ }
+ return true;
+}
+
+bool KGame::sendGroupMessage(const QDataStream &msg, int msgid, Q_UINT32 sender, const QString& group)
+{ return sendGroupMessage(((QBuffer*)msg.device())->buffer(), msgid, sender, group); }
+
+bool KGame::sendGroupMessage(const QString& msg, int msgid, Q_UINT32 sender, const QString& group)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer, IO_WriteOnly);
+ stream << msg;
+ return sendGroupMessage(stream, msgid, sender, group);
+}
+
+bool KGame::addProperty(KGamePropertyBase* data)
+{ return dataHandler()->addProperty(data); }
+
+bool KGame::sendPlayerProperty(int msgid, QDataStream& s, Q_UINT32 playerId)
+{ return sendSystemMessage(s, msgid, playerId); }
+
+void KGame::sendProperty(int msgid, QDataStream& stream, bool* sent)
+{
+ bool s = sendSystemMessage(stream, msgid);
+ if (s)
+ {
+ *sent = true;
+ }
+}
+
+void KGame::emitSignal(KGamePropertyBase *me)
+{
+ emit signalPropertyChanged(me,this);
+}
+
+KGamePropertyBase* KGame::findProperty(int id) const
+{ return d->mProperties->find(id); }
+
+KGame::GamePolicy KGame::policy() const
+{
+ return d->mPolicy;
+}
+void KGame::setPolicy(GamePolicy p,bool recursive)
+{
+ // Set KGame policy
+ d->mPolicy=p;
+ if (recursive)
+ {
+ // Set all KGame property policy
+ dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
+
+ // Set all KPLayer (active or inactive) property policy
+ for (QPtrListIterator<KPlayer> it(d->mPlayerList); it.current(); ++it)
+ {
+ it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
+ }
+ for (QPtrListIterator<KPlayer> it(d->mInactivePlayerList); it.current(); ++it)
+ {
+ it.current()->dataHandler()->setPolicy((KGamePropertyBase::PropertyPolicy)p,false);
+ }
+ }
+}
+
+/*
+ * vim: et sw=2
+ */
diff --git a/libkdegames/kgame/kgame.h b/libkdegames/kgame/kgame.h
new file mode 100644
index 00000000..37d8d974
--- /dev/null
+++ b/libkdegames/kgame/kgame.h
@@ -0,0 +1,932 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAME_H_
+#define __KGAME_H_
+
+#include <qstring.h>
+#include <qptrlist.h>
+#include <qvaluelist.h>
+
+#include "kgamenetwork.h"
+#include <kdemacros.h>
+class KRandomSequence;
+
+class KPlayer;
+class KGamePropertyBase;
+class KGamePropertyHandler;
+class KGameSequence;
+
+class KGamePrivate;
+
+/**
+ * @short The main KDE game object
+ *
+ * The KGame class is the central game object. A game basically
+ * consists of following features:
+ * - Player handling (add, remove,...)
+ * - Game status (end,start,pause,...)
+ * - load/save
+ * - Move (and message) handling
+ * - nextPlayer and gameOver()
+ * - Network connection (for KGameNetwork)
+ *
+ * Example:
+ * \code
+ * KGame *game=new KGame;
+ * \endcode
+ *
+ *
+ * @author Martin Heni <[email protected]>
+ *
+ */
+class KDE_EXPORT KGame : public KGameNetwork
+{
+ Q_OBJECT
+
+public:
+ typedef QPtrList<KPlayer> KGamePlayerList;
+
+ /**
+ * The policy of the property. This can be PolicyClean (setVale uses
+ * send), PolicyDirty (setValue uses changeValue) or
+ * PolicyLocal (setValue uses setLocal).
+ *
+ * A "clean" policy means that the property is always the same on every
+ * client. This is achieved by calling send which actually changes
+ * the value only when the message from the MessageServer is received.
+ *
+ * A "dirty" policy means that as soon as setValue is called the
+ * property is changed immediately. And additionally sent over network.
+ * This can sometimes lead to bugs as the other clients do not
+ * immediately have the same value. For more information see
+ * changeValue.
+ *
+ * PolicyLocal means that a KGameProperty behaves like ever
+ * "normal" variable. Whenever setValue is called (e.g. using "=")
+ * the value of the property is changes immediately without sending it
+ * over network. You might want to use this if you are sure that all
+ * clients set the property at the same time.
+ **/
+ enum GamePolicy
+ {
+ PolicyUndefined = 0,
+ PolicyClean = 1,
+ PolicyDirty = 2,
+ PolicyLocal = 3
+ };
+
+ /**
+ * Create a KGame object. The cookie is used to identify your
+ * game in load/save and network operations. Change this between
+ * games.
+ */
+ KGame(int cookie=42,QObject* parent=0);
+
+ /**
+ * Destructs the game
+ */
+ virtual ~KGame();
+
+ /**
+ * Gives debug output of the game status
+ */
+ virtual void Debug();
+
+ /**
+ * Game status - Use this to Control the game flow.
+ * The KGame e.g. sets the status to Pause when you have
+ * less player than the minimum amount
+ */
+ enum GameStatus
+ {
+ Init = 0,
+ Run = 1,
+ Pause = 2,
+ End = 3,
+ Abort = 4,
+ SystemPause = 5,
+ Intro = 6,
+ UserStatus = 7
+ };
+
+ // Properties
+ /**
+ * Returns a list of all active players
+ *
+ * @return the list of players
+ */
+ KGamePlayerList *playerList();
+
+ /**
+ * The same as @ref playerList but returns a const pointer.
+ **/
+ const KGamePlayerList *playerList() const;
+
+ /**
+ * Returns a list of all inactive players
+ * @return the list of players
+ */
+ KGamePlayerList *inactivePlayerList();
+
+ /**
+ * The same as @ref inactivePlayerList but returns a const pointer.
+ **/
+ const KGamePlayerList *inactivePlayerList() const;
+
+ /**
+ * Returns a pointer to the game's KRandomSequence. This sequence is
+ * identical for all network players!
+ * @return KRandomSequence pointer
+ */
+ KRandomSequence *random() const;
+
+ /**
+ * @return The KGameSequence object that is currently in use.
+ * @see setGameSequence
+ **/
+ KGameSequence *gameSequence() const;
+
+ /**
+ * Is the game running
+ * @return true/false
+ */
+ bool isRunning() const;
+
+ // Player handling
+ /**
+ * Returns the player object for a given player id
+ * @param id Player id
+ * @return player object
+ */
+ KPlayer *findPlayer(Q_UINT32 id) const;
+
+ /**
+ * Set a new @ref KGameSequence to control player management. By default
+ * KGame uses a normal @ref KGameSequence object. You might want to subclass
+ * that and provide your own object.
+ *
+ * The previous sequence will get deleted.
+ * @param sequence The new game sequence object. KGame takes ownership and
+ * will delete it on destruction!
+ **/
+ void setGameSequence(KGameSequence* sequence);
+
+ /**
+ * Note that KPlayer::save must be implemented properly, as well as
+ * KPlayer::rtti
+ * This will only send a message to all clients. The player is _not_ added
+ * directly!
+ * See also playerInput which will be called as soon as the
+ * player really has been added.
+ *
+ * Note that an added player will first get into a "queue" and won't be in
+ * the game. It will be added to the game as soon as systemAddPlayer is
+ * called what will happen as soon as IdAddPlayer is received.
+ *
+ * Note: you probably want to connect to signalPlayerJoinedGame for
+ * further initialization!
+ * @param newplayer The player you want to add. KGame will send a message to
+ * all clients and add the player using systemAddPlayer
+ **/
+ void addPlayer(KPlayer* newplayer);
+
+ /**
+ * Sends a message over the network, msgid=IdRemovePlayer.
+ *
+ * As soon as this message is received by networkTransmission
+ * systemRemovePlayer is called and the player is removed.
+ **/
+ //AB: TODO: make sendMessage to return if the message will be able to be
+ //sent, eg if a socket is connected, etc. If sendMessage returns false
+ //remove the player directly using systemRemovePlayer
+ bool removePlayer(KPlayer * player) { return removePlayer(player, 0); }
+
+ /**
+ * Called by the destructor of KPlayer to remove itself from the game
+ *
+ **/
+ void playerDeleted(KPlayer * player);
+
+ /**
+ * sends activate player: internal use only?
+ */
+ bool activatePlayer(KPlayer *player);
+
+ /**
+ * sends inactivate player: internal use only?
+ */
+ bool inactivatePlayer(KPlayer *player);
+
+ /**
+ * Set the maximal number of players. After this is
+ * reached no more players can be added. You must be ADMIN to call this (@see
+ * isAdmin).
+ * @param maxnumber maximal number of players
+ */
+ void setMaxPlayers(uint maxnumber);
+
+ /**
+ * What is the maximal number of players?
+ * @return maximal number of players
+ */
+ int maxPlayers() const;
+
+ /**
+ * Set the minimal number of players. A game can not be started
+ * with less player resp. is paused when already running. You must be ADMIN
+ * to call this (see @ref isAdmin)!
+ * @param minnumber minimal number of players
+ */
+ void setMinPlayers(uint minnumber);
+
+ /**
+ * What is the minimal number of players?
+ * @return minimal number of players
+ */
+ uint minPlayers() const;
+
+ /**
+ * Returns how many players are plugged into the game
+ * @return number of players
+ */
+ uint playerCount() const;
+
+ /**
+ * @deprecated
+ * Use @ref KGameSequence::nextPlayer instead
+ **/
+ virtual KPlayer * nextPlayer(KPlayer *last,bool exclusive=true);
+
+ // Input events
+ /**
+ * Called by KPlayer to send a player input to the
+ * KMessageServer.
+ **/
+ virtual bool sendPlayerInput(QDataStream &msg,KPlayer *player,Q_UINT32 sender=0);
+
+ /**
+ * Called when a player input arrives from KMessageServer.
+ *
+ * Calls prepareNext (using QTimer::singleShot) if gameOver()
+ * returns 0. This function should normally not be used outside KGame.
+ * It could be made non-virtual,protected in a later version. At the
+ * moment it is a virtual function to give you more control over KGame.
+ *
+ * For documentation see playerInput.
+ **/
+ virtual bool systemPlayerInput(QDataStream &msg,KPlayer *player,Q_UINT32 sender=0);
+
+ /**
+ * This virtual function is called if the KGame needs to create a new player.
+ * This happens only over a network and with load/save. Doing nothing
+ * will create a default KPlayer. If you want to have your own player
+ * you have to create one with the given rtti here.
+ * Note: If your game uses a player class derived from KPlayer you MUST
+ * override this function and create your player here. Otherwise the
+ * game will crash.
+ * Example:
+ * \code
+ * KPlayer *MyGame::createPlayer(int rtti,int io,bool isvirtual)
+ * {
+ * KPlayer *player=new MyPlayer;
+ * if (!isvirtual) // network player ?
+ * {
+ * // Define something like this to add the IO modules
+ * createIO(player,(KGameIO::IOMode)io);
+ * }
+ * return player;
+ * }
+ * \endcode
+ *
+ * @param rtti is the type of the player (0 means default KPlayer)
+ * @param io is the 'or'ed rtti of the KGameIO's
+ * @param isvirtual true if player is virtual
+ */
+ virtual KPlayer *createPlayer(int rtti,int io,bool isvirtual);
+
+ // load/save
+ /**
+ * Load a saved game, from file OR network. This function has
+ * to be overwritten or you need to connect to the load signal
+ * if you have game data other than KGameProperty.
+ * For file load you should reset() the game before any load attempt
+ * to make sure you load into an clear state.
+ *
+ * @param stream a data stream where you can stream the game from
+ * @param reset - shall the game be reset before loading
+ *
+ * @return true?
+ */
+ virtual bool load(QDataStream &stream,bool reset=true);
+
+ /**
+ * Same as above function but with different parameters
+ *
+ * @param filename - the filename of the file to be opened
+ * @param reset - shall the game be reset before loading
+ *
+ * @return true?
+ **/
+ virtual bool load(QString filename,bool reset=true);
+
+ /**
+ * Save a game to a file OR to network. Otherwise the same as
+ * the load function
+ *
+ * @param stream a data stream to load the game from
+ * @param saveplayers If true then all players wil be saved too
+ *
+ * @return true?
+ */
+ virtual bool save(QDataStream &stream,bool saveplayers=true);
+
+ /**
+ * Same as above function but with different parameters
+ *
+ * @param filename the filename of the file to be saved
+ * @param saveplayers If true then all players wil be saved too
+ *
+ * @return true?
+ **/
+ virtual bool save(QString filename,bool saveplayers=true);
+
+ /**
+ * Resets the game, i.e. puts it into a state where everything
+ * can be started from, e.g. a load game
+ * Right now it does only need to delete all players
+ *
+ * @return true on success
+ */
+ virtual bool reset();
+
+
+ // Game sequence
+ /**
+ * returns the game status, ie running,pause,ended,...
+ *
+ * @return game status
+ */
+ int gameStatus() const;
+
+ /**
+ * sets the game status
+ *
+ * @param status the new status
+ */
+ void setGameStatus(int status);
+
+ /**
+ * docu: see KPlayer
+ **/
+ bool addProperty(KGamePropertyBase* data);
+
+ /**
+ * This is called by KPlayer::sendProperty only! Internal function!
+ **/
+ bool sendPlayerProperty(int msgid, QDataStream& s, Q_UINT32 playerId);
+
+ /**
+ * This function allows to find the pointer to a player
+ * property when you know it's id
+ */
+ KGamePropertyBase* findProperty(int id) const;
+
+ /**
+ * Changes the consistency policy of a property. The
+ * GamePolicy is one of PolicyClean (default), PolicyDirty or PolicyLocal.
+ *
+ * It is up to you to decide how you want to work.
+ **/
+ void setPolicy(GamePolicy p,bool recursive=true);
+
+ /**
+ * @return The default policy of the property
+ **/
+ GamePolicy policy() const;
+
+ /**
+ * See KGameNetwork::sendMessage
+ *
+ * Send a network message msg with a given message ID msgid to all players of
+ * a given group (see KPlayer::group)
+ * @param msg the message which will be send. See messages.txt for contents
+ * @param msgid an id for this message
+ * @param sender the id of the sender
+ * @param group the group of the receivers
+ * @return true if worked
+ */
+ bool sendGroupMessage(const QByteArray& msg, int msgid, Q_UINT32 sender, const QString& group);
+ bool sendGroupMessage(const QDataStream &msg, int msgid, Q_UINT32 sender, const QString& group);
+ bool sendGroupMessage(int msg, int msgid, Q_UINT32 sender, const QString& group);
+ bool sendGroupMessage(const QString& msg, int msgid, Q_UINT32 sender, const QString& group);
+
+ /**
+ * This will either forward an incoming message to a specified player
+ * (see KPlayer::networkTransmission) or
+ * handle the message directly (e.g. if msgif==IdRemovePlayer it will remove
+ * the (in the stream) specified player). If both is not possible (i.e. the
+ * message is user specified data) the signal signalNetworkData is
+ * emitted.
+ *
+ * This emits signalMessageUpdate <em>before</em> doing anything with
+ * the message. You can use this signal when you want to be notified about
+ * an update/change.
+ * @param msgid Specifies the kind of the message. See messages.txt for
+ * further information
+ * @param stream The message that is being sent
+ * @param receiver The is of the player this message is for. 0 For broadcast.
+ * @param sender
+ * @param clientID the client from which we received the transmission - hardly used
+ **/
+ virtual void networkTransmission(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender, Q_UINT32 clientID);
+
+ /**
+ * Returns a pointer to the KGame property handler
+ **/
+ KGamePropertyHandler* dataHandler() const;
+
+protected slots:
+ /**
+ * Called by KGamePropertyHandler only! Internal function!
+ **/
+ void sendProperty(int msgid, QDataStream& stream, bool* sent);
+
+ /**
+ * Called by KGamePropertyHandler only! Internal function!
+ **/
+ void emitSignal(KGamePropertyBase *me);
+
+ /**
+ * @deprecated
+ * Use KGameSequence::prepareNext() instead
+ **/
+ virtual void prepareNext();
+
+
+ /**
+ * Calls negotiateNetworkGame()
+ * See KGameNetwork::signalClientConnected
+ **/
+ void slotClientConnected(Q_UINT32 clientId);
+
+ /**
+ * This slot is called whenever the connection to a client is lost (ie the
+ * signal KGameNetwork::signalClientDisconnected is emitted) and will remove
+ * the players from that client.
+ * @param clientId The client the connection has been lost to
+ * @param broken (ignore this - not used)
+ **/
+ void slotClientDisconnected(Q_UINT32 clientId,bool broken);
+
+ /**
+ * This slot is called whenever the connection to the server is lost (ie the
+ * signal KGameNetwork::signalConnectionBroken is emitted) and will
+ * switch to local game mode
+ **/
+ void slotServerDisconnected();
+
+signals:
+ /**
+ * When a client disconnects from the game usually all players from that
+ * client are removed. But if you use completely the KGame structure you
+ * probably don't want this. You just want to replace the KGameIO of the
+ * (human) player by a computer KGameIO. So this player continues game but
+ * is from this point on controlled by the computer.
+ *
+ * You achieve this by connecting to this signal. It is emitted as soon as a
+ * client disconnects on <em>all</em> other clients. Make sure to add a new
+ * KGameIO only once! you might want to use @ref isAdmin for this. If you
+ * added a new KGameIO set *remove=false otherwise the player is completely
+ * removed.
+ * @param player The player that is about to be removed. Add your new
+ * KGameIO here - but only on <em>one</em> client!
+ * @param remove Set this to FALSE if you don't want this player to be
+ * removed completely.
+ **/
+ void signalReplacePlayerIO(KPlayer* player, bool* remove);
+
+ /**
+ * The game will be loaded from the given stream. Load from here
+ * the data which is NOT a game or player property.
+ * It is not necessary to use this signal for a full property game.
+ *
+ * This signal is emitted <em>before</em> the players are loaded by
+ * KGame. See also signalLoad
+ *
+ * You must load <em>exactly</em> the same data from the stream that you have saved
+ * in signalSavePrePlayers. Otherwise player loading will not work
+ * anymore.
+ *
+ * @param stream the load stream
+ */
+ void signalLoadPrePlayers(QDataStream &stream);
+
+ /**
+ * The game will be loaded from the given stream. Load from here
+ * the data which is NOT a game or player property.
+ * It is not necessary to use this signal for a full property game.
+ *
+ * @param stream the load stream
+ */
+ void signalLoad(QDataStream &stream);
+
+ /**
+ * The game will be saved to the given stream. Fill this with data
+ * which is NOT a game or player property.
+ * It is not necessary to use this signal for a full property game.
+ *
+ * This signal is emitted <em>before</em> the players are saved by
+ * KGame. See also signalSave
+ *
+ * If you can choose between signalSavePrePlayers and signalSave then
+ * better use signalSave
+ *
+ * @param stream the save stream
+ **/
+ void signalSavePrePlayers(QDataStream &stream);
+
+ /**
+ * The game will be saved to the given stream. Fill this with data
+ * which is NOT a game or player property.
+ * It is not necessary to use this signal for a full property game.
+ *
+ * @param stream the save stream
+ */
+ void signalSave(QDataStream &stream);
+
+ /**
+ * Is emmited if a game with a different version cookie is loaded.
+ * Normally this should result in an error. But maybe you do support
+ * loading of older game versions. Here would be a good place to do a
+ * conversion.
+ *
+ * @param stream - the load stream
+ * @param network - true if this is a network connect. False for load game
+ * @param cookie - the saved cookie. It differs from KGame::cookie()
+ * @param result - set this to true if you managed to load the game
+ */
+ void signalLoadError(QDataStream &stream,bool network,int cookie, bool &result);
+
+ /**
+ * We got an user defined update message. This is usually done
+ * by a sendData in a inherited KGame Object which defines its
+ * own methods and has to syncronise them over the network.
+ * Reaction to this is usually a call to a KGame function.
+ */
+ void signalNetworkData(int msgid,const QByteArray& buffer, Q_UINT32 receiver, Q_UINT32 sender);
+
+ /**
+ * We got an network message. this can be used to notify us that something
+ * changed. What changed can be seen in the message id. Whether this is
+ * the best possible method to do this is unclear...
+ */
+ void signalMessageUpdate(int msgid,Q_UINT32 receiver,Q_UINT32 sender);
+
+ /**
+ * a player left the game because of a broken connection or so!
+ *
+ * Note that when this signal is emitted the player is not part of @ref
+ * playerList anymore but the pointer is still valid. You should do some
+ * final cleanups here since the player is usually deleted after the signal
+ * is emitted.
+ *
+ * @param player the player who left the game
+ */
+ void signalPlayerLeftGame(KPlayer *player);
+
+ /**
+ * a player joined the game
+ *
+ * @param player the player who joined the game
+ */
+ void signalPlayerJoinedGame(KPlayer *player);
+
+
+ /**
+ * This signal is emmited if a player property changes its value and
+ * the property is set to notify this change
+ */
+ void signalPropertyChanged(KGamePropertyBase *property, KGame *me);
+
+ /**
+ * Is emitted after a call to gameOver() returns a non zero
+ * return code. This code is forwarded to this signal as 'status'.
+ *
+ * @param status the return code of gameOver()
+ * @param current the player who did the last move
+ * @param me a pointer to the KGame object
+ */
+ void signalGameOver(int status, KPlayer *current, KGame *me);
+
+ /**
+ * Is emmited after a client is successfully connected to the game.
+ * The client id is the id of the new game client. An easy way to
+ * check whether that's us is
+ * \code
+ * if (clientid==gameid()) .. // we joined
+ * else ... // someone joined the game
+ * \endcode
+ * @param clientid - The id of the new client
+ * @param me - our game pointer
+ */
+ void signalClientJoinedGame(Q_UINT32 clientid,KGame *me);
+
+ /**
+ * This signal is emitted after a network partner left the
+ * game (either by a broken connection or voluntarily).
+ * All changes to the network players have already be done.
+ * If there are not enough players left, the game might have
+ * been paused. To check this you get the old gamestatus
+ * before the disconnection as argument here. The id of the
+ * client who left the game allows to distinguish who left the
+ * game. If it is 0, the server disconnected and you were a client
+ * which has been switched back to local play.
+ * You can use this signal to, e.g. set some menues back to local
+ * player when they were network before.
+ *
+ * @param clientID - 0:server left, otherwise the client who left
+ * @param oldgamestatus - the gamestatus before the loss
+ * @param me - our game pointer
+ **/
+ void signalClientLeftGame(int clientID,int oldgamestatus,KGame *me);
+
+
+protected:
+ /**
+ * A player input occurred. This is the most important function
+ * as the given message will contain the current move made by
+ * the given player.
+ * Note that you HAVE to overwrite this function. Otherwise your
+ * game makes no sense at all.
+ * Generally you have to return TRUE in this function. Only then
+ * the game sequence is proceeded by calling @ref playerInputFinished
+ * which in turn will check for game over or the next player
+ * However, if you have a delayed move, because you e.g. move a
+ * card or a piece you want to return FALSE to pause the game sequence
+ * and then manually call @ref playerInputFinished to resume it.
+ * Example:
+ * \code
+ * bool MyClass::playerInput(QDataStream &msg,KPlayer *player)
+ * {
+ * Q_INT32 move;
+ * msg >> move;
+ * kdDebug() << " Player " << player->id() << " moved to " << move <<
+ * endl;
+ * return true;
+ * }
+ * \endcode
+ *
+ * @param msg the move message
+ * @param player the player who did the move
+ * @return true - input ready, false: input manual
+ */
+ virtual bool playerInput(QDataStream &msg,KPlayer *player)=0;
+
+
+ /**
+ * Called after the player input is processed by the game. Here the
+ * checks for game over and nextPlayer (in the case of turn base games)
+ * are processed.
+ * Call this manually if you have a delayed move, i.e. your playerInput
+ * function returns FALSE. If it returns true you need not do anything
+ * here.
+ *
+ * @return the current player
+ *
+ **/
+ KPlayer *playerInputFinished(KPlayer *player);
+
+
+ /**
+ * This virtual function can be overwritten for your own player management.
+ * It is called when a new game connects to an existing network game or
+ * to the network master. In case you do not want all players of both games
+ * to be present in the new network game, you can deactivate players here.
+ * This is of particular importance if you have a game with fixed number of
+ * player like e.g. chess. A network connect needs to disable one player of
+ * each game to make sense.
+ *
+ * Not overwriting this function will activate a default behaviour which
+ * will deactivate players until the @ref maxPlayers() numebr is reached
+ * according to the KPlayer::networkPriority() value. Players with a low
+ * value will be kicked out first. With equal priority players of the new
+ * client will leave first. This means, not setting this value and not
+ * overwriting this function will never allow a chess game to add client
+ * players!!!
+ * On the other hand setting one player of each game to a networkPriorty of
+ * say 10, already does most of the work for you.
+ *
+ * The parameters of this function are the playerlist of the network game,
+ * which is @ref playerList(). The second argument is the player list of
+ * the new client who wants to join and the third argument serves as return
+ * parameter. All <em>player ID's</em> which are written into this list
+ * will be <em>removed</em> from the created game. You do this by an
+ * \code
+ * inactivate.append(player->id());
+ * \endcode
+ *
+ * @param oldplayer - the list of the network players
+ * @param newplayer - the list of the client players
+ * @param inactivate - the value list of ids to be deactivated
+ *
+ **/
+ virtual void newPlayersJoin(KGamePlayerList *oldplayer,
+ KGamePlayerList *newplayer,
+ QValueList<int> &inactivate) {
+ Q_UNUSED( oldplayer );
+ Q_UNUSED( newplayer );
+ Q_UNUSED( inactivate );
+ }
+
+ /**
+ * Save the player list to a stream. Used for network game and load/save.
+ * Can be overwritten if you know what you are doing
+ *
+ * @param stream is the stream to save the player ot
+ * @param list the optional list is the player list to be saved, default is playerList()
+ *
+ **/
+ void savePlayers(QDataStream &stream,KGamePlayerList *list=0);
+
+ /**
+ * Prepare a player for being added. Put all data about a player into the
+ * stream so that it can be sent to the KGameCommunicationServer using
+ * addPlayer (e.g.)
+ *
+ * This function ensures that the code for adding a player is the same in
+ * addPlayer as well as in negotiateNetworkGame
+ * @param stream is the stream to add the player
+ * @param player The player to add
+ **/
+ void savePlayer(QDataStream& stream,KPlayer* player);
+
+ /**
+ * Load the player list from a stream. Used for network game and load/save.
+ * Can be overwritten if you know what you are doing
+ *
+ * @param stream is the stream to save the player to
+ * @param isvirtual will set the virtual flag true/false
+ *
+ **/
+ KPlayer *loadPlayer(QDataStream& stream,bool isvirtual=false);
+
+
+ /**
+ * inactivates player. Use @ref inactivatePlayer instead!
+ */
+ bool systemInactivatePlayer(KPlayer *player);
+
+ /**
+ * activates player. Use @ref activatePlayer instead!
+ */
+ bool systemActivatePlayer(KPlayer *player);
+
+ /**
+ * Adds a player to the game
+ *
+ * Use @ref addPlayer to send @ref KGameMessage::IdAddPlayer. As soon as
+ * this Id is received this function is called, where the player (see @ref
+ * KPlayer::rtti) is added as well as its properties (see @ref KPlayer::save
+ * and @ref KPlayer::load)
+ *
+ * This method calls the overloaded @ref systemAddPlayer with the created
+ * player as argument. That method will really add the player.
+ * If you need to do some changes to your newly added player just connect to
+ * @ref signalPlayerJoinedGame
+ */
+
+ /**
+ * Finally adds a player to the game and therefore to the list.
+ **/
+ void systemAddPlayer(KPlayer* newplayer);
+
+ /**
+ * Removes a player from the game
+ *
+ * Use removePlayer to send KGameMessage::IdRemovePlayer. As soon
+ * as this Id is received systemRemovePlayer is called and the player is
+ * removed directly.
+ **/
+ void systemRemovePlayer(KPlayer* player,bool deleteit);
+
+ /**
+ * This member function will transmit e.g. all players to that client, as well as
+ * all properties of these players (at least if they have been added by
+ * @ref KPlayer::addProperty) so that the client will finally have the same
+ * status as the master. You want to overwrite this function if you expand
+ * KGame by any properties which have to be known by all clients.
+ *
+ * Only the ADMIN is allowed to call this.
+ * @param clientID The ID of the message client which has connected
+ **/
+ virtual void negotiateNetworkGame(Q_UINT32 clientID);
+
+ /**
+ * syncronise the random numbers with all network clients
+ * not used by KGame - if it should be kept then as public method
+ */
+ void syncRandom();
+
+ void deletePlayers();
+ void deleteInactivePlayers();
+
+ /**
+ * @deprecated
+ * Use @ref KGameSequence instead.
+ *
+ * @param player the player who made the last move
+ * @return anything else but 0 is considered as game over
+ */
+ virtual int checkGameOver(KPlayer *player);
+
+ /**
+ * Load a saved game, from file OR network. Internal.
+ * Warning: loadgame must not rely that all players all already
+ * activated. Actually the network will activate a player AFTER
+ * the loadgame only. This is not true anymore. But be careful
+ * anyway.
+ *
+ * @param stream a data stream where you can stream the game from
+ * @param network is it a network call -> make players virtual
+ * @param reset shall the game be reset before loading
+ *
+ * @return true?
+ */
+ virtual bool loadgame(QDataStream &stream, bool network, bool reset);
+
+ /**
+ * Save a game, to file OR network. Internal.
+ *
+ * @param stream a data stream where you can stream the game from
+ * @param network is it a call from the network or from a file (unused but informative)
+ * @param saveplayers shall the players be saved too (should be TRUE)
+ *
+ * @return true?
+ */
+ virtual bool savegame(QDataStream &stream, bool network,bool saveplayers);
+
+private:
+ //AB: this is to hide the "receiver" parameter from the user. It shouldn't be
+ //used if possible (except for init).
+ /**
+ * This is an overloaded function. Id differs from the public one only in
+ * its parameters:
+ *
+ * @param receiver The Client that will receive the message. You will hardly
+ * ever need this. It it internally used to initialize a newly connected
+ * client.
+ **/
+ //void addPlayer(KPlayer* newplayer, Q_UINT32 receiver);
+
+ /**
+ * Just the same as the public one except receiver:
+ * @param receiver 0 for broadcast, otherwise the receiver. Should only be
+ * used in special circumstances and not outside KGame.
+ **/
+ bool removePlayer(KPlayer * player, Q_UINT32 receiver);
+
+ /**
+ * Helping function - game negotiation
+ **/
+ void setupGame(Q_UINT32 sender);
+
+ /**
+ * Helping function - game negotiation
+ **/
+ void setupGameContinue(QDataStream& msg, Q_UINT32 sender);
+
+ /**
+ * Removes a player from all lists, removes the @ref KGame pointer from the
+ * @ref KPlayer and deletes the player. Used by (e.g.) @ref
+ * systemRemovePlayer
+ * @return True if the player has been removed, false if the current is not
+ * found
+ **/
+ bool systemRemove(KPlayer* player,bool deleteit);
+
+
+private:
+ KGamePrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamechat.cpp b/libkdegames/kgame/kgamechat.cpp
new file mode 100644
index 00000000..16ec7c18
--- /dev/null
+++ b/libkdegames/kgame/kgamechat.cpp
@@ -0,0 +1,341 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2002 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kgamechat.h"
+#include "kgamechat.moc"
+
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgameproperty.h"
+#include "kgamemessage.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qmap.h>
+#include <qintdict.h>
+
+//FIXME:
+#define FIRST_ID 2 // first id, that is free of use, aka not defined above
+
+class KGameChatPrivate
+{
+public:
+ KGameChatPrivate()
+ {
+ mFromPlayer = 0;
+ mGame = 0;
+
+ mToMyGroup = -1;
+ }
+
+ KGame* mGame;
+ KPlayer* mFromPlayer;
+ int mMessageId;
+
+
+ QIntDict<KPlayer> mIndex2Player;
+
+ QMap<int, int> mSendId2PlayerId;
+ int mToMyGroup; // just as the above - but for the group, not for players
+};
+
+KGameChat::KGameChat(KGame* g, int msgid, QWidget* parent) : KChatBase(parent)
+{
+ init(g, msgid);
+}
+
+KGameChat::KGameChat(KGame* g, int msgid, KPlayer* fromPlayer, QWidget* parent) : KChatBase(parent)
+{
+ init(g, msgid);
+ setFromPlayer(fromPlayer);
+}
+
+KGameChat::KGameChat(QWidget* parent) : KChatBase(parent)
+{
+ init(0, -1);
+}
+
+KGameChat::~KGameChat()
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ delete d;
+}
+
+void KGameChat::init(KGame* g, int msgId)
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ d = new KGameChatPrivate;
+ setMessageId(msgId);
+
+ setKGame(g);
+}
+
+void KGameChat::addMessage(int fromId, const QString& text)
+{
+ if (!d->mGame) {
+ kdWarning(11001) << "no KGame object has been set" << endl;
+ addMessage(i18n("Player %1").arg(fromId), text);
+ } else {
+ KPlayer* p = d->mGame->findPlayer(fromId);
+ if (p) {
+ kdDebug(11001) << "adding message of player " << p->name() << "id=" << fromId << endl;
+ addMessage(p->name(), text);
+ } else {
+ kdWarning(11001) << "Could not find player id " << fromId << endl;
+ addMessage(i18n("Unknown"), text);
+ }
+ }
+}
+
+void KGameChat::returnPressed(const QString& text)
+{
+ if (!d->mFromPlayer) {
+ kdWarning(11001) << k_funcinfo << ": You must set a player first!" << endl;
+ return;
+ }
+ if (!d->mGame) {
+ kdWarning(11001) << k_funcinfo << ": You must set a game first!" << endl;
+ return;
+ }
+
+ kdDebug(11001) << "from: " << d->mFromPlayer->id() << "==" << d->mFromPlayer->name() << endl;
+
+ int id = sendingEntry();
+
+ if (isToGroupMessage(id)) {
+ // note: there is currently no support for other groups than the players
+ // group! It might be useful to send to other groups, too
+ QString group = d->mFromPlayer->group();
+ kdDebug(11001) << "send to group " << group << endl;
+ int sender = d->mFromPlayer->id();
+ d->mGame->sendGroupMessage(text, messageId(), sender, group);
+
+ //TODO
+ //AB: this message is never received!! we need to connect to
+ //KPlayer::networkData!!!
+ //TODO
+
+ } else {
+ int toPlayer = 0;
+ if (!isSendToAllMessage(id) && isToPlayerMessage(id)) {
+ toPlayer = playerId(id);
+ if (toPlayer == -1) {
+ kdError(11001) << k_funcinfo << ": don't know that player "
+ << "- internal ERROR" << endl;
+ }
+ }
+ int receiver = toPlayer;
+ int sender = d->mFromPlayer->id();
+ d->mGame->sendMessage(text, messageId(), receiver, sender);
+ }
+}
+
+void KGameChat::setMessageId(int msgid)
+{ d->mMessageId = msgid; }
+
+int KGameChat::messageId() const
+{ return d->mMessageId; }
+
+bool KGameChat::isSendToAllMessage(int id) const
+{ return (id == KChatBase::SendToAll); }
+
+bool KGameChat::isToGroupMessage(int id) const
+{ return (id == d->mToMyGroup); }
+
+bool KGameChat::isToPlayerMessage(int id) const
+{
+return d->mSendId2PlayerId.contains(id); }
+
+QString KGameChat::sendToPlayerEntry(const QString& name) const
+{ return i18n("Send to %1").arg(name); }
+
+int KGameChat::playerId(int id) const
+{
+ if (!isToPlayerMessage(id)) {
+ return -1;
+ }
+
+ return d->mSendId2PlayerId[id];
+}
+
+int KGameChat::sendingId(int playerId) const
+{
+ QMap<int, int>::Iterator it;
+ for (it = d->mSendId2PlayerId.begin(); it != d->mSendId2PlayerId.end(); ++it) {
+ if (it.data() == playerId) {
+ return it.key();
+ }
+ }
+ return -1;
+}
+
+const QString& KGameChat::fromName() const
+{ return d->mFromPlayer ? d->mFromPlayer->name() : QString::null; }
+
+bool KGameChat::hasPlayer(int id) const
+{
+ return (sendingId(id) != -1);
+}
+
+void KGameChat::setFromPlayer(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": NULL player" << endl;
+ removeSendingEntry(d->mToMyGroup);
+ d->mFromPlayer = 0;
+ return;
+ }
+ if (d->mFromPlayer) {
+ changeSendingEntry(p->group(), d->mToMyGroup);
+ } else {
+ if (d->mToMyGroup != -1) {
+ kdWarning(11001) << "send to my group exists already - removing" << endl;
+ removeSendingEntry(d->mToMyGroup);
+ }
+ d->mToMyGroup = nextId();
+ addSendingEntry(i18n("Send to My Group (\"%1\")").arg(p->group()), d->mToMyGroup);
+ }
+ d->mFromPlayer = p;
+ kdDebug(11001) << k_funcinfo << " player=" << p << endl;
+}
+
+
+void KGameChat::setKGame(KGame* g)
+{
+ if (d->mGame) {
+ slotUnsetKGame();
+ }
+ kdDebug(11001) << k_funcinfo << " game=" << g << endl;
+ d->mGame = g;
+
+ if (d->mGame) {
+ connect(d->mGame, SIGNAL(signalPlayerJoinedGame(KPlayer*)),
+ this, SLOT(slotAddPlayer(KPlayer*)));
+ connect(d->mGame, SIGNAL(signalPlayerLeftGame(KPlayer*)),
+ this, SLOT(slotRemovePlayer(KPlayer*)));
+ connect(d->mGame, SIGNAL(signalNetworkData(int, const QByteArray&, Q_UINT32, Q_UINT32)),
+ this, SLOT(slotReceiveMessage(int, const QByteArray&, Q_UINT32, Q_UINT32)));
+ connect(d->mGame, SIGNAL(destroyed()), this, SLOT(slotUnsetKGame()));
+
+ QPtrList<KPlayer> playerList = *d->mGame->playerList();
+ for (int unsigned i = 0; i < playerList.count(); i++) {
+ slotAddPlayer(playerList.at(i));
+ }
+ }
+}
+
+KGame* KGameChat::game() const
+{
+ return d->mGame;
+}
+
+KPlayer* KGameChat::fromPlayer() const
+{
+ return d->mFromPlayer;
+}
+
+void KGameChat::slotUnsetKGame()
+{
+//TODO: test this method!
+
+ if (!d->mGame) {
+ return;
+ }
+ disconnect(d->mGame, 0, this, 0);
+ removeSendingEntry(d->mToMyGroup);
+ QMap<int, int>::Iterator it;
+ for (it = d->mSendId2PlayerId.begin(); it != d->mSendId2PlayerId.end(); ++it) {
+ removeSendingEntry(it.data());
+ }
+}
+
+void KGameChat::slotAddPlayer(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": cannot add NULL player" << endl;
+ return;
+ }
+ if (hasPlayer(p->id())) {
+ kdError(11001) << k_funcinfo << ": player was added before" << endl;
+ return;
+ }
+
+ int sendingId = nextId();
+ addSendingEntry(comboBoxItem(p->name()), sendingId);
+ d->mSendId2PlayerId.insert(sendingId, p->id());
+ connect(p, SIGNAL(signalPropertyChanged(KGamePropertyBase*, KPlayer*)),
+ this, SLOT(slotPropertyChanged(KGamePropertyBase*, KPlayer*)));
+ connect(p, SIGNAL(signalNetworkData(int, const QByteArray&, Q_UINT32, KPlayer*)),
+ this, SLOT(slotReceivePrivateMessage(int, const QByteArray&, Q_UINT32, KPlayer*)));
+}
+
+void KGameChat::slotRemovePlayer(KPlayer* p)
+{
+ if (!p) {
+ kdError(11001) << k_funcinfo << ": NULL player" << endl;
+ return;
+ }
+ if (!hasPlayer(p->id())) {
+ kdError(11001) << k_funcinfo << ": cannot remove non-existent player" << endl;
+ return;
+ }
+
+ int id = sendingId(p->id());
+ removeSendingEntry(id);
+ p->disconnect(this);
+ d->mSendId2PlayerId.remove(id);
+}
+
+void KGameChat::slotPropertyChanged(KGamePropertyBase* prop, KPlayer* player)
+{
+ if (prop->id() == KGamePropertyBase::IdName) {
+// kdDebug(11001) << "new Name" << endl;
+ changeSendingEntry(player->name(), sendingId(player->id()));
+/*
+ mCombo->changeItem(comboBoxItem(player->name()), index);
+ */
+ } else if (prop->id() == KGamePropertyBase::IdGroup) {
+ //TODO
+ }
+}
+
+void KGameChat::slotReceivePrivateMessage(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer* me)
+{
+ if (!me || me != fromPlayer()) {
+ kdDebug() << k_funcinfo << "nope - not for us!" << endl;
+ return;
+ }
+ slotReceiveMessage(msgid, buffer, me->id(), sender);
+}
+
+void KGameChat::slotReceiveMessage(int msgid, const QByteArray& buffer, Q_UINT32 , Q_UINT32 sender)
+{
+ QDataStream msg(buffer, IO_ReadOnly);
+ if (msgid != messageId()) {
+ return;
+ }
+
+ QString text;
+ msg >> text;
+
+ addMessage(sender, text);
+}
+
diff --git a/libkdegames/kgame/kgamechat.h b/libkdegames/kgame/kgamechat.h
new file mode 100644
index 00000000..6f7ea65d
--- /dev/null
+++ b/libkdegames/kgame/kgamechat.h
@@ -0,0 +1,223 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001-2002 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMECHAT_H__
+#define __KGAMECHAT_H__
+
+#include <qstring.h>
+
+#include "kchatbase.h"
+#include <kdemacros.h>
+class KPlayer;
+class KGame;
+class KGamePropertyBase;
+
+class KGameChatPrivate;
+
+/**
+ * @short A Chat widget for KGame-based games
+ *
+ * Call @ref setFromPlayer() first - this will be used as the "from" part of
+ * every message you will send. Otherwise it won't work! You can also use the
+ * fromPlayer parameter in the constructor though...
+ *
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KDE_EXPORT KGameChat : public KChatBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct a @ref KGame chat widget on @p game that used @p msgid for
+ * the chat message. The @p fromPlayer is the local player (see @ref
+ * setFromPlayer).
+ **/
+ KGameChat(KGame* game, int msgid, KPlayer* fromPlayer, QWidget * parent);
+
+ /**
+ * @overload
+ * To make use of this widget you need to call @ref setFromPlayer
+ * manually.
+ **/
+ KGameChat(KGame* game, int msgId, QWidget* parent);
+
+ /**
+ * @overload
+ * This constructs a widget that is not usable. You must call at least
+ * setGame, setFromPlayer and setMessageId manually.
+ * @since 3.2
+ **/
+ KGameChat(QWidget* parent);
+
+ virtual ~KGameChat();
+
+ enum SendingIds {
+ SendToGroup = 1
+ };
+
+ /**
+ * This sets the fromPlayer to @p player. The fromPlayer is the
+ * player that will appear as "from" when you send messages through this
+ * widget.
+ * @param player The player of this widget
+ **/
+ void setFromPlayer(KPlayer* player);
+
+ KPlayer* fromPlayer() const;
+
+ /**
+ * Set the @ref KGame object for this chat widget. All messages will be
+ * sent through this object. You don't have to implement any send
+ * functions, just call this function, call @ref setFromPlayer and be
+ * done :-)
+ * @param g The @ref KGame object the messages will be sent through
+ **/
+ void setKGame(KGame* g);
+
+ KGame* game() const;
+
+ /**
+ * @return The id of the messages produced by KGameChat. The id will be
+ * used in @ref KGame as parameter msgid in the method @ref KGame::sendMessage
+ **/
+ int messageId() const;
+
+ /**
+ * Change the message id of the chat widget. It is recommended that you
+ * don't use this but prefer the constructor instead, but in certain
+ * situations (such as using this widget in Qt designer) it may be
+ * useful to change the message id.
+ *
+ * See also @ref messageId
+ * @since 3.2
+ **/
+ void setMessageId(int msgid);
+
+ /**
+ * reimplemented from @ref KChatBase
+ * @return @ref KPlayer::name() for the player set by @ref setFromPlayer
+ **/
+ virtual const QString& fromName() const;
+
+
+public slots:
+ virtual void addMessage(const QString& fromName, const QString& text) { KChatBase::addMessage(fromName, text);}
+ virtual void addMessage(int fromId, const QString& text);
+
+ void slotReceiveMessage(int, const QByteArray&, Q_UINT32 receiver, Q_UINT32 sender);
+
+protected:
+ /**
+ * @param id The ID of the sending entry, as returned by @ref
+ * KChatBase::sendingEntry
+ * @return True if the entry "send to all" was selected, otherwise false
+ **/
+ bool isSendToAllMessage(int id) const;
+
+ /**
+ * Used to indicate whether a message shall be sent to a group of
+ * players. Note that this was not yet implemented when this doc was
+ * written so this description might be wrong. (FIXME)
+ * @param id The ID of the sending entry, as returned by @ref
+ * KChatBase::sendingEntry
+ * @return True if the message is meant to be sent to a group (see @ref
+ * KPlayer::group), e.g. if "send to my group" was selected.
+ **/
+ bool isToGroupMessage(int id) const;
+
+
+ /**
+ * Used to indicate whether the message shall be sent to a single player
+ * only. Note that you can also call @ref isSendToAllMessage and @ref
+ * isToGroupMessage - if both return false it must be a player message.
+ * This behaviour might be changed later - so don't depend on it.
+ *
+ * See also toPlayerId
+ * @param id The ID of the sending entry, as returned by
+ * KChatBase::sendingEntry
+ * @return True if the message shall be sent to a special player,
+ * otherwise false.
+ **/
+ bool isToPlayerMessage(int id) const;
+
+ /**
+ * @param id The ID of the sending entry, as returned by
+ * KChatBase::sendingEntry
+ * @return The ID of the player (see KPlayer::id) the sending entry
+ * belongs to. Note that the parameter id is an id as returned by ref
+ * KChatBase::sendingEntry and the id this method returns is a
+ * KPlayer ID. If isToPlayerMessage returns false this method
+ * returns -1
+ **/
+ int playerId(int id) const;
+
+ /**
+ * @param playerId The ID of the KPlayer object
+ * @return The ID of the sending entry (see KChatBase) or -1 if
+ * the player id was not found.
+ **/
+ int sendingId(int playerId) const;
+
+ /**
+ * @return True if the player with this ID was added before (see
+ * slotAddPlayer)
+ **/
+ bool hasPlayer(int id) const;
+
+ /**
+ * @param name The name of the added player
+ * @return A string that will be added as sending entry in @ref
+ * KChatBase. By default this is "send to name" where name is the name
+ * that you specify. See also KChatBase::addSendingEntry
+ **/
+ virtual QString sendToPlayerEntry(const QString& name) const;
+
+
+protected slots:
+ /**
+ * Unsets a KGame object that has been set using setKGame
+ * before. You don't have to call this - this is usually done
+ * automatically.
+ **/
+ void slotUnsetKGame();
+
+
+ void slotPropertyChanged(KGamePropertyBase*, KPlayer*);
+ void slotAddPlayer(KPlayer*);
+ void slotRemovePlayer(KPlayer*);
+
+ /**
+ * Called when KPlayer::signalNetworkData is emitted. The message
+ * gets forwarded to slotReceiveMessage if @p me equals
+ * fromPlayer.
+ **/
+ void slotReceivePrivateMessage(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer* me);
+
+protected:
+ virtual void returnPressed(const QString& text);
+
+private:
+ void init(KGame* g, int msgid);
+
+private:
+ KGameChatPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kgameerror.cpp b/libkdegames/kgame/kgameerror.cpp
new file mode 100644
index 00000000..93f40f93
--- /dev/null
+++ b/libkdegames/kgame/kgameerror.cpp
@@ -0,0 +1,80 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgameerror.h"
+#include "kgamemessage.h"
+
+#include <klocale.h>
+
+QByteArray KGameError::errVersion(int remoteVersion)
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ s << (Q_INT32)KGameMessage::version();
+ s << (Q_INT32)remoteVersion;
+ return b;
+}
+
+QByteArray KGameError::errCookie(int localCookie, int remoteCookie)
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ s << (Q_INT32)localCookie;
+ s << (Q_INT32)remoteCookie;
+ return b;
+}
+
+QString KGameError::errorText(int errorCode, const QByteArray& message)
+{
+ QDataStream s(message, IO_ReadOnly);
+ return errorText(errorCode, s);
+}
+
+QString KGameError::errorText(int errorCode, QDataStream& s)
+{
+ QString text;
+ switch (errorCode) {
+ case Cookie:
+ {
+ Q_INT32 cookie1;
+ Q_INT32 cookie2;
+ s >> cookie1;
+ s >> cookie2;
+ text = i18n("Cookie mismatch!\nExpected Cookie: %1\nReceived Cookie: %2").arg(cookie1).arg(cookie2);
+ break;
+ }
+ case Version:
+ {
+ Q_INT32 version1;
+ Q_INT32 version2;
+ s >> version1;
+ s >> version2;
+ text = i18n("KGame Version mismatch!\nExpected Version: %1\nReceived Version: %2\n").arg(version1).arg(version2);
+ break;
+ }
+ default:
+ text = i18n("Unknown error code %1").arg(errorCode);
+ }
+ return text;
+}
+
diff --git a/libkdegames/kgame/kgameerror.h b/libkdegames/kgame/kgameerror.h
new file mode 100644
index 00000000..2916e891
--- /dev/null
+++ b/libkdegames/kgame/kgameerror.h
@@ -0,0 +1,59 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEERROR_H_
+#define __KGAMEERROR_H_
+
+#include <qstring.h>
+
+
+class KGameError
+{
+public:
+ KGameError() { }
+ ~KGameError() { }
+
+ enum ErrorCodes {
+ Cookie = 0, // Cookie mismatch
+ Version = 1 // Version mismatch
+ };
+
+ /**
+ * Generate an error message with Erorr Code = ErrCookie
+ **/
+ static QByteArray errCookie(int localCookie, int remoteCookie);
+ static QByteArray errVersion(int remoteVersion);
+
+ /**
+ * Create an erorr text using a QDataStream (QByteArray) which was
+ * created using @ref KGameError. This is the opposite function to all
+ * the errXYZ() function (e.g. @ref errVersion).
+ * You want to use this to generate the message that shall be
+ * displayed to the user.
+ * @return an error message
+ **/
+ static QString errorText(int errorCode, QDataStream& message);
+ static QString errorText(int errorCode, const QByteArray& message);
+
+};
+
+#endif
diff --git a/libkdegames/kgame/kgameio.cpp b/libkdegames/kgame/kgameio.cpp
new file mode 100644
index 00000000..9b3a55e8
--- /dev/null
+++ b/libkdegames/kgame/kgameio.cpp
@@ -0,0 +1,539 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgameio.h"
+#include "kgameio.moc"
+#include "kgame.h"
+#include "kplayer.h"
+#include "kgamemessage.h"
+#include "kmessageio.h"
+
+#include <kdebug.h>
+
+#include <qwidget.h>
+#include <qbuffer.h>
+#include <qtimer.h>
+
+#include <stdlib.h>
+
+// ----------------------- Generic IO -------------------------
+KGameIO::KGameIO() : QObject(0,0)
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)" << sizeof(KGameIO) << endl;
+ mPlayer = 0;
+}
+
+KGameIO::KGameIO(KPlayer* player) : QObject(0,0)
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)" << sizeof(KGameIO) << endl;
+ mPlayer = 0;
+ if (player)
+ {
+ player->addGameIO(this);
+ }
+}
+
+KGameIO::~KGameIO()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ // unregister ourselves
+ if (player())
+ {
+ player()->removeGameIO(this, false);
+ }
+}
+
+void KGameIO::initIO(KPlayer *p)
+{
+ setPlayer(p);
+}
+
+void KGameIO::notifyTurn(bool b)
+{
+ if (!player())
+ {
+ kdWarning(11001) << k_funcinfo << ": player() is NULL" << endl;
+ return;
+ }
+ bool sendit=false;
+ QByteArray buffer;
+ QDataStream stream(buffer, IO_WriteOnly);
+ emit signalPrepareTurn(stream, b, this, &sendit);
+ if (sendit)
+ {
+ QDataStream ostream(buffer,IO_ReadOnly);
+ Q_UINT32 sender = player()->id(); // force correct sender
+ kdDebug(11001) << "Prepare turn sendInput" << endl;
+ sendInput(ostream, true, sender);
+ }
+}
+
+KGame* KGameIO::game() const
+{
+ if (!player())
+ {
+ return 0;
+ }
+ return player()->game();
+}
+
+bool KGameIO::sendInput(QDataStream& s, bool transmit, Q_UINT32 sender)
+{
+ if (!player())
+ {
+ return false;
+ }
+ return player()->forwardInput(s, transmit, sender);
+}
+
+void KGameIO::Debug()
+{
+ kdDebug(11001) << "------------------- KGAMEINPUT --------------------" << endl;
+ kdDebug(11001) << "this: " << this << endl;
+ kdDebug(11001) << "rtti : " << rtti() << endl;
+ kdDebug(11001) << "Player: " << player() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+
+// ----------------------- Key IO ---------------------------
+KGameKeyIO::KGameKeyIO(QWidget *parent)
+ : KGameIO()
+{
+ if (parent)
+ {
+ kdDebug(11001) << "Key Event filter installed" << endl;
+ parent->installEventFilter(this);
+ }
+}
+
+KGameKeyIO::~KGameKeyIO()
+{
+ if (parent())
+ {
+ parent()->removeEventFilter(this);
+ }
+}
+
+int KGameKeyIO::rtti() const { return KeyIO; }
+
+bool KGameKeyIO::eventFilter( QObject *o, QEvent *e )
+{
+ if (!player())
+ {
+ return false;
+ }
+
+ // key press/release
+ if ( e->type() == QEvent::KeyPress ||
+ e->type() == QEvent::KeyRelease )
+ {
+ QKeyEvent *k = (QKeyEvent*)e;
+ // kdDebug(11001) << "KGameKeyIO " << this << " key press/release " << k->key() << endl ;
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ bool eatevent=false;
+ emit signalKeyEvent(this,stream,k,&eatevent);
+ QDataStream msg(buffer,IO_ReadOnly);
+
+ if (eatevent && sendInput(msg))
+ {
+ return eatevent;
+ }
+ return false; // do not eat otherwise
+ }
+ return QObject::eventFilter( o, e ); // standard event processing
+}
+
+
+// ----------------------- Mouse IO ---------------------------
+KGameMouseIO::KGameMouseIO(QWidget *parent,bool trackmouse)
+ : KGameIO()
+{
+ if (parent)
+ {
+ kdDebug(11001) << "Mouse Event filter installed tracking=" << trackmouse << endl;
+ parent->installEventFilter(this);
+ parent->setMouseTracking(trackmouse);
+ }
+}
+
+KGameMouseIO::~KGameMouseIO()
+{
+ if (parent())
+ {
+ parent()->removeEventFilter(this);
+ }
+}
+
+int KGameMouseIO::rtti() const
+{
+ return MouseIO;
+}
+
+void KGameMouseIO::setMouseTracking(bool b)
+{
+ if (parent())
+ {
+ ((QWidget*)parent())->setMouseTracking(b);
+ }
+}
+
+bool KGameMouseIO::eventFilter( QObject *o, QEvent *e )
+{
+ if (!player())
+ {
+ return false;
+ }
+// kdDebug(11001) << "KGameMouseIO " << this << endl ;
+
+ // mouse action
+ if ( e->type() == QEvent::MouseButtonPress ||
+ e->type() == QEvent::MouseButtonRelease ||
+ e->type() == QEvent::MouseButtonDblClick ||
+ e->type() == QEvent::Wheel ||
+ e->type() == QEvent::MouseMove
+ )
+ {
+ QMouseEvent *k = (QMouseEvent*)e;
+ // kdDebug(11001) << "KGameMouseIO " << this << endl ;
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ bool eatevent=false;
+ emit signalMouseEvent(this,stream,k,&eatevent);
+// kdDebug(11001) << "################# eatevent=" << eatevent << endl;
+ QDataStream msg(buffer,IO_ReadOnly);
+ if (eatevent && sendInput(msg))
+ {
+ return eatevent;
+ }
+ return false; // do not eat otherwise
+ }
+ return QObject::eventFilter( o, e ); // standard event processing
+}
+
+
+// ----------------------- KGameProcesPrivate ---------------------------
+class KGameProcessIO::KGameProcessIOPrivate
+{
+public:
+ KGameProcessIOPrivate()
+ {
+ //mMessageServer = 0;
+ //mMessageClient = 0;
+ mProcessIO=0;
+ }
+ //KMessageServer *mMessageServer;
+ //KMessageClient *mMessageClient;
+ KMessageProcess *mProcessIO;
+};
+
+// ----------------------- Process IO ---------------------------
+KGameProcessIO::KGameProcessIO(const QString& name)
+ : KGameIO()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)=" << sizeof(KGameProcessIO) << endl;
+ d = new KGameProcessIOPrivate;
+
+ //kdDebug(11001) << "================= KMEssageServer ==================== " << endl;
+ //d->mMessageServer=new KMessageServer(0,this);
+ //kdDebug(11001) << "================= KMEssageClient ==================== " << endl;
+ //d->mMessageClient=new KMessageClient(this);
+ kdDebug(11001) << "================= KMEssageProcessIO ==================== " << endl;
+ d->mProcessIO=new KMessageProcess(this,name);
+ kdDebug(11001) << "================= KMEssage Add client ==================== " << endl;
+ //d->mMessageServer->addClient(d->mProcessIO);
+ //kdDebug(11001) << "================= KMEssage SetSErver ==================== " << endl;
+ //d->mMessageClient->setServer(d->mMessageServer);
+ kdDebug(11001) << "================= KMEssage: Connect ==================== " << endl;
+ //connect(d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)),
+ // this, SLOT(clientMessage(const QByteArray&, Q_UINT32)));
+ //connect(d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList <Q_UINT32> &)),
+ // this, SLOT(clientMessage(const QByteArray&, Q_UINT32, const QValueList <Q_UINT32> &)));
+ connect(d->mProcessIO, SIGNAL(received(const QByteArray&)),
+ this, SLOT(receivedMessage(const QByteArray&)));
+ //kdDebug(11001) << "Our client is id="<<d->mMessageClient->id() << endl;
+}
+
+KGameProcessIO::~KGameProcessIO()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ kdDebug(11001) << "player="<<player() << endl;
+ if (player())
+ {
+ player()->removeGameIO(this,false);
+ }
+ if (d->mProcessIO)
+ {
+ delete d->mProcessIO;
+ d->mProcessIO=0;
+ }
+ delete d;
+}
+
+int KGameProcessIO::rtti() const
+{
+ return ProcessIO;
+}
+
+void KGameProcessIO::initIO(KPlayer *p)
+{
+ KGameIO::initIO(p);
+ // Send 'hello' to process
+ QByteArray buffer;
+ QDataStream stream(buffer, IO_WriteOnly);
+ Q_INT16 id = p->userId();
+ stream << id;
+
+ bool sendit=true;
+ if (p)
+ {
+ emit signalIOAdded(this,stream,p,&sendit);
+ if (sendit )
+ {
+ Q_UINT32 sender = p->id();
+ kdDebug(11001) << "Sending IOAdded to process player !!!!!!!!!!!!!! " << endl;
+ sendSystemMessage(stream, KGameMessage::IdIOAdded, 0, sender);
+ }
+ }
+}
+
+void KGameProcessIO::notifyTurn(bool b)
+{
+ if (!player())
+ {
+ kdWarning(11001) << k_funcinfo << ": player() is NULL" << endl;
+ return;
+ }
+ bool sendit=true;
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << (Q_INT8)b;
+ emit signalPrepareTurn(stream,b,this,&sendit);
+ if (sendit)
+ {
+ Q_UINT32 sender=player()->id();
+ kdDebug(11001) << "Sending Turn to process player !!!!!!!!!!!!!! " << endl;
+ sendSystemMessage(stream, KGameMessage::IdTurn, 0, sender);
+ }
+}
+
+void KGameProcessIO::sendSystemMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ sendAllMessages(stream, msgid, receiver, sender, false);
+}
+
+void KGameProcessIO::sendMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ sendAllMessages(stream, msgid, receiver, sender, true);
+}
+
+void KGameProcessIO::sendAllMessages(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender, bool usermsg)
+{
+ kdDebug(11001) << "==============> KGameProcessIO::sendMessage (usermsg="<<usermsg<<")" << endl;
+ // if (!player()) return ;
+ //if (!player()->isActive()) return ;
+
+ if (usermsg)
+ {
+ msgid+=KGameMessage::IdUser;
+ }
+
+ kdDebug(11001) << "=============* ProcessIO (" << msgid << "," << receiver << "," << sender << ") ===========" << endl;
+
+ QByteArray buffer;
+ QDataStream ostream(buffer,IO_WriteOnly);
+ QBuffer *device=(QBuffer *)stream.device();
+ QByteArray data=device->buffer();;
+
+ KGameMessage::createHeader(ostream,sender,receiver,msgid);
+ // ostream.writeRawBytes(data.data()+device->at(),data.size()-device->at());
+ ostream.writeRawBytes(data.data(),data.size());
+ kdDebug(11001) << " Adding user data from pos="<< device->at() <<" amount= " << data.size() << " byte " << endl;
+ //if (d->mMessageClient) d->mMessageClient->sendBroadcast(buffer);
+ if (d->mProcessIO)
+ {
+ d->mProcessIO->send(buffer);
+ }
+}
+
+//void KGameProcessIO::clientMessage(const QByteArray& receiveBuffer, Q_UINT32 clientID, const QValueList <Q_UINT32> &recv)
+void KGameProcessIO::receivedMessage(const QByteArray& receiveBuffer)
+{
+ QDataStream stream(receiveBuffer,IO_ReadOnly);
+ int msgid;
+ Q_UINT32 sender;
+ Q_UINT32 receiver;
+ KGameMessage::extractHeader(stream,sender,receiver,msgid);
+
+ kdDebug(11001) << "************* Got process message sender =" << sender
+ << " receiver=" << receiver << " msgid=" << msgid << endl;
+
+
+ // Cut out the header part...to not confuse network code
+ QBuffer *buf=(QBuffer *)stream.device();
+ QByteArray newbuffer;
+ newbuffer.setRawData(buf->buffer().data()+buf->at(),buf->size()-buf->at());
+ QDataStream ostream(newbuffer,IO_ReadOnly);
+ kdDebug(11001) << "Newbuffer size=" << newbuffer.size() << endl;
+
+
+
+// This is a dummy message which allows us the process to talk with its owner
+ if (msgid==KGameMessage::IdProcessQuery)
+ {
+ emit signalProcessQuery(ostream,this);
+ }
+ else if (player())
+ {
+ sender = player()->id(); // force correct sender
+ if (msgid==KGameMessage::IdPlayerInput)
+ {
+ sendInput(ostream,true,sender);
+ }
+ else
+ {
+ player()->forwardMessage(ostream,msgid,receiver,sender);
+ }
+ }
+ else
+ {
+ kdDebug(11001) << k_funcinfo << ": Got message from process but no player defined!" << endl;
+ }
+ newbuffer.resetRawData(buf->buffer().data()+buf->at(),buf->size()-buf->at());
+}
+
+
+// ----------------------- Computer IO --------------------------
+class KGameComputerIO::KGameComputerIOPrivate
+{
+//TODO: maybe these should be KGameProperties!!
+public:
+ KGameComputerIOPrivate()
+ {
+ mAdvanceCounter = 0;
+ mReactionPeriod = 0;
+
+ mPauseCounter = 0;
+
+ mAdvanceTimer = 0;
+ }
+ int mAdvanceCounter;
+ int mReactionPeriod;
+
+ int mPauseCounter;
+
+ QTimer* mAdvanceTimer;
+};
+
+KGameComputerIO::KGameComputerIO() : KGameIO()
+{
+ init();
+}
+
+KGameComputerIO::KGameComputerIO(KPlayer* p) : KGameIO(p)
+{
+ init();
+}
+
+void KGameComputerIO::init()
+{
+ d = new KGameComputerIOPrivate;
+}
+
+KGameComputerIO::~KGameComputerIO()
+{
+ if (d->mAdvanceTimer)
+ {
+ delete d->mAdvanceTimer;
+ }
+ delete d;
+}
+
+int KGameComputerIO::rtti() const
+{
+ return ComputerIO;
+}
+
+void KGameComputerIO::setReactionPeriod(int calls)
+{
+ d->mReactionPeriod = calls;
+}
+
+int KGameComputerIO::reactionPeriod() const
+{
+ return d->mReactionPeriod;
+}
+
+void KGameComputerIO::setAdvancePeriod(int ms)
+{
+ stopAdvancePeriod();
+ d->mAdvanceTimer = new QTimer(this);
+ connect(d->mAdvanceTimer, SIGNAL(timeout()), this, SLOT(advance()));
+ d->mAdvanceTimer->start(ms);
+}
+
+void KGameComputerIO::stopAdvancePeriod()
+{
+ if (d->mAdvanceTimer)
+ {
+ d->mAdvanceTimer->stop();
+ delete d->mAdvanceTimer;
+ }
+}
+
+void KGameComputerIO::pause(int calls)
+{
+ d->mPauseCounter = calls;
+}
+
+void KGameComputerIO::unpause()
+{
+ pause(0);
+}
+
+void KGameComputerIO::advance()
+{
+ if (d->mPauseCounter > 0)
+ {
+ d->mPauseCounter--;
+ return;
+ }
+ else if (d->mPauseCounter < 0)
+ {
+ return;
+ }
+ d->mAdvanceCounter++;
+ if (d->mAdvanceCounter >= d->mReactionPeriod)
+ {
+ d->mAdvanceCounter = 0;
+ reaction();
+ }
+}
+
+void KGameComputerIO::reaction()
+{
+ emit signalReaction();
+}
+
+
diff --git a/libkdegames/kgame/kgameio.h b/libkdegames/kgame/kgameio.h
new file mode 100644
index 00000000..4d7e0f5b
--- /dev/null
+++ b/libkdegames/kgame/kgameio.h
@@ -0,0 +1,566 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEIO_H__
+#define __KGAMEIO_H__
+
+#include <qstring.h>
+#include <qobject.h>
+#include <kdemacros.h>
+class KPlayer;
+class KGame;
+class KProcess;
+
+/**
+ * \short Base class for IO devices for games
+ *
+ * This is the master class for
+ * creating IO game devices. You cannot use it directly.
+ * Either take one of the classes derived from it or
+ * you have to create your own IO class derived from it (more probably).
+ *
+ * The idea behind this class is to provide a common interface
+ * for input devices into your game. By programming a KGameIO
+ * device you need not distinguish the actual IO in the game
+ * anymore. All work is done by the IO's. This allows very
+ * easy reuse in other games as well.
+ * A further advantage of using the IO's is that you can exchange
+ * the control of a player at runtime. E.g. you switch a player
+ * to be controlled by the computer or vice versa.
+ *
+ * To achieve this you have to make all of your player inputs through a
+ * KGameIO. You will usually call KGameIO::sendInput to do so.
+ *
+ * @author Martin Heni <[email protected]>
+ */
+class KDE_EXPORT KGameIO : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructs a KGameIO object
+ */
+ KGameIO();
+ KGameIO(KPlayer*);
+ virtual ~KGameIO();
+
+ /**
+ * Gives debug output of the game status
+ */
+ void Debug();
+
+ /**
+ * Identifies the KGameIO via the rtti function
+ */
+ enum IOMode {GenericIO=1,KeyIO=2,MouseIO=4,ProcessIO=8,ComputerIO=16};
+ /**
+ * Run time idendification. Predefined values are from IOMode
+ * You MUST overwrite this in derived classes!
+ *
+ * @return rtti value
+ */
+ virtual int rtti() const = 0; // Computer, network, local, ...
+
+ /**
+ * This function returns the player who owns this IO
+ *
+ * @return the player this IO device is plugged into
+ */
+ KPlayer *player() const {return mPlayer;}
+
+ /**
+ * Equivalent to player()->game()
+ * @return the @ref KGame object of this player
+ **/
+ KGame* game() const;
+
+ /**
+ * Sets the player to which this IO belongs to. This
+ * is done automatically when adding a device to a
+ * player
+ *
+ * @param p the player
+ */
+ void setPlayer(KPlayer *p) {mPlayer=p;}
+
+ /**
+ * Init this device by setting the player and e.g. sending an
+ * init message to the device. This initialisation message is
+ * very useful for computer players as you can transmit the
+ * game status to them and only update this status in the setTurn
+ * commands.
+ *
+ * Called by @ref KPlayer::addGameIO only!
+ */
+ virtual void initIO(KPlayer *p);
+
+ /**
+ * Notifies the IO device that the player's setTurn had been called
+ * Called by KPlayer
+ *
+ * This emits @ref signalPrepareTurn and sends the turn if the send
+ * parameter is set to true.
+ *
+ * @param b turn is true/false
+ */
+ virtual void notifyTurn(bool b);
+
+ /**
+ * Send an input message using @ref KPlayer::forwardInput
+ **/
+ bool sendInput(QDataStream& stream, bool transmit = true, Q_UINT32 sender = 0);
+
+signals:
+ /**
+ * Signal generated when @ref KPlayer::myTurn changes. This can either be
+ * when you get the turn status or when you lose it.
+ *
+ * The datastream has to be filled with a move. If you set (or leave) the
+ * send parameter to FALSE then nothing happens: the datastream will be
+ * ignored. If you set it to TRUE @ref sendInput is used to
+ * send the move.
+ *
+ * Often you want to ignore this signal (leave send=FALSE) and send the
+ * message later. This is usually the case for a human player as he probably
+ * doesn't react immediately. But you can still use this e.g. to notify the
+ * player about the turn change.
+ *
+ * Example:
+ * \code
+ * void GameWindow::slotPrepareTurn(QDataStream &stream,bool b,KGameIO *input,bool * )
+ * {
+ * KPlayer *player=input->player();
+ * if (!player->myTurn()) return ;
+ * if (!b) return ; // only do something on setTurn(true)
+ * stream << 1 << 2 << 3; // Some data for the process
+ * }
+ * \endcode
+ *
+ * @param io the KGameIO object itself
+ * @param stream the stream into which the move will be written
+ * @param turn the argument of setTurn
+ * @param send set this to true to send the generated move using @ref
+ * sendInput
+ **/
+ void signalPrepareTurn(QDataStream & stream, bool turn, KGameIO *io, bool * send);
+
+
+private:
+ KPlayer *mPlayer;
+};
+
+/**
+ * The KGameKeyIO class. It is used to process keyboard input
+ * from a widget and create moves for the player it belongs to.
+ * @author Martin Heni <[email protected]>
+ */
+class KDE_EXPORT KGameKeyIO : public KGameIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a keyboard input devices. All keyboards
+ * inputs of the given widgets are passed through a signal
+ * handler signalKeyEvent and can be used to generate
+ * a valid move for the player.
+ * Note the widget you pass to the constructor must be
+ * the main window of your application, e.g. view->parentWidget()
+ * as QT does not forward your keyevents otherwise. This means
+ * that this might be a different widget comapred to the one you
+ * use for mouse inputs!
+ * Example:
+ * \code
+ * KGameKeyIO *input;
+ * input=new KGameKeyIO(myWidget);
+ * connect(input,SIGNAL(signalKeyEvent(KGameIO *,QDataStream &,QKeyEvent *,bool *)),
+ * this,SLOT(slotKeyInput(KGameIO *,QDataStream &,QKeyEvent *,bool *)));
+ * \endcode
+ *
+ * @param parent The parents widget whose keyboard events * should be grabbed
+ */
+ KGameKeyIO(QWidget *parent);
+ virtual ~KGameKeyIO();
+
+ /**
+ * The idendification of the IO
+ *
+ * @return KeyIO
+ */
+ virtual int rtti() const;
+
+signals:
+ /**
+ * Signal handler for keyboard events. This function is called
+ * on every keyboard event. If appropriate it can generate a
+ * move for the player the device belongs to. If this is done
+ * and the event is eaten eatevent needs to be set to true.
+ * What move you generate (i.e. what you write to the stream)
+ * is totally up to you as it will not be evaluated but forwared
+ * to the player's/game's input move function
+ * Example:
+ * \code
+ * KPlayer *player=input->player(); // Get the player
+ * Q_INT32 key=e->key();
+ * stream << key;
+ * eatevent=true;
+ * \endcode
+ *
+ * @param io the IO device we belong to
+ * @param stream the stream where we write our move into
+ * @param m The QKeyEvent we can evaluate
+ * @param eatevent set this to true if we processed the event
+ */
+ void signalKeyEvent(KGameIO *io,QDataStream &stream,QKeyEvent *m,bool *eatevent);
+
+protected:
+ /**
+ * Internal method to process the events
+ */
+ bool eventFilter( QObject *o, QEvent *e );
+};
+
+/**
+ * The KGameMouseIO class. It is used to process mouse input
+ * from a widget and create moves for the player it belongs to.
+ * @author Martin Heni <[email protected]>
+ */
+class KDE_EXPORT KGameMouseIO : public KGameIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a mouse IO device. It captures all mouse
+ * event of the given widget and forwards them to the
+ * signal handler signalMouseEvent.
+ * Example:
+ * \code
+ * KGameMouseIO *input;
+ * input=new KGameMouseIO(mView);
+ * connect(input,SIGNAL(signalMouseEvent(KGameIO *,QDataStream &,QMouseEvent *,bool *)),
+ * this,SLOT(slotMouseInput(KGameIO *,QDataStream &,QMouseEvent *,bool *)));
+ * \endcode
+ *
+ * @param parent The widget whose events should be captured
+ * @param trackmouse enables mouse tracking (gives mouse move events)
+ */
+ KGameMouseIO(QWidget *parent,bool trackmouse=false);
+ virtual ~KGameMouseIO();
+
+ /**
+ * Manually activate or deactivate mouse tracking
+ *
+ * @param b true = tracking on
+ */
+ void setMouseTracking(bool b);
+ /**
+ * The idendification of the IO
+ *
+ * @return MouseIO
+ */
+ virtual int rtti() const;
+
+signals:
+ /**
+ * Signal handler for mouse events. This function is called
+ * on every mouse event. If appropriate it can generate a
+ * move for the player the device belongs to. If this is done
+ * and the event is eaten eatevent needs to be set to true.
+ * @see signalKeyEvent
+ * Example:
+ * \code
+ * KPlayer *player=input->player(); // Get the player
+ * Q_INT32 button=e->button();
+ * stream << button;
+ * eatevent=true;
+ * \endcode
+ *
+ * @param io the IO device we belong to
+ * @param stream the stream where we write our move into
+ * @param m The QMouseEvent we can evaluate
+ * @param eatevent set this to true if we processed the event
+ */
+ void signalMouseEvent(KGameIO *io,QDataStream &stream,QMouseEvent *m,bool *eatevent);
+
+protected:
+ /**
+ * Internal event filter
+ */
+ bool eventFilter( QObject *o, QEvent *e );
+
+};
+
+
+/**
+ * The KGameProcessIO class. It is used to create a computer player
+ * via a separate process and communicate transparetly with it.
+ * Its counterpart is the @ref KGameProcess class which needs
+ * to be used by the computer player. See its documentation
+ * for the definition of the computer player.
+ * @author Martin Heni <[email protected]>
+ */
+class KDE_EXPORT KGameProcessIO : public KGameIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a computer player via a separate process. The process
+ * name is given as fully qualified filename.
+ * Example:
+ * \code
+ * KGameProcessIO *input;
+ * input=new KGameProcessIO(executable_file);
+ * connect(input,SIGNAL(signalPrepareTurn(QDataStream &,bool,KGameIO *,bool *)),
+ * this,SLOT(slotPrepareTurn(QDataStream &,bool,KGameIO *,bool *)));
+ * connect(input,SIGNAL(signalProcessQuery(QDataStream &,KGameProcessIO *)),
+ * this,SLOT(slotProcessQuery(QDataStream &,KGameProcessIO *)));
+ * \endcode
+ *
+ * @param name the filename of the process to start
+ */
+ KGameProcessIO(const QString& name);
+
+ /**
+ * Deletes the process input devices
+ */
+ virtual ~KGameProcessIO();
+
+ /**
+ * The idendification of the IO
+ *
+ * @return ProcessIO
+ */
+ int rtti() const;
+
+ /**
+ * Send a message to the process. This is analogous to the sendMessage
+ * commands of KGame. It will result in a signal of the computer player
+ * on which you can react in the process player.
+ *
+ * @param stream - the actual data
+ * @param msgid - the id of the message
+ * @param receiver - not used
+ * @param sender - who send the message
+ */
+ void sendMessage(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender);
+
+ /**
+ * Send a system message to the process. This is analogous to the sendMessage
+ * commands of KGame. It will result in a signal of the computer player
+ * on which you can react in the process player.
+ *
+ * @param stream - the actual data
+ * @param msgid - the id of the message
+ * @param receiver - not used
+ * @param sender - who send the message
+ */
+ void sendSystemMessage(QDataStream &stream, int msgid, Q_UINT32 receiver, Q_UINT32 sender);
+
+ /**
+ * Init this device by setting the player and e.g. sending an
+ * init message to the device. Calling this function will emit
+ * the IOAdded signal on which you can react and initilise the
+ * computer player.
+ * This function is called automatically when adding the IO to
+ * a player.
+ */
+ void initIO(KPlayer *p);
+
+ /**
+ * Notifies the IO device that the player's setTurn had been called
+ * Called by KPlayer. You can react on the @ref signalPrepareTurn to
+ * prepare a message for the process, i.e. either update it on
+ * the changes made to the game since the last turn or the initIO
+ * has been called or transmit your gamestatus now.
+ *
+ * @param turn is true/false
+ */
+ virtual void notifyTurn(bool turn);
+
+ protected:
+ /**
+ * Internal ~ombined function for all message handling
+ **/
+ void sendAllMessages(QDataStream &stream,int msgid, Q_UINT32 receiver, Q_UINT32 sender, bool usermsg);
+
+ protected slots:
+ /**
+ * Internal message handler to receive data from the process
+ */
+ void receivedMessage(const QByteArray& receiveBuffer);
+
+
+signals:
+ /**
+ * A computer query message is received. This is a 'dummy'
+ * message sent by the process if it needs to communicate
+ * with us. It is not forwarded over the network.
+ * Reacting to this message allows you to 'answer' questions
+ * of the process, e.g. sending addition data which the process
+ * needs to calculate a move.
+ *
+ * Example:
+ * \code
+ * void GameWindow::slotProcessQuery(QDataStream &stream,KGameProcessIO *reply)
+ * {
+ * int no;
+ * stream >> no; // We assume the process sends us an integer question numner
+ * if (no==1) // but YOU have to do this in the process player
+ * {
+ * QByteArray buffer;
+ * QDataStream out(buffer,IO_WriteOnly);
+ * reply->sendSystemMessage(out,4242,0,0); // lets reply something...
+ * }
+ * }
+ * \endcode
+ */
+ void signalProcessQuery(QDataStream &stream,KGameProcessIO *me);
+
+ /**
+ * Signal generated when the computer player is added.
+ * You can use this to communicated with the process and
+ * e.g. send initialisation information to the process.
+ *
+ * @param game the KGameIO object itself
+ * @param stream the stream into which the move will be written
+ * @param p the player itself
+ * @param send set this to false if no move should be generated
+ */
+ void signalIOAdded(KGameIO *game,QDataStream &stream,KPlayer *p,bool *send);
+
+
+protected:
+
+private:
+ class KGameProcessIOPrivate;
+ KGameProcessIOPrivate* d;
+};
+
+/**
+ * \brief KGameIO variant for real-time games
+ *
+ * The KGameComputerIO class. It is used to create a LOCAL computer player
+ * and communicate transparently with it.
+ * Question: Is this needed or is it overwritten anyway for a real game?
+ *
+ * You most probably don't want to use this if you want to design a turn based
+ * game/player. You'll rather use @ref KGameIO directly, i.e. subclass it
+ * yourself. You just need to use @ref KGameIO::signalPrepareTurn and/or @ref
+ * KGameIO::notifyTurn there.
+ *
+ * This is rather meant to be of use in real time games.
+ *
+ * @author <[email protected]>
+ */
+class KDE_EXPORT KGameComputerIO : public KGameIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a LOCAL computer player
+ *
+ */
+ KGameComputerIO();
+ KGameComputerIO(KPlayer* player);
+ ~KGameComputerIO();
+
+ int rtti() const;
+
+ /**
+ * The number of advance calls until the player (or rather: the IO)
+ * does something (default: 1).
+ **/
+ void setReactionPeriod(int advanceCalls);
+ int reactionPeriod() const;
+
+ /**
+ * Start a QTimer which calls advance every @p ms milli seconds.
+ **/
+ void setAdvancePeriod(int ms);
+
+ void stopAdvancePeriod();
+
+ /**
+ * Ignore calls number of advance calls. if calls is -1 then all
+ * following advance calls are ignored until unpause is called.
+ *
+ * This simply prevents the internal advance counter to be increased.
+ *
+ * You may want to use this to emulate a "thinking" computer player. Note
+ * that this means if you increase the advance period (see
+ * setAdvancePeriod), i.e. if you change the speed of your game, your
+ * computer player thinks "faster".
+ * @param calls Number of advance calls to be ignored
+ **/
+ void pause(int calls = -1);
+
+ /**
+ * Equivalent to pause(0). Immediately continue to increase the internal
+ * advance counter.
+ **/
+ void unpause();
+
+public slots:
+ /**
+ * Works kind of similar to QCanvas::advance. Increase the internal
+ * advance counter. If @p reactionPeriod is reached the counter is set back to
+ * 0 and @ref signalReaction is emitted. This is when the player is meant
+ * to do something (move its units or so).
+ *
+ * This is very useful if you use QCanvas as you can use this in your
+ * QCanvas::advance call. The advantage is that if you change the speed
+ * of the game (i.e. change QCanvas::setAdvancePeriod) the computer
+ * player gets slower as well.
+ *
+ * If you don't use QCanvas you can use setAdvancePeriod to get
+ * the same result. Alternatively you can just use a QTimer.
+ *
+ **/
+ virtual void advance();
+
+signals:
+ /**
+ * This signal is emitted when your computer player is meant to do
+ * something, or better is meant to be allowed to do something.
+ **/
+ void signalReaction();
+
+protected:
+ /**
+ * Default implementation simply emits signalReaction
+ **/
+ virtual void reaction();
+
+private:
+ void init();
+
+private:
+ class KGameComputerIOPrivate;
+ KGameComputerIOPrivate* d;
+};
+
+
+#endif
diff --git a/libkdegames/kgame/kgamemessage.cpp b/libkdegames/kgame/kgamemessage.cpp
new file mode 100644
index 00000000..6464d407
--- /dev/null
+++ b/libkdegames/kgame/kgamemessage.cpp
@@ -0,0 +1,156 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgamemessage.h"
+
+#include <klocale.h>
+
+#define MESSAGE_VERSION 2
+
+Q_UINT32 KGameMessage::createPlayerId(int oldplayerid,Q_UINT32 gameid)
+{
+ int p;
+ p = oldplayerid & 0x3ff; // remove game id
+ p |= (gameid << 10);
+ return p;
+}
+
+int KGameMessage::rawPlayerId(Q_UINT32 playerid)
+{
+ return playerid & 0x03ff;
+}
+
+Q_UINT32 KGameMessage::rawGameId(Q_UINT32 playerid)
+{
+ return (playerid & 0xfc00) >> 10;
+}
+
+bool KGameMessage::isPlayer(Q_UINT32 msgid)
+{
+ if (msgid & 0xfc00) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool KGameMessage::isGame(Q_UINT32 msgid)
+{
+ return !isPlayer(msgid);
+}
+
+
+void KGameMessage::createHeader(QDataStream &msg,Q_UINT32 sender,Q_UINT32 receiver,int msgid)
+{
+ msg << (Q_INT16)sender << (Q_INT16)receiver << (Q_INT16)msgid;
+}
+
+void KGameMessage::extractHeader(QDataStream &msg,Q_UINT32 &sender,Q_UINT32 &receiver,int &msgid)
+{
+ Q_INT16 d3,d4,d5;
+ msg >> d3 >> d4 >> d5;
+ sender=d3;receiver=d4;msgid=d5;
+}
+
+void KGameMessage::createPropertyHeader(QDataStream &msg,int id)
+{
+ msg << (Q_INT16)id;
+}
+
+void KGameMessage::extractPropertyHeader(QDataStream &msg,int &id)
+{
+ Q_INT16 d1;
+ msg >> d1;
+ id=d1;
+}
+
+void KGameMessage::createPropertyCommand(QDataStream &msg,int cmdid,int pid,int cmd)
+{
+ createPropertyHeader(msg,cmdid);
+ msg << (Q_INT16)pid ;
+ msg << (Q_INT8)cmd ;
+}
+
+void KGameMessage::extractPropertyCommand(QDataStream &msg,int &pid,int &cmd)
+{
+ Q_INT16 d1;
+ Q_INT8 d2;
+ msg >> d1 >> d2;
+ pid=d1;
+ cmd=d2;
+}
+
+int KGameMessage::version()
+{
+ return MESSAGE_VERSION;
+}
+
+QString KGameMessage::messageId2Text(int msgid)
+{
+// this should contain all KGameMessage::GameMessageIds
+// feel free to add missing ones, to remove obsolete one and even feel free to
+// let it be ;-)
+ switch (msgid) {
+ case KGameMessage::IdSetupGame:
+ return i18n("Setup Game");
+ case KGameMessage::IdSetupGameContinue:
+ return i18n("Setup Game Continue");
+ case KGameMessage::IdGameLoad:
+ return i18n("Load Game");
+ case KGameMessage::IdGameConnected:
+ return i18n("Client game connected");
+ case KGameMessage::IdGameSetupDone:
+ return i18n("Game setup done");
+ case KGameMessage::IdSyncRandom:
+ return i18n("Synchronize Random");
+ case KGameMessage::IdDisconnect:
+ return i18n("Disconnect");
+ case KGameMessage::IdPlayerProperty:
+ return i18n("Player Property");
+ case KGameMessage::IdGameProperty:
+ return i18n("Game Property");
+ case KGameMessage::IdAddPlayer:
+ return i18n("Add Player");
+ case KGameMessage::IdRemovePlayer:
+ return i18n("Remove Player");
+ case KGameMessage::IdActivatePlayer:
+ return i18n("Activate Player");
+ case KGameMessage::IdInactivatePlayer:
+ return i18n("Inactivate Player");
+ case KGameMessage::IdTurn:
+ return i18n("Id Turn");
+ case KGameMessage::IdError:
+ return i18n("Error Message");
+ case KGameMessage::IdPlayerInput:
+ return i18n("Player Input");
+ case KGameMessage::IdIOAdded:
+ return i18n("An IO was added");
+ case KGameMessage::IdProcessQuery:
+ return i18n("Process Query");
+ case KGameMessage::IdPlayerId:
+ return i18n("Player ID");
+ case KGameMessage::IdUser: // IdUser must be unknown for use, too!
+ default:
+ return QString::null;
+ }
+}
diff --git a/libkdegames/kgame/kgamemessage.h b/libkdegames/kgame/kgamemessage.h
new file mode 100644
index 00000000..4394b4fa
--- /dev/null
+++ b/libkdegames/kgame/kgamemessage.h
@@ -0,0 +1,173 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEMSG_H_
+#define __KGAMEMSG_H_
+
+#include <qdatastream.h>
+#include <kdemacros.h>
+
+class KDE_EXPORT KGameMessage
+{
+ public:
+ /**
+ * Creates a fully qualified player ID which contains the original
+ * player id in the lower bits and the game number in the higher bits.
+ * Do not rely on the exact bit positions as they are internal.
+ *
+ * See also @ref rawPlayerId and @ref rawGameId which are the inverse
+ * operations
+ *
+ * @param playerid the player id - can include a gameid (will get removed)
+ * @param gameid The game id (<64). 0 For broadcast.
+ * @return the new player id
+ */
+ static Q_UINT32 createPlayerId(int player, Q_UINT32 game);
+
+ /**
+ * Returns the raw playerid, that is, a id which does not
+ * contain the game number encoded in it. See also @ref createPlayerId which
+ * is the inverse operation.
+ *
+ * @param the player id
+ * @return the raw player id
+ **/
+ static int rawPlayerId(Q_UINT32 playerid);
+
+ /**
+ * Returns the raw game id, that is, the game id the player
+ * belongs to. Se also @ref createPlayerId which is the inverse operation.
+ *
+ * @param the player id
+ * @return the raw game id
+ **/
+ static Q_UINT32 rawGameId(Q_UINT32 playerid);
+
+ /**
+ * Checks whether a message receiver/sender is a player
+ *
+ * @param id The ID of the sender/receiver
+ * @return true/false
+ */
+ static bool isPlayer(Q_UINT32 id);
+
+ /**
+ * Checks whether the sender/receiver of a message is a game
+ *
+ * @param id The ID of the sender/receiver
+ * @return true/false
+ */
+ static bool isGame(Q_UINT32 id);
+
+ /**
+ * Creates a message header given cookie,sender,receiver,...
+ *
+ * Also puts "hidden" header into the stream which are used by KGameClient
+ * (message length and magic cookie). If you don't need them remove them
+ * with @ref dropExternalHeader
+ */
+ static void createHeader(QDataStream &msg, Q_UINT32 sender, Q_UINT32 receiver, int msgid);
+
+ /**
+ * Retrieves the information like cookie,sender,receiver,... from a message header
+ *
+ * Note that it could be necessary to call @ref dropExternalHeader first
+ */
+ static void extractHeader(QDataStream &msg,Q_UINT32 &sender, Q_UINT32 &receiver, int &msgid);
+
+ /**
+ * Creates a property header given the property id
+ */
+ static void createPropertyHeader(QDataStream &msg, int id);
+
+ /**
+ * Retrieves the property id from a property message header
+ */
+ static void extractPropertyHeader(QDataStream &msg, int &id);
+
+ /**
+ * Creates a property header given the property id
+ */
+ static void createPropertyCommand(QDataStream &msg, int cmdid, int pid, int cmd);
+
+ /**
+ * Retrieves the property id from a property message header
+ */
+ static void extractPropertyCommand(QDataStream &msg, int &pid, int &cmd);
+
+ /**
+ * @return Version of the network library
+ */
+ static int version();
+
+ /**
+ * This function takes a @ref GameMessageIds as argument and returns a
+ * suitable string for it. This string can't be used to identify a message
+ * (as it is i18n'ed) but it can make debugging more easy. See also @ref
+ * KGameDebugDialog.
+ * @return Either a i18n'ed string (the name of the id) or QString::null if
+ * the msgid is unknown
+ **/
+ static QString messageId2Text(int msgid);
+
+
+ /**
+ * Message Ids used inside @ref KGame.
+ *
+ * You can use your own custom message Id by adding @p IdUser to it.
+ **/
+// please document every new id with a short comment
+ enum GameMessageIds {
+// game init, game load, disconnect, ...
+ IdSetupGame=1, // sent to a newly connected player
+ IdSetupGameContinue=2, // continue the setup
+ IdGameLoad=3, // load/save the game to the client
+ IdGameConnected=4, // Client successfully connected to master
+ IdSyncRandom=5, // new random seed set - sync games
+ IdDisconnect=6, // KGame object disconnects from game
+ IdGameSetupDone=7, // New game client is now operational
+
+// properties
+ IdPlayerProperty=20, // a player property changed
+ IdGameProperty=21, // a game property changed
+
+// player management
+ IdAddPlayer=30, // add a player
+ IdRemovePlayer=31, // the player will be removed
+ IdActivatePlayer=32, // Activate a player
+ IdInactivatePlayer=33, // Inactivate a player
+ IdTurn=34, // Turn to be prepared
+
+// to-be-categorized
+ IdError=100, // an error occurred
+ IdPlayerInput=101, // a player input occurred
+ IdIOAdded=102, // KGameIO got added to a player...init this IO
+
+// special ids for computer player
+ IdProcessQuery=220, // Process queries data (process only)
+ IdPlayerId=221, // PlayerId got changed (process only)
+
+ IdUser=256 // a user specified message
+ };
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamenetwork.cpp b/libkdegames/kgame/kgamenetwork.cpp
new file mode 100644
index 00000000..9eccb868
--- /dev/null
+++ b/libkdegames/kgame/kgamenetwork.cpp
@@ -0,0 +1,516 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgamenetwork.h"
+#include "kgamenetwork.moc"
+#include "kgamemessage.h"
+#include "kgameerror.h"
+
+#include "kmessageserver.h"
+#include "kmessageclient.h"
+#include "kmessageio.h"
+#include <dnssd/publicservice.h>
+
+#include <kdebug.h>
+
+#include <qbuffer.h>
+
+
+class KGameNetworkPrivate
+{
+public:
+ KGameNetworkPrivate()
+ {
+ mMessageClient = 0;
+ mMessageServer = 0;
+ mDisconnectId = 0;
+ mService = 0;
+ }
+
+public:
+ KMessageClient* mMessageClient;
+ KMessageServer* mMessageServer;
+ Q_UINT32 mDisconnectId; // Stores gameId() over a disconnect process
+ DNSSD::PublicService* mService;
+ QString mType;
+ QString mName;
+
+ int mCookie;
+};
+
+// ------------------- NETWORK GAME ------------------------
+KGameNetwork::KGameNetwork(int c, QObject* parent) : QObject(parent, 0)
+{
+ d = new KGameNetworkPrivate;
+ d->mCookie = (Q_INT16)c;
+
+ // Init the game as a local game, i.e.
+ // create your own KMessageServer and a KMessageClient connected to it.
+ setMaster();
+
+ kdDebug(11001) << k_funcinfo << "this=" << this <<", cookie=" << cookie() << " sizeof(this)="<<sizeof(KGameNetwork) << endl;
+}
+
+KGameNetwork::~KGameNetwork()
+{
+ kdDebug(11001) << k_funcinfo << "this=" << this << endl;
+// Debug();
+ delete d->mService;
+ delete d;
+}
+
+// ----------------------------- status methods
+bool KGameNetwork::isNetwork() const
+{ return isOfferingConnections() || d->mMessageClient->isNetwork();}
+
+Q_UINT32 KGameNetwork::gameId() const
+{
+ //return d->mMessageClient->id() ;
+ // Return stored id in the case of disconnect. In any other
+ // case the disconnect id is 0
+ if (d->mMessageClient->id()!=0 ) {
+ return d->mMessageClient->id() ;
+ } else {
+ return d->mDisconnectId;
+ }
+}
+
+int KGameNetwork::cookie() const
+{ return d->mCookie; }
+
+bool KGameNetwork::isMaster() const
+{ return (d->mMessageServer != 0); }
+
+bool KGameNetwork::isAdmin() const
+{ return (d->mMessageClient->isAdmin()); }
+
+KMessageClient* KGameNetwork::messageClient() const
+{ return d->mMessageClient; }
+
+KMessageServer* KGameNetwork::messageServer() const
+{ return d->mMessageServer; }
+
+// ----------------------- network init
+void KGameNetwork::setMaster()
+{
+ if (!d->mMessageServer) {
+ d->mMessageServer = new KMessageServer (cookie(), this);
+ } else {
+ kdWarning(11001) << k_funcinfo << "Server already running!!" << endl;
+ }
+ if (!d->mMessageClient) {
+ d->mMessageClient = new KMessageClient (this);
+ connect (d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)),
+ this, SLOT(receiveNetworkTransmission(const QByteArray&, Q_UINT32)));
+ connect (d->mMessageClient, SIGNAL(connectionBroken()),
+ this, SIGNAL(signalConnectionBroken()));
+ connect (d->mMessageClient, SIGNAL(aboutToDisconnect(Q_UINT32)),
+ this, SLOT(aboutToLoseConnection(Q_UINT32)));
+ connect (d->mMessageClient, SIGNAL(connectionBroken()),
+ this, SLOT(slotResetConnection()));
+
+ connect (d->mMessageClient, SIGNAL(adminStatusChanged(bool)),
+ this, SLOT(slotAdminStatusChanged(bool)));
+ connect (d->mMessageClient, SIGNAL(eventClientConnected(Q_UINT32)),
+ this, SIGNAL(signalClientConnected(Q_UINT32)));
+ connect (d->mMessageClient, SIGNAL(eventClientDisconnected(Q_UINT32, bool)),
+ this, SIGNAL(signalClientDisconnected(Q_UINT32, bool)));
+
+ // broacast and direct messages are treated equally on receive.
+ connect (d->mMessageClient, SIGNAL(forwardReceived(const QByteArray&, Q_UINT32, const QValueList<Q_UINT32>&)),
+ d->mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)));
+
+ } else {
+ // should be no problem but still has to be tested
+ kdDebug(11001) << k_funcinfo << "Client already exists!" << endl;
+ }
+ d->mMessageClient->setServer(d->mMessageServer);
+}
+
+void KGameNetwork::setDiscoveryInfo(const QString& type, const QString& name)
+{
+ kdDebug() << k_funcinfo << type << ":" << name << endl;
+ d->mType = type;
+ d->mName = name;
+ tryPublish();
+}
+
+void KGameNetwork::tryPublish()
+{
+ if (d->mType.isNull() || !isOfferingConnections()) return;
+ if (!d->mService) d->mService = new DNSSD::PublicService(d->mName,d->mType,port());
+ else {
+ if (d->mType!=d->mService->type()) d->mService->setType(d->mType);
+ if (d->mName!=d->mService->serviceName()) d->mService->setServiceName(d->mName);
+ }
+ if (!d->mService->isPublished()) d->mService->publishAsync();
+}
+
+void KGameNetwork::tryStopPublishing()
+{
+ if (d->mService) d->mService->stop();
+}
+
+bool KGameNetwork::offerConnections(Q_UINT16 port)
+{
+ kdDebug (11001) << k_funcinfo << "on port " << port << endl;
+ if (!isMaster()) {
+ setMaster();
+ }
+
+ // Make sure this is 0
+ d->mDisconnectId = 0;
+
+ // FIXME: This debug message can be removed when the program is working correct.
+ if (d->mMessageServer && d->mMessageServer->isOfferingConnections()) {
+ kdDebug (11001) << k_funcinfo << "Already running as server! Changing the port now!" << endl;
+ }
+
+ tryStopPublishing();
+ kdDebug (11001) << k_funcinfo << "before Server->initNetwork" << endl;
+ if (!d->mMessageServer->initNetwork (port)) {
+ kdError (11001) << k_funcinfo << "Unable to bind to port " << port << "!" << endl;
+ // no need to delete - we just cannot listen to the port
+// delete d->mMessageServer;
+// d->mMessageServer = 0;
+// d->mMessageClient->setServer((KMessageServer*)0);
+ return false;
+ }
+ kdDebug (11001) << k_funcinfo << "after Server->initNetwork" << endl;
+ tryPublish();
+ return true;
+}
+
+bool KGameNetwork::connectToServer (const QString& host, Q_UINT16 port)
+{
+ if (host.isEmpty()) {
+ kdError(11001) << k_funcinfo << "No hostname given" << endl;
+ return false;
+ }
+
+ // Make sure this is 0
+ d->mDisconnectId = 0;
+
+// if (!d->mMessageServer) {
+// // FIXME: What shall we do here? Probably must stop a running game.
+// kdWarning (11001) << k_funcinfo << "We are already connected to another server!" << endl;
+/// }
+
+ if (d->mMessageServer) {
+ // FIXME: What shall we do here? Probably must stop a running game.
+ kdWarning(11001) << "we are server but we are trying to connect to another server! "
+ << "make sure that all clients connect to that server! "
+ << "quitting the local server now..." << endl;
+ stopServerConnection();
+ d->mMessageClient->setServer((KMessageIO*)0);
+ delete d->mMessageServer;
+ d->mMessageServer = 0;
+ }
+
+ kdDebug(11001) << " about to set server" << endl;
+ d->mMessageClient->setServer(host, port);
+ emit signalAdminStatusChanged(false); // as we delete the connection above isAdmin() is always false now!
+
+ // OK: We say that we already have connected, but this isn't so yet!
+ // If the connection cannot be established, it will look as being disconnected
+ // again ("slotConnectionLost" is called).
+ // Shall we differ between these?
+ kdDebug(11001) << "connected to " << host << ":" << port << endl;
+ return true;
+}
+
+Q_UINT16 KGameNetwork::port() const
+{
+ if (isNetwork()) {
+ if (isOfferingConnections()) {
+ return d->mMessageServer->serverPort();
+ } else {
+ return d->mMessageClient->peerPort();
+ }
+ }
+ return 0;
+}
+
+QString KGameNetwork::hostName() const
+{
+ return d->mMessageClient->peerName();
+}
+
+bool KGameNetwork::stopServerConnection()
+{
+ // We still are the Master, we just don't accept further connections!
+ tryStopPublishing();
+ if (d->mMessageServer) {
+ d->mMessageServer->stopNetwork();
+ return true;
+ }
+ return false;
+}
+
+bool KGameNetwork::isOfferingConnections() const
+{ return (d->mMessageServer && d->mMessageServer->isOfferingConnections()); }
+
+void KGameNetwork::disconnect()
+{
+ // TODO MH
+ kdDebug(11001) << k_funcinfo << endl;
+ stopServerConnection();
+ if (d->mMessageServer) {
+ QValueList <Q_UINT32> list=d->mMessageServer->clientIDs();
+ QValueList<Q_UINT32>::Iterator it;
+ for( it = list.begin(); it != list.end(); ++it )
+ {
+ kdDebug(11001) << "Client id=" << (*it) << endl;
+ KMessageIO *client=d->mMessageServer->findClient(*it);
+ if (!client)
+ {
+ continue;
+ }
+ kdDebug(11001) << " rtti=" << client->rtti() << endl;
+ if (client->rtti()==2)
+ {
+ kdDebug(11001) << "DIRECT IO " << endl;
+ }
+ else
+ {
+ d->mMessageServer->removeClient(client,false);
+ }
+ }
+ }
+ else
+ {
+ kdDebug(11001) << k_funcinfo << "before client->disconnect() id="<<gameId()<< endl;
+ //d->mMessageClient->setServer((KMessageIO*)0);
+ kdDebug(11001) << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++"<<endl;
+ d->mMessageClient->disconnect();
+
+ kdDebug(11001) << "++++++--------------------------------------------+++++"<<endl;
+ }
+ //setMaster();
+ /*
+ if (d->mMessageServer) {
+ //delete d->mMessageServer;
+ //d->mMessageServer=0;
+ server=true;
+ kdDebug(11001) << " server true" << endl;
+ d->mMessageServer->deleteClients();
+ kdDebug(11001) << " server deleteClients" << endl;
+ }
+ */
+ kdDebug(11001) << k_funcinfo << "DONE" << endl;
+}
+
+void KGameNetwork::aboutToLoseConnection(Q_UINT32 clientID)
+{
+ kdDebug(11001) << "Storing client id of connection "<<clientID<<endl;
+ d->mDisconnectId = clientID;
+}
+
+void KGameNetwork::slotResetConnection()
+{
+ kdDebug(11001) << "Resseting client disconnect id"<<endl;
+ d->mDisconnectId = 0;
+}
+
+void KGameNetwork::electAdmin(Q_UINT32 clientID)
+{
+ if (!isAdmin()) {
+ kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl;
+ return;
+ }
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << static_cast<Q_UINT32>( KMessageServer::REQ_ADMIN_CHANGE );
+ stream << clientID;
+ d->mMessageClient->sendServerMessage(buffer);
+}
+
+void KGameNetwork::setMaxClients(int max)
+{
+ if (!isAdmin()) {
+ kdWarning(11001) << k_funcinfo << "only ADMIN is allowed to call this!" << endl;
+ return;
+ }
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << static_cast<Q_UINT32>( KMessageServer::REQ_MAX_NUM_CLIENTS );
+ stream << (Q_INT32)max;
+ d->mMessageClient->sendServerMessage(buffer);
+}
+
+void KGameNetwork::lock()
+{
+ if (messageClient()) {
+ messageClient()->lock();
+ }
+}
+
+void KGameNetwork::unlock()
+{
+ if (messageClient()) {
+ messageClient()->unlock();
+ }
+}
+
+// --------------------- send messages ---------------------------
+
+bool KGameNetwork::sendSystemMessage(int data, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << data;
+ return sendSystemMessage(buffer,msgid,receiver,sender);
+}
+
+bool KGameNetwork::sendSystemMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer, IO_WriteOnly);
+ stream << msg;
+ return sendSystemMessage(buffer, msgid, receiver, sender);
+}
+
+bool KGameNetwork::sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(((QBuffer*)msg.device())->buffer(), msgid, receiver, sender); }
+
+bool KGameNetwork::sendSystemMessage(const QByteArray& data, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ if (!sender) {
+ sender = gameId();
+ }
+
+ Q_UINT32 receiverClient = KGameMessage::rawGameId(receiver); // KGame::gameId()
+ int receiverPlayer = KGameMessage::rawPlayerId(receiver); // KPlayer::id()
+
+ KGameMessage::createHeader(stream, sender, receiver, msgid);
+ stream.writeRawBytes(data.data(), data.size());
+
+ /*
+ kdDebug(11001) << "transmitGameClientMessage msgid=" << msgid << " recv="
+ << receiver << " sender=" << sender << " Buffersize="
+ << buffer.size() << endl;
+ */
+
+ if (!d->mMessageClient) {
+ // No client created, this should never happen!
+ // Having a local game means we have our own
+ // KMessageServer and we are the only client.
+ kdWarning (11001) << k_funcinfo << "We don't have a client! Should never happen!" << endl;
+ return false;
+ }
+
+ if (receiverClient == 0 || receiverPlayer != 0)
+ {
+ // if receiverClient == 0 this is a broadcast message. if it is != 0 but
+ // receiverPlayer is also != 0 we have to send broadcast anyway, because the
+ // KPlayer object on all clients needs to receive the message.
+ d->mMessageClient->sendBroadcast(buffer);
+ }
+ else
+ {
+ d->mMessageClient->sendForward(buffer, receiverClient);
+ }
+ return true;
+}
+
+bool KGameNetwork::sendMessage(int data, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(data,msgid+KGameMessage::IdUser,receiver,sender); }
+
+bool KGameNetwork::sendMessage(const QString &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(msg,msgid+KGameMessage::IdUser,receiver,sender); }
+
+bool KGameNetwork::sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
+
+bool KGameNetwork::sendMessage(const QByteArray &msg, int msgid, Q_UINT32 receiver, Q_UINT32 sender)
+{ return sendSystemMessage(msg, msgid+KGameMessage::IdUser, receiver, sender); }
+
+void KGameNetwork::sendError(int error,const QByteArray& message, Q_UINT32 receiver, Q_UINT32 sender)
+{
+ QByteArray buffer;
+ QDataStream stream(buffer,IO_WriteOnly);
+ stream << (Q_INT32) error;
+ stream.writeRawBytes(message.data(), message.size());
+ sendSystemMessage(stream,KGameMessage::IdError,receiver,sender);
+}
+
+
+// ----------------- receive messages from the network
+void KGameNetwork::receiveNetworkTransmission(const QByteArray& receiveBuffer, Q_UINT32 clientID)
+{
+ QDataStream stream(receiveBuffer, IO_ReadOnly);
+ int msgid;
+ Q_UINT32 sender; // the id of the KGame/KPlayer who sent the message
+ Q_UINT32 receiver; // the id of the KGame/KPlayer the message is for
+ KGameMessage::extractHeader(stream, sender, receiver, msgid);
+// kdDebug(11001) << k_funcinfo << "id=" << msgid << " sender=" << sender << " recv=" << receiver << endl;
+
+ // No broadcast : receiver==0
+ // No player isPlayer(receiver)
+ // Different game gameId()!=receiver
+ if (receiver && receiver!=gameId() && !KGameMessage::isPlayer(receiver) )
+ {
+ // receiver=0 is broadcast or player message
+ kdDebug(11001) << k_funcinfo << "Message not meant for us "
+ << gameId() << "!=" << receiver << " rawid="
+ << KGameMessage::rawGameId(receiver) << endl;
+ return;
+ }
+ else if (msgid==KGameMessage::IdError)
+ {
+ QString text;
+ Q_INT32 error;
+ stream >> error;
+ kdDebug(11001) << k_funcinfo << "Got IdError " << error << endl;
+ text = KGameError::errorText(error, stream);
+ kdDebug(11001) << "Error text: " << text.latin1() << endl;
+ emit signalNetworkErrorMessage((int)error,text);
+ }
+ else
+ {
+ networkTransmission(stream, msgid, receiver, sender, clientID);
+ }
+}
+
+// -------------- slots for the signals of the client
+void KGameNetwork::slotAdminStatusChanged(bool isAdmin)
+{
+ emit signalAdminStatusChanged(isAdmin);
+
+// TODO: I'm pretty sure there are a lot of things that should be done here...
+}
+
+void KGameNetwork::Debug()
+{
+ kdDebug(11001) << "------------------- KNETWORKGAME -------------------------" << endl;
+ kdDebug(11001) << "gameId " << gameId() << endl;
+ kdDebug(11001) << "gameMaster " << isMaster() << endl;
+ kdDebug(11001) << "gameAdmin " << isAdmin() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+/*
+ * vim: et sw=2
+ */
diff --git a/libkdegames/kgame/kgamenetwork.h b/libkdegames/kgame/kgamenetwork.h
new file mode 100644
index 00000000..6ff5cf94
--- /dev/null
+++ b/libkdegames/kgame/kgamenetwork.h
@@ -0,0 +1,431 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMENETWORK_H_
+#define __KGAMENETWORK_H_
+
+#include <qstring.h>
+#include <qobject.h>
+#include <kdemacros.h>
+class KGameIO;
+class KMessageClient;
+class KMessageServer;
+
+class KGameNetworkPrivate;
+
+/**
+ * The KGameNetwork class is the KGame class with network
+ * support. All other features are the same but they are
+ * now network transparent. It is not used directly but
+ * only via a KGame object. So you do not really have
+ * to bother with this object.
+ *
+ * @short The main KDE game object
+ * @author Martin Heni <[email protected]>
+ * @version $Id$
+ */
+class KDE_EXPORT KGameNetwork : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a KGameNetwork object
+ */
+ KGameNetwork(int cookie=42,QObject* parent=0);
+ virtual ~KGameNetwork();
+
+ /**
+ * Gives debug output of the game status
+ **/
+ virtual void Debug();
+
+ /**
+ * @return TRUE if this is a network game - i.e. you are either MASTER or
+ * connected to a remote MASTER.
+ **/
+ bool isNetwork() const;
+
+ /**
+ * Is this the game MASTER (i.e. has started theKMessageServer). A
+ * game has always exactly one MASTER. This is either a KGame object (i.e. a
+ * Client) or an own MessageServer-process. A KGame object that has the
+ * MASTER status is always admin.
+ *
+ * You probably don't want to use this. It is a mostly internal method which
+ * will probably become protected. Better use isAdmin
+ *
+ * @see isAdmin
+ * @return Whether this client has started the KMessageServer
+ **/
+ bool isMaster() const;
+
+ /**
+ * The admin of a game is the one who initializes newly connected clients
+ * using negotiateNetworkGame and is allowed to configure the game.
+ * E.g. only the admin is allowed to use KGame::setMaxPlayers.
+ *
+ * If one KGame object in the game is MASTER then this client is the admin
+ * as well. isMaster and isAdmin differ only if the KMessageServer
+ * is running in an own process.
+ * @return Whether this client (KGame object) is the admin
+ **/
+ bool isAdmin() const;
+
+ /**
+ * The unique ID of this game
+ *
+ * @return int id
+ **/
+ Q_UINT32 gameId() const;
+
+ /**
+ * Inits a network game as network MASTER. Note that if the
+ * KMessageServer is not yet started it will be started here (see
+ * setMaster). Any existing connection will be disconnected.
+ *
+ * If you already offer connections the port is changed.
+ *
+ * @param port The port on which the service is offered
+ * @return true if it worked
+ **/
+ bool offerConnections (Q_UINT16 port);
+
+ /**
+ * Announces game MASTER on network using DNS-SD. Clients then can discover it using
+ * DNSSD::ServiceBrowser (or KGameConnectWidget) instead of manually entering
+ * IP address.
+ * @param type service type (something like _kwin4._tcp).
+ * It should be unique for application.
+ * @param name game name that will be displayed by clients. If not
+ * set hostname will be used. In case of name conflict -2, -3 and so on will be added to name.
+ * @since 3.4
+ **/
+ void setDiscoveryInfo(const QString& type, const QString& name=QString::null);
+
+ /**
+ * Inits a network game as a network CLIENT
+ *
+ * @param host the host to which we want to connect
+ * @param port the port we want to connect to
+ *
+ * @return true if connected
+ **/
+ bool connectToServer(const QString& host, Q_UINT16 port);
+
+ /**
+ * @since 3.2
+ * @return The port we are listening to if offerConnections was called
+ * or the port we are connected to if connectToServer was called.
+ * Otherwise 0.
+ **/
+ Q_UINT16 port() const;
+
+ /**
+ * @since 3.2
+ * @return The name of the host that we are currently connected to is
+ * isNetwork is TRUE and we are not the MASTER, i.e. if connectToServer
+ * was called. Otherwise this will return "localhost".
+ **/
+ QString hostName() const;
+
+ /**
+ * Stops offering server connections - only for game MASTER
+ * @return true
+ **/
+ bool stopServerConnection();
+
+ /**
+ * Changes the maximal connection number of the KMessageServer to max.
+ * -1 Means infinite connections are possible. Note that existing
+ * connections are not affected, so even if you set this to 0 in a running
+ * game no client is being disconnected. You can call this only if you are
+ * the ADMIN!
+ *
+ * @see KMessageServer::setMaxClients
+ * @param max The maximal number of connections possible.
+ **/
+ void setMaxClients(int max);
+
+ //AB: is this now internal only? Can we make it protected (maybe with
+ //friends)? sendSystemMessage AND sendMessage is very confusing to the
+ //user.
+ /**
+ * Sends a network message msg with a given msg id msgid to all clients.
+ * Use this to communicate with KGame (e.g. to add a player ot to configure
+ * the game - usually not necessary).
+ *
+ * For your own messages use sendMessage instead! This is mostly
+ * internal!
+ *
+ * @param buffer the message which will be send. See messages.txt for contents
+ * @param msgid an id for this message. See
+ * KGameMessage::GameMessageIds
+ * @param receiver the KGame / KPlayer this message is for.
+ * @param sender The KGame / KPlayer this message is from (i.e.
+ * you). You
+ * probably want to leave this 0, then KGameNetwork will create the correct
+ * value for you. You might want to use this if you send a message from a
+ * specific player.
+ * @return true if worked
+ */
+ // AB: TODO: doc on how "receiver" and "sender" should be created!
+ bool sendSystemMessage(const QByteArray& buffer, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * @overload
+ **/
+ bool sendSystemMessage(int data, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * @overload
+ **/
+ bool sendSystemMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * @overload
+ **/
+ bool sendSystemMessage(const QString& msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * Sends a network message
+ * @param error The error code
+ * @param message The error message - use KGameError
+ * @param receiver the KGame / KPlayer this message is for. 0 For
+ * all
+ * @param sender The KGame / KPlayer this message is from (i.e.
+ * you). You probably want to leave this 0, then KGameNetwork will create
+ * the correct value for you. You might want to use this if you send a
+ * message from a specific player.
+ **/
+ void sendError(int error, const QByteArray& message, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * Are we still offer offering server connections - only for game MASTER
+ * @return true/false
+ **/
+ bool isOfferingConnections() const;
+
+ /**
+ * Application cookie. this idendifies the game application. It
+ * help to distinguish between e.g. KPoker and KWin4
+ * @return the application cookie
+ **/
+ int cookie() const;
+
+ /**
+ * Send a network message msg with a given message ID msgid to all clients.
+ * You want to use this to send a message to the clients.
+ *
+ * Note that a message is always sent to ALL clients! This is necessary so
+ * that all clients always have the same data and can easily be changed from
+ * network to non-network without restarting the game. If you want a
+ * specific KGame / KPlayer to react to the message use the
+ * receiver and sender parameters. See KGameMessage::calsMessageId
+ *
+ * SendMessage differs from sendSystemMessage only by the msgid parameter.
+ * sendSystemMessage is thought as a KGame only mehtod while
+ * sendMessage is for public use. The msgid parameter will be
+ * +=KGameMessage::IdUser and in KGame::signalNetworkData msgid will
+ * be -= KGameMessage::IdUser again, so that one can easily distinguish
+ * between system and user messages.
+ *
+ * Use sendSystemMessage to comunicate with KGame (e.g. by adding a
+ * player) and sendMessage for your own user message.
+ *
+ * Note: a player should send messages through a KGameIO!
+ *
+ * @param buffer the message which will be send. See messages.txt for contents
+ * @param msgid an id for this message. See KGameMessage::GameMessageIds
+ * @param receiver the KGame / KPlayer this message is for.
+ * @param sender The KGame / KPlayer this message is from (i.e.
+ * you). You
+ * probably want to leave this 0, then KGameNetwork will create the correct
+ * value for you. You might want to use this if you send a message from a
+ * specific player.
+ * @return true if worked
+ **/
+ // AB: TODO: doc on how "receiver" and "sender" should be created!
+ bool sendMessage(const QByteArray& buffer, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ **/
+ bool sendMessage(const QDataStream &msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ **/
+ bool sendMessage(const QString& msg, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ **/
+ bool sendMessage(int data, int msgid, Q_UINT32 receiver=0, Q_UINT32 sender=0);
+
+
+ /**
+ * Called by ReceiveNetworkTransmission(). Will be overwritten by
+ * KGame and handle the incoming message.
+ **/
+ virtual void networkTransmission(QDataStream&, int, Q_UINT32, Q_UINT32, Q_UINT32 clientID) = 0;
+
+
+ /**
+ * Disconnect the current connection and establish a new local one.
+ **/
+ void disconnect();
+
+
+ /**
+ * If you are the ADMIN of the game you can give the ADMIN status away to
+ * another client. Use this e.g. if you want to quit the game or if you want
+ * another client to administrate the game (note that disconnect calls
+ * this automatically).
+ * @param clientID the ID of the new ADMIN (note: this is the _client_ID
+ * which has nothing to do with the player IDs. See KMessageServer)
+ **/
+ void electAdmin(Q_UINT32 clientID);
+
+ /**
+ * Don't use this unless you really know what youre doing! You might
+ * experience some strange behaviour if you send your messages directly
+ * through the KMessageClient!
+ *
+ * @return a pointer to the KMessageClient used internally to send the
+ * messages. You should rather use one of the send functions!
+ **/
+ KMessageClient* messageClient() const;
+
+ /**
+ * Don't use this unless you really know what you are doing! You might
+ * experience some strange behaviour if you use the message server directly!
+ *
+ * @return a pointer to the message server if this is the MASTER KGame
+ * object. Note that it might be possible that no KGame object contains
+ * the KMessageServer at all! It might even run stand alone!
+ **/
+ KMessageServer* messageServer() const;
+
+ /**
+ * You should call this before doing thigs like, e.g. qApp->processEvents().
+ * Don't forget to call unlock once you are done!
+ *
+ * @see KMessageClient::lock
+ **/
+ virtual void lock();
+
+ /**
+ * @see KMessageClient::unlock
+ **/
+ virtual void unlock();
+
+signals:
+ /**
+ * A network error occurred
+ * @param error the error code
+ * @param text the error text
+ */
+ void signalNetworkErrorMessage(int error, QString text);
+
+ /**
+ * Our connection to the KMessageServer has broken.
+ * See KMessageClient::connectionBroken
+ **/
+ void signalConnectionBroken();
+
+ /**
+ * This signal is emitted whenever the KMessageServer sends us a message that a
+ * new client connected. KGame uses this to call KGame::negotiateNetworkGame
+ * for the newly connected client if we are admin (see isAdmin)
+ *
+ * @see KMessageClient::eventClientConnected
+ *
+ * @param clientID the ID of the newly connected client
+ **/
+ void signalClientConnected(Q_UINT32 clientID);
+
+ /**
+ * This signal is emitted whenever the KMessageServer sends us a message
+ * that a connection to a client was detached. The second parameter can be used
+ * to distinguish between network errors or removing on purpose.
+ *
+ * @see KMessageClient::eventClientDisconnected
+ *
+ * @param clientID the client that has disconnected
+ * @param broken true if the connection was lost because of a network error, false
+ * if the connection was closed by the message server admin.
+ */
+ void signalClientDisconnected(Q_UINT32 clientID, bool broken);
+
+ /**
+ * This client gets or loses the admin status.
+ * @see KMessageClient::adminStatusChanged
+ * @param isAdmin True if this client gets the ADMIN status otherwise FALSE
+ **/
+ void signalAdminStatusChanged(bool isAdmin);
+
+protected:
+ /**
+ * @internal
+ * Start a KMessageServer object and use it as the MASTER of the game.
+ * Note that you must not call this if there is already another master
+ * running!
+ **/
+ void setMaster();
+
+protected slots:
+ /**
+ * Called by KMessageClient::broadcastReceived() and will check if the
+ * message format is valid. If it is not, it will generate an error (see
+ * signalNetworkVersionError and signalNetworkErorrMessage).
+ * If it is valid, the pure virtual method networkTransmission() is called.
+ * (This one is overwritten in KGame.)
+ **/
+ void receiveNetworkTransmission(const QByteArray& a, Q_UINT32 clientID);
+
+ /**
+ * This KGame object receives or loses the admin status.
+ * @param isAdmin Whether we are admin or not
+ **/
+ void slotAdminStatusChanged(bool isAdmin);
+
+ /**
+ * Called when the network connection is about to terminate. Is used
+ * to store the network parameter like the game id
+ */
+ void aboutToLoseConnection(Q_UINT32 id);
+
+ /**
+ * Called when the network connection is terminated. Used to clean
+ * up the disconnect parameter
+ */
+ void slotResetConnection();
+
+
+private:
+ void tryPublish();
+ void tryStopPublishing();
+ KGameNetworkPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kgameprocess.cpp b/libkdegames/kgame/kgameprocess.cpp
new file mode 100644
index 00000000..96efe0ce
--- /dev/null
+++ b/libkdegames/kgame/kgameprocess.cpp
@@ -0,0 +1,158 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgameprocess.h"
+#include "kplayer.h"
+#include "kgame.h"
+#include "kgamemessage.h"
+#include "kmessageio.h"
+
+#include <krandomsequence.h>
+
+#include <qbuffer.h>
+#include <qdatastream.h>
+#include <qcstring.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define READ_BUFFER_SIZE 1024
+
+// ----------------------- Process Child ---------------------------
+
+KGameProcess::KGameProcess() : QObject(0,0)
+{
+ mTerminate=false;
+ // Check whether a player is set. If not create one!
+ rFile.open(IO_ReadOnly|IO_Raw,stdin);
+ wFile.open(IO_WriteOnly|IO_Raw,stdout);
+ mMessageIO=new KMessageFilePipe(this,&rFile,&wFile);
+// mMessageClient=new KMessageClient(this);
+// mMessageClient->setServer(mMessageIO);
+// connect (mMessageClient, SIGNAL(broadcastReceived(const QByteArray&, Q_UINT32)),
+// this, SLOT(receivedMessage(const QByteArray&, Q_UINT32)));
+ connect (mMessageIO, SIGNAL(received(const QByteArray&)),
+ this, SLOT(receivedMessage(const QByteArray&)));
+ fprintf(stderr,"KGameProcess::constructor %p %p\n",&rFile,&wFile);
+
+ mRandom = new KRandomSequence;
+ mRandom->setSeed(0);
+}
+KGameProcess::~KGameProcess()
+{
+ delete mRandom;
+ //delete mMessageClient;
+ //delete mMessageServer;
+ delete mMessageIO;
+ rFile.close();
+ wFile.close();
+ fprintf(stderr,"KGameProcess::destructor\n");
+}
+
+
+bool KGameProcess::exec(int argc, char *argv[])
+{
+ // Get id and cookie, ... from command line
+ processArgs(argc,argv);
+ do
+ {
+ mMessageIO->exec();
+ } while(!mTerminate);
+ return true;
+}
+
+// You have to do this to create a message
+// QByteArray buffer;
+// QDataStream wstream(buffer,IO_WriteOnly);
+// then stream data into the stream and call this function
+void KGameProcess::sendSystemMessage(QDataStream &stream,int msgid,Q_UINT32 receiver)
+{
+ fprintf(stderr,"KGameProcess::sendMessage id=%d recv=%d",msgid,receiver);
+ QByteArray a;
+ QDataStream outstream(a,IO_WriteOnly);
+
+ QBuffer *device=(QBuffer *)stream.device();
+ QByteArray data=device->buffer();;
+
+ KGameMessage::createHeader(outstream,0,receiver,msgid);
+ outstream.writeRawBytes(data.data(),data.size());
+
+ //if (mMessageClient) mMessageClient->sendBroadcast(a);
+ // TODO: The fixed received 2 will cause problems. But how to address the
+ // proper one?
+// if (mMessageClient) mMessageClient->sendForward(a,2);
+ if (mMessageIO) mMessageIO->send(a);
+}
+
+void KGameProcess::sendMessage(QDataStream &stream,int msgid,Q_UINT32 receiver)
+{
+ sendSystemMessage(stream,msgid+KGameMessage::IdUser,receiver);
+}
+
+void KGameProcess::processArgs(int argc, char *argv[])
+{
+ int v=0;
+ if (argc>2)
+ {
+ v=atoi(argv[2]);
+ //kdDebug(11001) << "cookie (unused) " << v << endl;
+ }
+ if (argc>1)
+ {
+ v=atoi(argv[1]);
+ //kdDebug(11001) << "id (unused) " << v << endl;
+ }
+ fprintf(stderr,"processArgs \n");
+ fflush(stderr);
+}
+
+void KGameProcess::receivedMessage(const QByteArray& receiveBuffer)
+{
+ QDataStream stream(receiveBuffer, IO_ReadOnly);
+ int msgid;
+ Q_UINT32 sender;
+ Q_UINT32 receiver;
+ KGameMessage::extractHeader(stream, sender, receiver, msgid);
+ fprintf(stderr,"------ receiveNetworkTransmission(): id=%d sender=%d,recv=%d\n",msgid,sender,receiver);
+ switch(msgid)
+ {
+ case KGameMessage::IdTurn:
+ Q_INT8 b;
+ stream >> b;
+ emit signalTurn(stream,(bool)b);
+ break;
+ case KGameMessage::IdIOAdded:
+ Q_INT16 id;
+ stream >> id;
+ emit signalInit(stream,(int)id);
+ break;
+ default:
+ emit signalCommand(stream,msgid-KGameMessage::IdUser,receiver,sender);
+ break;
+ }
+}
+
+#include "kgameprocess.moc"
diff --git a/libkdegames/kgame/kgameprocess.h b/libkdegames/kgame/kgameprocess.h
new file mode 100644
index 00000000..a8db4fcd
--- /dev/null
+++ b/libkdegames/kgame/kgameprocess.h
@@ -0,0 +1,242 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEPROCESS_H_
+#define __KGAMEPROCESS_H_
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qfile.h>
+
+#include "kgameproperty.h"
+#include <krandomsequence.h>
+#include <kdemacros.h>
+class KPlayer;
+class KMessageFilePipe;
+
+/**
+ * This is the process class used on the computer player
+ * side to communicate with its counterpart KProcessIO class.
+ * Using these two classes will give fully transparent communication
+ * via QDataStreams.
+ */
+class KDE_EXPORT KGameProcess: public QObject
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Creates a KGameProcess class. Done only in the computer
+ * player. To activate the communication you have to call
+ * the exec function of this class which will listen
+ * to the communication and emit signals to notify you of
+ * any incoming messages.
+ * Note: This function will only return after you set
+ * setTerminate(true) in one of the received signals.
+ * So you can not do any computer calculation after the exec function.
+ * Instead you react on the signals which are emitted after a
+ * message is received and perform the calculations there!
+ * Example:
+ * \code
+ * int main(int argc ,char * argv[])
+ * {
+ * KGameProcess proc;
+ * connect(&proc,SIGNAL(signalCommand(QDataStream &,int ,int ,int )),
+ * this,SLOT(slotCommand(QDataStream & ,int ,int ,int )));
+ * connect(&proc,SIGNAL(signalInit(QDataStream &,int)),
+ * this,SLOT(slotInit(QDataStream & ,int )));
+ * connect(&proc,SIGNAL(signalTurn(QDataStream &,bool )),
+ * this,SLOT(slotTurn(QDataStream & ,bool )));
+ * return proc.exec(argc,argv);
+ * }
+ * \endcode
+ */
+ KGameProcess();
+ /**
+ * Destruct the process
+ */
+ ~KGameProcess();
+
+ /**
+ * Enters the event loop of the computer process. Does only
+ * return on setTerminate(true)!
+ */
+ bool exec(int argc, char *argv[]);
+
+ /**
+ * Should the computer process leave its exec function?
+ * Activated if you setTerminate(true);
+ *
+ * @return true/false
+ */
+ bool terminate() const {return mTerminate;}
+
+ /**
+ * Set this to true if the computer process should end, ie
+ * leave its exec function.
+ *
+ * @param b true for exit the exec function
+ */
+ void setTerminate(bool b) {mTerminate=b;}
+
+ /**
+ * Sends a message to the corresponding KGameIO
+ * device. Works like the sendSystemMessage but
+ * for user id's
+ *
+ * @param stream the QDataStream containing the message
+ * @param msgid the message id for the message
+ * @param receiver unused
+ */
+ void sendMessage(QDataStream &stream,int msgid,Q_UINT32 receiver=0);
+
+ /**
+ * Sends a system message to the corresonding KGameIO device.
+ * This will normally be either a performed move or a query
+ * (IdProcessQuery). The query option is a way to communicate
+ * with the KGameIO at the other side and e.g. retrieve some
+ * game relevant data from here.
+ * Exmaple for a query:
+ * \code
+ * QByteArray buffer;
+ * QDataStream out(buffer,IO_WriteOnly);
+ * int msgid=KGameMessage::IdProcessQuery;
+ * out << (int)1;
+ * proc.sendSystemMessage(out,msgid,0);
+ * \endcode
+ *
+ * @param stream the QDataStream containing the message
+ * @param msgid the message id for the message
+ * @param receiver unused
+ */
+ void sendSystemMessage(QDataStream &stream,int msgid,Q_UINT32 receiver=0);
+
+ /**
+ * Returns a pointer to a KRandomSequence. You can generate
+ * random numbers via e.g.
+ * \code
+ * random()->getLong(100);
+ * \endcode
+ *
+ * @return KRandomSequence pointer
+ */
+ KRandomSequence *random() {return mRandom;}
+
+ protected:
+ /**
+ * processes the command line argumens to set up the computer player
+ * Pass the argumens exactely as given by main()
+ */
+ void processArgs(int argc, char *argv[]);
+
+ protected slots:
+ /**
+ * A message is received via the interprocess connection. The
+ * appropriate signals are called.
+ */
+ void receivedMessage(const QByteArray& receiveBuffer);
+
+ signals:
+ /**
+ * The generic communication signal. You have to connect to this
+ * signal to generate a valid computer response onto arbitrary messages.
+ * All signals but IdIOAdded and IdTurn end up here!
+ * Example:
+ * \code
+ * void Computer::slotCommand(int &msgid,QDataStream &in,QDataStream &out)
+ * {
+ * Q_INT32 data,move;
+ * in >> data;
+ * // compute move ...
+ * move=data*2;
+ * out << move;
+ * }
+ * \endcode
+ *
+ * @param inputStream the incoming data stream
+ * @param msgid the message id of the message which got transmitted to the computer
+ * @param receiver the id of the receiver
+ * @param sender the id of the sender
+ */
+ void signalCommand(QDataStream &inputStream,int msgid,int receiver,int sender);
+
+ /**
+ * This signal is emmited if the computer player should perform a turn.
+ * Calculations can be made here and the move can then be send back with
+ * sendSystemMessage with the message id KGameMessage::IdPlayerInput.
+ * These must provide a move which complies to your other move syntax as
+ * e.g. produces by keyboard or mouse input.
+ * Additonal data which have been written into the stream from the
+ * ProcessIO's signal signalPrepareTurn can be retrieved from the
+ * stream here.
+ * Example:
+ * \code
+ * void slotTurn(QDataStream &in,bool turn)
+ * {
+ * int id;
+ * int recv;
+ * QByteArray buffer;
+ * QDataStream out(buffer,IO_WriteOnly);
+ * if (turn)
+ * {
+ * // Create a move - the format is yours to decide
+ * // It arrives exactly as this in the kgame inputMove function!!
+ * Q_INT8 x1,y1,pl;
+ * pl=-1;
+ * x1=proc.random()->getLong(8);
+ * y1=proc.random()->getLong(8);
+ * // Stream it
+ * out << pl << x1 << y1;
+ * id=KGameMessage::IdPlayerInput;
+ * proc.sendSystemMessage(out,id,0);
+ * }
+ * }
+ * \endcode
+ *
+ * @param stream The datastream which contains user data
+ * @param turn True or false whether the turn is activated or deactivated
+ *
+ */
+ void signalTurn(QDataStream &stream,bool turn);
+
+ /**
+ * This signal is emmited when the process is initialized, i.e. added
+ * to a KPlayer. Initial initialisation can be performed here be reacting
+ * to the KProcessIO signal signalIOAdded and retrieving the data here
+ * from the stream.
+ * It works just as the signalTurn() but is only send when the player is
+ * added to the game, i.e. it needs some initialization data
+ *
+ * @param stream The datastream which contains user data
+ * @param userid The userId of the player. (Careful to rely on it yet)
+ */
+ void signalInit(QDataStream &stream,int userid);
+
+ protected:
+ bool mTerminate;
+ KMessageFilePipe *mMessageIO;
+ private:
+ QFile rFile;
+ QFile wFile;
+ KRandomSequence* mRandom;
+};
+#endif
diff --git a/libkdegames/kgame/kgameproperty.cpp b/libkdegames/kgame/kgameproperty.cpp
new file mode 100644
index 00000000..68a33bcb
--- /dev/null
+++ b/libkdegames/kgame/kgameproperty.cpp
@@ -0,0 +1,211 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgameproperty.h"
+#include "kgamepropertyhandler.h"
+#include "kgamemessage.h"
+#include "kplayer.h"
+#include "kgame.h"
+
+#define KPLAYERHANDLER_LOAD_COOKIE 6239
+
+KGamePropertyBase::KGamePropertyBase(int id, KGame* parent)
+{
+ init();
+ registerData(id, parent);
+}
+
+KGamePropertyBase::KGamePropertyBase(int id, KPlayer* parent)
+{
+ init();
+ registerData(id, parent);
+}
+
+KGamePropertyBase::KGamePropertyBase(int id, KGamePropertyHandler* owner)
+{
+ init();
+ registerData(id, owner);
+}
+
+KGamePropertyBase::KGamePropertyBase()
+{
+ init();
+}
+
+KGamePropertyBase::~KGamePropertyBase()
+{
+ unregisterData();
+}
+
+void KGamePropertyBase::init()
+{
+ mOwner = 0;
+ setDirty(false);
+
+ // this is very useful and used by e.g. KGameDialog so
+ // it is activated by default. Big games may profit by deactivating it to get
+ // a better performance.
+ setEmittingSignal(true);
+
+ setOptimized(false);
+
+ //setReadOnly(false);
+ mFlags.bits.locked = false ; // setLocked(false); is NOT possible as it checks whether isLocked() allows to change the status
+
+ // local is default
+ setPolicy(PolicyLocal);
+}
+
+int KGamePropertyBase::registerData(int id, KGame* owner, QString name)
+{ return registerData(id, owner->dataHandler(), name); }
+
+int KGamePropertyBase::registerData(int id, KPlayer* owner, QString name)
+{ return registerData(id, owner->dataHandler(), name); }
+
+int KGamePropertyBase::registerData( KGamePropertyHandler* owner,PropertyPolicy p, QString name)
+{ return registerData(-1, owner,p, name); }
+
+int KGamePropertyBase::registerData(int id, KGamePropertyHandler* owner, QString name)
+{ return registerData(id, owner,PolicyUndefined, name); }
+
+int KGamePropertyBase::registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, QString name)
+{
+// we don't support changing the id
+ if (!owner) {
+ kdWarning(11001) << k_funcinfo << "Resetting owner=0. Sure you want to do this?" << endl;
+ mOwner=0;
+ return -1;
+ }
+ if (!mOwner) {
+ if (id==-1) {
+ id=owner->uniquePropertyId();
+ }
+ mId = id;
+ mOwner = owner;
+ mOwner->addProperty(this, name);
+ if (p!=PolicyUndefined) {
+ setPolicy(p);
+ } else {
+ setPolicy(mOwner->policy());
+ }
+ }
+ return mId;
+}
+
+void KGamePropertyBase::unregisterData()
+{
+ if (!mOwner) {
+ return;
+ }
+ mOwner->removeProperty(this);
+ mOwner = 0;
+}
+
+bool KGamePropertyBase::sendProperty()
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyHeader(s, id());
+ save(s);
+ if (mOwner) {
+ return mOwner->sendProperty(s);
+ } else {
+ kdError(11001) << k_funcinfo << "Cannot send because there is no receiver defined" << endl;
+ return false;
+ }
+}
+
+bool KGamePropertyBase::sendProperty(const QByteArray& data)
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyHeader(s, id());
+ s.writeRawBytes(data.data(), data.size());
+ if (mOwner) {
+ return mOwner->sendProperty(s);
+ } else {
+ kdError(11001) << k_funcinfo << ": Cannot send because there is no receiver defined" << endl;
+ return false;
+ }
+}
+
+bool KGamePropertyBase::lock()
+{
+ if (isLocked()) {
+ return false;
+ }
+ setLock(true);
+ return true;
+}
+
+bool KGamePropertyBase::unlock(bool force)
+{
+ if (isLocked() && !force) {
+ return false;
+ }
+ setLock(false);
+ return true;
+}
+
+void KGamePropertyBase::setLock(bool l)
+{
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s, IdCommand, id(), CmdLock);
+
+ s << (Q_INT8)l;
+ if (mOwner) {
+ mOwner->sendProperty(s);
+ } else {
+ kdError(11001) << k_funcinfo << ": Cannot send because there is no receiver defined" << endl;
+ return ;
+ }
+}
+
+void KGamePropertyBase::emitSignal()
+{
+ //kdDebug(11001) << k_funcinfo << ": mOwnerP="<< mOwner << " id=" << id() << endl;
+ if (mOwner ) {
+ mOwner->emitSignal(this);
+ } else {
+ kdError(11001) << k_funcinfo << ":id="<<id()<<" Cannot emitSignal because there is no handler set" << endl;
+ }
+}
+
+void KGamePropertyBase::command(QDataStream& s, int cmd, bool isSender)
+{
+ switch (cmd) {
+ case CmdLock:
+ {
+ if (!isSender) {
+ Q_INT8 locked;
+ s >> locked;
+ mFlags.bits.locked = (bool)locked ;
+ break;
+ }
+ }
+ default: // probably in derived classes
+ break;
+ }
+}
+
diff --git a/libkdegames/kgame/kgameproperty.h b/libkdegames/kgame/kgameproperty.h
new file mode 100644
index 00000000..c6915606
--- /dev/null
+++ b/libkdegames/kgame/kgameproperty.h
@@ -0,0 +1,848 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEPROPERTY_H_
+#define __KGAMEPROPERTY_H_
+
+#include <qdatastream.h>
+
+#include <kdebug.h>
+#include <typeinfo>
+#include <kdemacros.h>
+class KGame;
+class KPlayer;
+class KGamePropertyHandler;
+using namespace std;
+
+/**
+ * @short Base class of KGameProperty
+ *
+ * The KGamePropertyBase class is the base class of KGameProperty. See
+ * KGameProperty for further information.
+ *
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KDE_EXPORT KGamePropertyBase
+{
+public:
+ enum PropertyDataIds { // these belong to KPlayer/KGame!
+ //KPlayer
+ IdGroup=1,
+ IdUserId=2,
+ IdAsyncInput=3,
+ IdTurn=4,
+ IdName=5,
+
+ //KGame
+ IdGameStatus=6,
+ IdMaxPlayer=7,
+ IdMinPlayer=8,
+
+ // Input Grabbing
+ IdGrabInput=16,
+ IdReleaseInput=17,
+
+ IdCommand, // Reserved for internal use
+ IdUser=256,
+
+ IdAutomatic=0x7000 // Id's from here on are automatically given (16bit)
+ };
+
+ /**
+ * Commands for advanced properties (Q_INT8)
+ **/
+ enum PropertyCommandIds
+ {
+ // General
+ CmdLock=1,
+
+ // Array
+ CmdAt=51,
+ CmdResize=52,
+ CmdFill=53,
+ CmdSort=54,
+ // List (could be the same id's actually)
+ CmdInsert=61,
+ CmdAppend=62,
+ CmdRemove=63,
+ CmdClear=64
+ };
+
+ /**
+ * The policy of the property. This can be PolicyClean (setValue uses
+ * send), PolicyDirty (setValue uses changeValue) or
+ * PolicyLocal (setValue uses setLocal).
+ *
+ * A "clean" policy means that the property is always the same on every
+ * client. This is achieved by calling send which actually changes
+ * the value only when the message from the MessageServer is received.
+ *
+ * A "dirty" policy means that as soon as setValue is called the
+ * property is changed immediately. And additionally sent over network.
+ * This can sometimes lead to bugs as the other clients do not
+ * immediately have the same value. For more information see
+ * changeValue.
+ *
+ * PolicyLocal means that a KGameProperty behaves like ever
+ * "normal" variable. Whenever setValue is called (e.g. using "=")
+ * the value of the property is changes immediately without sending it
+ * over network. You might want to use this if you are sure that all
+ * clients set the property at the same time.
+ **/
+ enum PropertyPolicy
+ {
+ PolicyUndefined = 0,
+ PolicyClean = 1,
+ PolicyDirty = 2,
+ PolicyLocal = 3
+ };
+
+
+ /**
+ * Constructs a KGamePropertyBase object and calls registerData.
+ * @param id The id of this property. MUST be UNIQUE! Used to send and
+ * receive changes in the property of the playere automatically via
+ * network.
+ * @param owner The owner of the object. Must be a KGamePropertyHandler which manages
+ * the changes made to this object, i.e. which will send the new data
+ **/
+ KGamePropertyBase(int id, KGamePropertyHandler* owner);
+
+ KGamePropertyBase(int id, KGame* parent);
+ KGamePropertyBase(int id, KPlayer* parent);
+
+ /**
+ * Creates a KGamePropertyBase object without an owner. Remember to call
+ * registerData!
+ **/
+ KGamePropertyBase();
+
+ virtual ~KGamePropertyBase();
+
+ /**
+ * Changes the consistency policy of a property. The
+ * PropertyPolicy is one of PolicyClean (defaulz), PolicyDirty or PolicyLocal.
+ *
+ * It is up to you to decide how you want to work.
+ **/
+ void setPolicy(PropertyPolicy p) { mFlags.bits.policy = p; }
+
+ /**
+ * @return The default policy of the property
+ **/
+ PropertyPolicy policy() const { return (PropertyPolicy)mFlags.bits.policy; }
+
+ /**
+ * Sets this property to emit a signal on value changed.
+ * As the proerties do not inehrit QObject for optimisation
+ * this signal is emited via the KPlayer or KGame object
+ **/
+ void setEmittingSignal(bool p) { mFlags.bits.emitsignal=p; }
+
+ /**
+ * See also setEmittingSignal
+ * @return Whether this property emits a signal on value change
+ **/
+ bool isEmittingSignal() const { return mFlags.bits.emitsignal; }
+
+ /**
+ * Sets this property to try to optimize signal and network handling
+ * by not sending it out when the property value is not changed.
+ **/
+ void setOptimized(bool p) { mFlags.bits.optimize = p ; }
+
+ /**
+ * See also setOptimize
+ * @return Whether the property optimizes access (signals,network traffic)
+ **/
+ bool isOptimized() const { return mFlags.bits.optimize; }
+
+ /**
+ * @return Whether this property is "dirty". See also setDirty
+ **/
+ bool isDirty() const { return mFlags.bits.dirty; }
+
+ /**
+ * A locked property can only be changed by the player who has set the
+ * lock. See also setLocked
+ * @return Whether this property is currently locked.
+ **/
+ bool isLocked() const { return mFlags.bits.locked; }
+
+ /**
+ * A locked property can only be changed by the player who has set the
+ * lock.
+ *
+ * You can only call this if isLocked is false. A message is sent
+ * over network so that the property is locked for all players except
+ * you.
+ *
+ * @return returns false if the property can not be locked, i.e. it is already locked
+ *
+ **/
+ bool lock();
+
+ /**
+ * A locked property can only be changed by the player who has set the
+ * lock.
+ *
+ * You can only call this if isLocked is false. A message is sent
+ * over network so that the property is locked for all players except
+ * you.
+ *
+ * @return returns false if the property can not be locked, i.e. it is already locked
+ *
+ **/
+ bool unlock(bool force=false);
+
+ /**
+ * This will read the value of this property from the stream. You MUST
+ * overwrite this method in order to use this class
+ * @param s The stream to read from
+ **/
+ virtual void load(QDataStream& s) = 0;
+
+ /**
+ * Write the value into a stream. MUST be overwritten
+ **/
+ virtual void save(QDataStream& s) = 0;
+
+ /**
+ * send a command to advanced properties like arrays
+ * @param stream The stream containing the data of the comand
+ * @param msgid The ID of the command - see PropertyCommandIds
+ * @param isSender whether this client is also the sender of the command
+ **/
+ virtual void command(QDataStream &stream, int msgid, bool isSender=false);
+
+ /**
+ * @return The id of this property
+ **/
+ int id() const { return mId; }
+
+ /**
+ * @return a type_info of the data this property contains. This is used
+ * e.g. by KGameDebugDialog
+ **/
+ virtual const type_info* typeinfo() { return &typeid(this); }
+
+ /**
+ * You have to register a KGamePropertyBase before you can use it.
+ *
+ * You MUST call this before you can use KGamePropertyBase!
+ *
+ * @param id the id of this KGamePropertyBase object. The id MUST be
+ * unique, i.e. you cannot have two properties with the same id for one
+ * player, although (currently) nothing prevents you from doing so. But
+ * you will get strange results!
+ *
+ * @param owner The owner of this data. This will send the data
+ * using KPropertyHandler::sendProperty whenever you call send
+ *
+ * @param p If not 0 you can set the policy of the property here
+ *
+ * @param name if not 0 you can assign a name to this property
+ *
+ **/
+ int registerData(int id, KGamePropertyHandler* owner,PropertyPolicy p, QString name=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ **/
+ int registerData(int id, KGamePropertyHandler* owner, QString name=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ **/
+ int registerData(int id, KGame* owner, QString name=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ **/
+ int registerData(int id, KPlayer* owner, QString name=0);
+
+ /**
+ * This is an overloaded member function, provided for convenience.
+ * It differs from the above function only in what argument(s) it accepts.
+ * In particular you can use this function to create properties which
+ * will have an automatic id assigned. The new id is returned.
+ **/
+ int registerData(KGamePropertyHandler* owner,PropertyPolicy p=PolicyUndefined, QString name=0);
+
+ void unregisterData();
+
+
+protected:
+ /**
+ * A locked property can only be changed by the player who has set the
+ * lock.
+ *
+ * You can only call this if isLocked is false. A message is sent
+ * over network so that the property is locked for all players except
+ * you.
+ * Usually you use lock and unlock to access this property
+ *
+ **/
+ void setLock(bool l);
+
+ /**
+ * Sets the "dirty" flag of the property. If a property is "dirty" i.e.
+ * KGameProperty::setLocal has been called there is no guarantee
+ * that all clients share the same value. You have to ensure this
+ * yourself e.g. by calling KGameProperty::setLocal on every
+ * client. You can also ignore the dirty flag and continue working withe
+ * the property depending on your situation.
+ **/
+ void setDirty(bool d) { mFlags.bits.dirty = d ; }
+
+ /**
+ * Forward the data to the owner of this property which then sends it
+ * over network. save is used to store the data into a stream so
+ * you have to make sure that function is working properly if you
+ * implement your own property!
+ *
+ * Note: this sends the <em>current</em> property!
+ *
+ * Might be obsolete - KGamePropertyArray still uses it. Is this a bug
+ * or correct?
+ **/
+ bool sendProperty();
+
+ /**
+ * Forward the data to the owner of this property which then sends it
+ * over network. save is used to store the data into a stream so
+ * you have to make sure that function is working properly if you
+ * implement your own property!
+ *
+ * This function is used by send to send the data over network.
+ * This does <em>not</em> send the current value but the explicitly
+ * given value.
+ *
+ * @return TRUE if the message could be sent successfully, otherwise
+ * FALSE
+ **/
+ bool sendProperty(const QByteArray& b);
+
+ /**
+ * Causes the parent object to emit a signal on value change
+ **/
+ void emitSignal();
+
+protected:
+ KGamePropertyHandler* mOwner;
+
+ // Having this as a union of the bitfield and the char
+ // allows us to stream this quantity easily (if we need to)
+ // At the moment it is not yet transmitted
+ union Flags {
+ char flag;
+ struct {
+ // unsigned char dosave : 1; // do save this property
+ // unsigned char delaytransmit : 1; // do not send immediately on
+ // change but a KPlayer:QTimer
+ // sends it later on - fast
+ // changing variables
+ unsigned char emitsignal : 1; // KPlayer notifies on variable change (true)
+ //unsigned char readonly : 1; // whether the property can be changed (false)
+ unsigned char optimize : 1; // whether the property tries to optimize send/emit (false)
+ unsigned char dirty: 1; // whether the property dirty (setLocal() was used)
+ unsigned char policy : 2; // whether the property is always consistent (see PropertyPolicy)
+ unsigned char locked: 1; // whether the property is locked (true)
+ } bits;
+ } mFlags;
+
+private:
+ friend class KGamePropertyHandler;
+ void init();
+
+private:
+ int mId;
+
+};
+
+/**
+ * @short A class for network transparent games
+ *
+ * Note: The entire API documentation is obsolete!
+ *
+ * The class KGameProperty can store any form of data and will transmit it via
+ * network whenver you call send. This makes network transparent games
+ * very easy. You first have to register the data to a KGamePropertyHandler
+ * using KGamePropertyBase::registerData (which is called by the
+ * constructor). For the KGamePropertyHandler you can use
+ * KGame::dataHandler or KPlayer::dataHandler but you can also create your
+ * own data handler.
+ *
+ * There are several concepts you can follow when writing network games. These
+ * concepts differ completely from the way how data is transferred so you should
+ * decide which one to use. You can also mix these concepts for a single
+ * property but we do not recommend this. The concepts:
+ * <ul>
+ * <li> Always Consistent (clean)
+ * <li> Not Always Consistent (dirty)
+ * <li> A Mixture (very dirty)
+ * </ul>
+ * I repeat: we do <em>not</em> recommend the third option ("a mixture"). Unless
+ * you have a good reason for this you will probably introduce some hard to find
+ * (and to fix) bugs.
+ *
+ * @section Always consistent (clean):
+ *
+ * This "policy" is default. Whenever you create a KGameProperty it is always
+ * consistent. This means that consistency is the most important thing for the
+ * property. This is achieved by using send to change the value of the
+ * property. send needs a running KMessageServer and therefore
+ * <em>MUST</em> be plugged into a KGamePropertyHandler using either
+ * registerData or the constructor. The parent of the dataHandler must be able
+ * to send messages (see above: the message server must be running). If you use
+ * send to change the value of a property you won't see the effect
+ * immediately: The new value is first transferred to the message server which
+ * queues the message. As soon as <em>all</em> messages in the message server
+ * which are before the changed property have been transferred the message
+ * server delivers the new value of the KGameProperty to all clients. A
+ * QTimer::singleShot is used to queue the messages inside the
+ * KMessageServer.
+ *
+ * This means that if you do the following:
+ * \code
+ * KGamePropertyInt myProperty(id, dataHandler());
+ * myProperty.initData(0);
+ * myProperty = 10;
+ * int value = myProperty.value();
+ * \endcode
+ * then "value" will be "0". initData is used to initialize the property
+ * (e.g. when the KMessageServer is not yet running or can not yet be
+ * reached). This is because "myProperty = 10" or "myProperty.send(10)" send a
+ * message to the KMessageServer which uses QTimer::singleShot to
+ * queue the message. The game first has to go back into the event loop where
+ * the message is received. The KGamePropertyHandler receives the new value
+ * sets the property. So if you need the new value you need to store it in a
+ * different variable (see setLocal which creates one for you until the
+ * message is received). The KGamePropertyHandler emits a signal (unless
+ * you called setEmitSignal with false) when the new value is received:
+ * KGamePropertyHandler::signalPropertyChanged. You can use this to react
+ * to a changed property.
+ *
+ * This may look quite confusing but it has a <em>big</em> advantage: all
+ * KGameProperty objects are ensured to have the same value on all clients in
+ * the game at every time. This way you will save you a lot of trouble as
+ * debugging can be very difficult if the value of a property changes
+ * immediately on client A but only after one or two additianal messages
+ * (function calls, status changes, ...) on client B.
+ *
+ * The only disadvantage of this (clean) concept is that you cannot use a
+ * changed variable immediately but have to wait for the KMessageServer to
+ * change it. You probably want to use
+ * KGamePropertyHandler::signalPropertyChanged for this.
+ *
+ * @section Not Always Consistent (dirty):
+ *
+ * There are a lot of people who don't want to use the (sometimes quite complex)
+ * "clean" way. You can use setAlwaysConsistent to change the default
+ * behaviour of the KGameProperty. If a property is not always consistent
+ * it will use changeValue to send the property. changeValue also uses
+ * send to send the new value over network but it also uses
+ * setLocal to create a local copy of the property. This copy is created
+ * dynamically and is deleted again as soon as the next message from the network
+ * is received. To use the example above again:
+ * \code
+ * KGamePropertyInt myProperty(id, dataHandler());
+ * myProperty.setAlwaysConsistent(false);
+ * myProperty.initData(0);
+ * myProperty = 10;
+ * int value = myProperty.value();
+ * \endcode
+ * Now this example will "work" so value now is 10. Additionally the
+ * KMessageServer receives a message from the local client (just as explained
+ * above in "Always Consistent"). As soon as the message returns to the local
+ * client again the local value is deleted, as the "network value" has the same
+ * value as the local one. So you won't lose the ability to use the always
+ * consistent "clean" value of the property if you use the "dirty" way. Just use
+ * networkValue to access the value which is consistent among all clients.
+ *
+ * The advantage of this concept is clear: you can use a KGameProperty as
+ * every other variable as the changes value takes immediate effect.
+ * Additionally you can be sure that the value is transferred to all clients.
+ * You will usually not experience serious bugs just because you use the "dirty"
+ * way. Several events have to happen at once to get these "strange errors"
+ * which result in inconsistent properties (like "game running" on client A but
+ * "game ended/paused" on client B). But note that there is a very good reason
+ * for the existence of these different concepts of KGameProperty. I have
+ * myself experienced such a "strange error" and it took me several days to find
+ * the reason until I could fix it. So I personally recommend the "clean" way.
+ * On the other hand if you want to port a non-network game to a network game
+ * you will probably start with "dirty" properties as it is you will not have to
+ * change that much code...
+ *
+ * @section A Mixture (very dirty):
+ *
+ * You can also mix the concepts above. Note that we really don't recommend
+ * this. With a mixture I mean something like this:
+ * \code
+ * KGamePropertyInt myProperty(id, dataHandler());
+ * myProperty.setAlwaysConsistent(false);
+ * myProperty.initData(0);
+ * myProperty = 10;
+ * myProperty.setAlwaysConsistent(true);
+ * myProperty = 20;
+ * \endcode
+ * (totally senseless example, btw) I.e. I am speaking of mixing both concepts
+ * for a single property. Things like
+ * \code
+ * KGamePropertyInt myProperty1(id1, dataHandler());
+ * KGamePropertyInt myProperty2(id2, dataHandler());
+ * myProperty1.initData(0);
+ * myProperty2.initData(0);
+ * myProperty1.setAlwaysConsistent(false);
+ * myProperty2.setAlwaysConsistent(true);
+ * myProperty1 = 10;
+ * myProperty2 = 20;
+ * \endcode
+ * are ok. But mixing the concepts for a single property will make it nearly
+ * impossible to you to debug your game.
+ *
+ * So the right thing to do(tm) is to decide in the constructor whether you want
+ * a "clean" or "dirty" property.
+ *
+ * Even if you have decided for one of the concepts you still can manually
+ * follow another concept than the "policy" of your property. So if you use an
+ * always consistent KGameProperty you still can manually call
+ * changeValue as if it was not always consistent. Note that although this is
+ * also kind of a "mixture" as described above this is very useful sometimes. In
+ * contrast to the "mixture" above you don't have the problem that you don't
+ * exactly know which concept you are currently following because you used the
+ * function of the other concept only once.
+ *
+ * @section Custom classes:
+ *
+ * If you want to use a custum class with KGameProperty you have to implement the
+ * operators << and >> for QDataStream:
+ * \code
+ * class Card
+ * {
+ * public:
+ * int type;
+ * int suite;
+ * };
+ * QDataStream& operator<<(QDataStream& stream, Card& card)
+ * {
+ * Q_INT16 type = card.type;
+ * Q_INT16 suite = card.suite;
+ * s << type;
+ * s << suite;
+ * return s;
+ * }
+ * QDataStream& operator>>(QDataStream& stream, Card& card)
+ * {
+ * Q_INT16 type;
+ * Q_INT16 suite;
+ * s >> type;
+ * s >> suite;
+ * card.type = (int)type;
+ * card.suite = (int)suite;
+ * return s;
+ * }
+ *
+ * class Player : KPlayer
+ * {
+ * [...]
+ * KGameProperty<Card> mCards;
+ * };
+ * \endcode
+ *
+ * Note: unlike most QT classes KGameProperty objects are *not* deleted
+ * automatically! So if you create an object using e.g. KGameProperty<int>* data =
+ * new KGameProperty(id, dataHandler()) you have to put a delete data into your
+ * destructor!
+ *
+ * @author Andreas Beckermann <[email protected]>
+ **/
+template<class type>
+class KGameProperty : public KGamePropertyBase
+{
+public:
+ /**
+ * Constructs a KGameProperty object. A KGameProperty object will transmit
+ * any changes to the KMessageServer and then to all clients in the
+ * game (including the one that has sent the new value)
+ * @param id The id of this property. <em>MUST be UNIQUE</em>! Used to send and
+ * receive changes in the property of the playere automatically via
+ * network.
+ * @param owner The parent of the object. Must be a KGame which manages
+ * the changes made to this object, i.e. which will send the new data.
+ * Note that in contrast to most KDE/QT classes KGameProperty objects
+ * are <em>not</em> deleted automatically!
+ **/
+// TODO: ID: Very ugly - better use something like parent()->propertyId() or so which assigns a free id automatically.
+ KGameProperty(int id, KGamePropertyHandler* owner) : KGamePropertyBase(id, owner) { init(); }
+
+ /**
+ * This constructor does nothing. You have to call
+ * KGamePropertyBase::registerData
+ * yourself before using the KGameProperty object.
+ **/
+ KGameProperty() : KGamePropertyBase() { init(); }
+
+ virtual ~KGameProperty() {}
+
+ /**
+ * Set the value depending on the current policy (see
+ * setConsistent). By default KGameProperty just uses send to set
+ * the value of a property. This behaviour can be changed by using
+ * setConsistent.
+ * @param v The new value of the property
+ **/
+ void setValue(type v)
+ {
+ switch (policy()) {
+ case PolicyClean:
+ send(v);
+ break;
+ case PolicyDirty:
+ changeValue(v);
+ break;
+ case PolicyLocal:
+ setLocal(v);
+ break;
+ default: // NEVER!
+ kdError(11001) << "Undefined Policy in property " << id() << endl;
+ return;
+ }
+ }
+
+
+ /**
+ * This function sends a new value over network.
+ *
+ * Note that the value DOES NOT change when you call this function. This
+ * function saves the value into a QDataStream and calls
+ * sendProperty where it gets forwarded to the owner and finally the
+ * value is sent over network. The KMessageServer now sends the
+ * value to ALL clients - even the one who called this function. As soon
+ * as the value from the message server is received load is called
+ * and _then_ the value of the KGameProperty has been set.
+ *
+ * This ensures that a KGameProperty has _always_ the same value on
+ * _every_ client in the network. Note that this means you can NOT do
+ * something like
+ * \code
+ * myProperty.send(1);
+ * doSomething(myProperty);
+ * \endcode
+ * as myProperty has not yet been set when doSomething is being called.
+ *
+ * You are informed about a value change by a singal from the parent of
+ * the property which can be deactivated by setEmittingSignal because of
+ * performance (you probably don't have to deactivate it - except you
+ * want to write a real-time game like Command&Conquer with a lot of
+ * acitvity). See emitSignal
+ *
+ * Note that if there is no KMessageServer accessible - before
+ * the property has been registered to the KGamePropertyHandler (as
+ * it is the case e.g. before a KPlayer has been plugged into the
+ * KGame object) the property is *not* sent but set *locally* (see
+ * setLocal)!
+ *
+ * @param v The new value of the property
+ * @return whether the property could be sent successfully
+ * @see setValue setLocal changeValue value
+ **/
+ bool send(type v)
+ {
+ if (isOptimized() && mData == v) {
+ return true;
+ }
+ if (isLocked()) {
+ return false;
+ }
+ QByteArray b;
+ QDataStream stream(b, IO_WriteOnly);
+ stream << v;
+ if (!sendProperty(b)) {
+ setLocal(v);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This function sets the value of the property directly, i.e. it
+ * doesn't send it to the network.
+ *
+ * Int contrast to @see you change _only_ the local value when using
+ * this function. You do _not_ change the value of any other client. You
+ * probably don't want to use this if you are using a dedicated server
+ * (which is the only "client" which is allowed to change a value) but
+ * rather want to use send().
+ *
+ * But if you use your clients as servers (i.e. all clients receive a
+ * players turn and then calculate the reaction of the game theirselves)
+ * then you probably want to use setLocal as you can do things like
+ * \code
+ * myProperty.setLocal(1);
+ * doSomething(myProperty);
+ * \endcode
+ * on every client.
+ *
+ * If you want to set the value locally AND send it over network you
+ * want to call changeValue!
+ *
+ * You can also use setPolicy to set the default policy to
+ * PolicyLocal.
+ *
+ * @see setValue send changeValue value
+ **/
+ bool setLocal(type v)
+ {
+ if (isOptimized() && mData == v) {
+ return false;
+ }
+ if (isLocked()) {
+ return false;
+ }
+ mData = v;
+ setDirty(true);
+ if (isEmittingSignal()) {
+ emitSignal();
+ }
+ return true;
+ }
+
+ /**
+ * This function does both, change the local value and change the
+ * network value. The value is sent over network first, then changed
+ * locally.
+ *
+ * This function is a convenience function and just calls send
+ * followed by setLocal
+ *
+ * Note that emitSignal is also called twice: once after
+ * setLocal and once when the value from send is received
+ *
+ * @see send setLocal setValue value
+ **/
+ void changeValue(type v)
+ {
+ send(v);
+ setLocal(v);
+ }
+
+ /**
+ * Saves the object to a stream.
+ * @param stream The stream to save to
+ **/
+ virtual void save(QDataStream &stream)
+ {
+ stream << mData;
+ }
+
+ /**
+ * @return The local value (see setLocal) if it is existing,
+ * otherwise the network value which is always consistent on every
+ * client.
+ **/
+ const type& value() const
+ {
+ return mData;
+ }
+
+ /**
+ * Reads from a stream and assigns the read value to this object.
+ *
+ * This function is called automatically when a new value is received
+ * over network (i.e. it has been sent using send on this or any
+ * other client) or when a game is loaded (and maybe on some other
+ * events).
+ *
+ * Also calls emitSignal if isEmittingSignal is TRUE.
+ * @param s The stream to read from
+ **/
+ virtual void load(QDataStream& s)
+ {
+ s >> mData;
+ setDirty(false);
+ if (isEmittingSignal()) {
+ emitSignal();
+ }
+ }
+
+ /**
+ * This calls setValue to change the value of the property. Note
+ * that depending on the policy (see setAlwaysConsistent) the
+ * returned value might be different from the assigned value!!
+ *
+ * So if you use setPolicy(PolicyClean):
+ * \code
+ * int a, b = 10;
+ * myProperty = b;
+ * a = myProperty.value();
+ * \endcode
+ * Here a and b would differ!
+ * The value is actually set as soon as it is received from the
+ * KMessageServer which forwards it to ALL clients in the network.
+ *
+ * If you use a clean policy (see setPolicy) then
+ * the returned value is the assigned value
+ **/
+ const type& operator=(const type& t)
+ {
+ setValue(t);
+ return value();
+ }
+
+ /**
+ * This copies the data of property to the KGameProperty object.
+ *
+ * Equivalent to setValue(property.value());
+ **/
+ const type& operator=(const KGameProperty& property)
+ {
+ setValue(property.value());
+ return value();
+ }
+
+ /**
+ * Yeah, you can do it!
+ * \code
+ * int a = myGamePropertyInt;
+ * \endcode
+ * If you don't see it: you don't have to use integerData.value()
+ **/
+ operator type() const { return value(); }
+
+ virtual const type_info* typeinfo() { return &typeid(type); }
+
+private:
+ void init() { }
+
+private:
+ type mData;
+};
+
+
+typedef KGameProperty<int> KGamePropertyInt;
+typedef KGameProperty<unsigned int> KGamePropertyUInt;
+typedef KGameProperty<QString> KGamePropertyQString;
+typedef KGameProperty<Q_INT8> KGamePropertyBool;
+
+#endif
diff --git a/libkdegames/kgame/kgamepropertyarray.h b/libkdegames/kgame/kgamepropertyarray.h
new file mode 100644
index 00000000..f91bd75c
--- /dev/null
+++ b/libkdegames/kgame/kgamepropertyarray.h
@@ -0,0 +1,309 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEPROPERTYARRAY_H_
+#define __KGAMEPROPERTYARRAY_H_
+
+#include <qdatastream.h>
+#include <kdebug.h>
+
+#include "kgamemessage.h"
+#include "kgameproperty.h"
+#include "kgamepropertyhandler.h"
+
+
+template<class type>
+class KGamePropertyArray : public QMemArray<type>, public KGamePropertyBase
+{
+public:
+ KGamePropertyArray() :QMemArray<type>(), KGamePropertyBase()
+ {
+ //kdDebug(11001) << "KGamePropertyArray init" << endl;
+ }
+
+ KGamePropertyArray( int size )
+ {
+ resize(size);
+ }
+
+ KGamePropertyArray( const KGamePropertyArray<type> &a ) : QMemArray<type>(a)
+ {
+ }
+
+ bool resize( uint size )
+ {
+ if (size!=QMemArray<type>::size())
+ {
+ bool a=true;
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdResize);
+ s << size ;
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ extractProperty(b);
+// a=QMemArray<type>::resize(size);// FIXME: return value!
+ }
+ return a;
+ }
+ else return true;
+ }
+
+ void setAt(uint i,type data)
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdAt);
+ s << i ;
+ s << data;
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ extractProperty(b);
+ }
+ //kdDebug(11001) << "KGamePropertyArray setAt send COMMAND for id="<<id() << " type=" << 1 << " at(" << i<<")="<<data << endl;
+ }
+
+ type at( uint i ) const
+ {
+ return QMemArray<type>::at(i);
+ }
+
+ type operator[]( int i ) const
+ {
+ return QMemArray<type>::at(i);
+ }
+
+ KGamePropertyArray<type> &operator=(const KGamePropertyArray<type> &a)
+ {
+ return assign(a);
+ }
+
+ bool truncate( uint pos )
+ {
+ return resize(pos);
+ }
+
+ bool fill( const type &data, int size = -1 )
+ {
+ bool r=true;
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdFill);
+ s << data;
+ s << size ;
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ extractProperty(b);
+// r=QMemArray<type>::fill(data,size);//FIXME: return value!
+ }
+ return r;
+ }
+
+ KGamePropertyArray<type>& assign( const KGamePropertyArray<type>& a )
+ {
+// note: send() has been replaced by sendProperty so it might be broken now!
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::assign(a);
+ }
+ return *this;
+ }
+ KGamePropertyArray<type>& assign( const type *a, uint n )
+ {
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::assign(a,n);
+ }
+ return *this;
+ }
+ KGamePropertyArray<type>& duplicate( const KGamePropertyArray<type>& a )
+ {
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::duplicate(a);
+ }
+ return *this;
+ }
+ KGamePropertyArray<type>& duplicate( const type *a, uint n )
+ {
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::duplicate(a,n);
+ }
+ return *this;
+ }
+ KGamePropertyArray<type>& setRawData( const type *a, uint n )
+ {
+ if (policy()==PolicyClean || policy()==PolicyDirty)
+ {
+ sendProperty();
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ QMemArray<type>::setRawData(a,n);
+ }
+ return *this;
+ }
+ void sort()
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdSort);
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy()==PolicyLocal || policy()==PolicyDirty)
+ {
+ extractProperty(b);
+ }
+ }
+
+ void load(QDataStream& s)
+ {
+ //kdDebug(11001) << "KGamePropertyArray load " << id() << endl;
+ type data;
+ for (unsigned int i=0; i<QMemArray<type>::size(); i++)
+ {
+ s >> data;
+ QMemArray<type>::at(i)=data;
+ }
+ if (isEmittingSignal())
+ {
+ emitSignal();
+ }
+ }
+ void save(QDataStream &s)
+ {
+ //kdDebug(11001) << "KGamePropertyArray save "<<id() << endl;
+ for (unsigned int i=0; i<QMemArray<type>::size(); i++)
+ {
+ s << at(i);
+ }
+ }
+
+ void command(QDataStream &s,int cmd,bool)
+ {
+ KGamePropertyBase::command(s, cmd);
+ //kdDebug(11001) << "Array id="<<id()<<" got command ("<<cmd<<") !!!" <<endl;
+ switch(cmd)
+ {
+ case CmdAt:
+ {
+ uint i;
+ type data;
+ s >> i >> data;
+ QMemArray<type>::at(i)=data;
+ //kdDebug(11001) << "CmdAt:id="<<id()<<" i="<<i<<" data="<<data <<endl;
+ if (isEmittingSignal())
+ {
+ emitSignal();
+ }
+ break;
+ }
+ case CmdResize:
+ {
+ uint size;
+ s >> size;
+ //kdDebug(11001) << "CmdResize:id="<<id()<<" oldsize="<<QMemArray<type>::size()<<" newsize="<<size <<endl;
+ if (QMemArray<type>::size() != size)
+ {
+ QMemArray<type>::resize(size);
+ }
+ break;
+ }
+ case CmdFill:
+ {
+ int size;
+ type data;
+ s >> data >> size;
+ //kdDebug(11001) << "CmdFill:id="<<id()<<"size="<<size <<endl;
+ QMemArray<type>::fill(data,size);
+ if (isEmittingSignal())
+ {
+ emitSignal();
+ }
+ break;
+ }
+ case CmdSort:
+ {
+ //kdDebug(11001) << "CmdSort:id="<<id()<<endl;
+ QMemArray<type>::sort();
+ break;
+ }
+ default:
+ kdError(11001) << "Error in KPropertyArray::command: Unknown command " << cmd << endl;
+ break;
+ }
+ }
+protected:
+ void extractProperty(const QByteArray& b)
+ {
+ QDataStream s(b, IO_ReadOnly);
+ int cmd;
+ int propId;
+ KGameMessage::extractPropertyHeader(s, propId);
+ KGameMessage::extractPropertyCommand(s, propId, cmd);
+ command(s, cmd, true);
+ }
+
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamepropertyhandler.cpp b/libkdegames/kgame/kgamepropertyhandler.cpp
new file mode 100644
index 00000000..75b3e7e2
--- /dev/null
+++ b/libkdegames/kgame/kgamepropertyhandler.cpp
@@ -0,0 +1,407 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgamepropertyhandler.h"
+#include "kgameproperty.h"
+#include "kgamemessage.h"
+
+#include <qmap.h>
+#include <qptrqueue.h>
+
+#include <klocale.h>
+#include <typeinfo>
+
+#define KPLAYERHANDLER_LOAD_COOKIE 6239
+
+//---------------------- KGamePropertyHandler -----------------------------------
+class KGamePropertyHandlerPrivate
+{
+public:
+ KGamePropertyHandlerPrivate()
+ {
+ }
+
+ QMap<int, QString> mNameMap;
+ QIntDict<KGamePropertyBase> mIdDict;
+ int mUniqueId;
+ int mId;
+ KGamePropertyBase::PropertyPolicy mDefaultPolicy;
+ bool mDefaultUserspace;
+ int mIndirectEmit;
+ QPtrQueue<KGamePropertyBase> mSignalQueue;
+};
+
+KGamePropertyHandler::KGamePropertyHandler(int id, const QObject* receiver, const char * sendf, const char *emitf, QObject* parent) : QObject(parent)
+{
+ init();
+ registerHandler(id,receiver,sendf,emitf);
+}
+
+KGamePropertyHandler::KGamePropertyHandler(QObject* parent) : QObject(parent)
+{
+ init();
+}
+
+KGamePropertyHandler::~KGamePropertyHandler()
+{
+ clear();
+ delete d;
+}
+
+void KGamePropertyHandler::init()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this << endl;
+ d = new KGamePropertyHandlerPrivate; // for future use - is BC important to us?
+ d->mId = 0;
+ d->mUniqueId=KGamePropertyBase::IdAutomatic;
+ d->mDefaultPolicy=KGamePropertyBase::PolicyLocal;
+ d->mDefaultUserspace=true;
+ d->mIndirectEmit=0;
+}
+
+
+int KGamePropertyHandler::id() const
+{
+ return d->mId;
+}
+
+void KGamePropertyHandler::setId(int id)
+{
+ d->mId = id;
+}
+
+void KGamePropertyHandler::registerHandler(int id,const QObject * receiver, const char * sendf, const char *emitf)
+{
+ setId(id);
+ if (receiver && sendf) {
+ kdDebug(11001) << "Connecting SLOT " << sendf << endl;
+ connect(this, SIGNAL(signalSendMessage(int, QDataStream &, bool*)), receiver, sendf);
+ }
+ if (receiver && emitf) {
+ kdDebug(11001) << "Connecting SLOT " << emitf << endl;
+ connect(this, SIGNAL(signalPropertyChanged(KGamePropertyBase *)), receiver, emitf);
+ }
+}
+
+bool KGamePropertyHandler::processMessage(QDataStream &stream, int id, bool isSender)
+{
+// kdDebug(11001) << k_funcinfo << ": id=" << id << " mId=" << d->mId << endl;
+ if (id != d->mId) {
+ return false; // Is the message meant for us?
+ }
+ KGamePropertyBase* p;
+ int propertyId;
+ KGameMessage::extractPropertyHeader(stream, propertyId);
+// kdDebug(11001) << k_funcinfo << ": Got property " << propertyId << endl;
+ if (propertyId==KGamePropertyBase::IdCommand) {
+ int cmd;
+ KGameMessage::extractPropertyCommand(stream, propertyId, cmd);
+//kdDebug(11001) << k_funcinfo << ": Got COMMAND for id= "<<propertyId <<endl;
+ p = d->mIdDict.find(propertyId);
+ if (p) {
+ if (!isSender || p->policy()==KGamePropertyBase::PolicyClean) {
+ p->command(stream, cmd, isSender);
+ }
+ } else {
+ kdError(11001) << k_funcinfo << ": (cmd): property " << propertyId << " not found" << endl;
+ }
+ return true;
+ }
+ p = d->mIdDict.find(propertyId);
+ if (p) {
+ //kdDebug(11001) << k_funcinfo << ": Loading " << propertyId << endl;
+ if (!isSender || p->policy()==KGamePropertyBase::PolicyClean) {
+ p->load(stream);
+ }
+ } else {
+ kdError(11001) << k_funcinfo << ": property " << propertyId << " not found" << endl;
+ }
+ return true;
+}
+
+bool KGamePropertyHandler::removeProperty(KGamePropertyBase* data)
+{
+ if (!data) {
+ return false;
+ }
+ d->mNameMap.erase(data->id());
+ return d->mIdDict.remove(data->id());
+}
+
+bool KGamePropertyHandler::addProperty(KGamePropertyBase* data, QString name)
+{
+ //kdDebug(11001) << k_funcinfo << ": " << data->id() << endl;
+ if (d->mIdDict.find(data->id())) {
+ // this id already exists
+ kdError(11001) << " -> cannot add property " << data->id() << endl;
+ return false;
+ } else {
+ d->mIdDict.insert(data->id(), data);
+ // if here is a check for "is_debug" or so we can add the strings only in debug mode
+ // and save memory!!
+ if (!name.isNull()) {
+ d->mNameMap[data->id()] = name;
+ //kdDebug(11001) << k_funcinfo << ": nid="<< (data->id()) << " inserted in Map name=" << d->mNameMap[data->id()] <<endl;
+ //kdDebug(11001) << "Typeid=" << typeid(data).name() << endl;
+ //kdDebug(11001) << "Typeid call=" << data->typeinfo()->name() << endl;
+ }
+ }
+ return true;
+}
+
+QString KGamePropertyHandler::propertyName(int id) const
+{
+ QString s;
+ if (d->mIdDict.find(id)) {
+ if (d->mNameMap.contains(id)) {
+ s = i18n("%1 (%2)").arg(d->mNameMap[id]).arg(id);
+ } else {
+ s = i18n("Unnamed - ID: %1").arg(id);
+ }
+ } else {
+ // Should _never_ happen
+ s = i18n("%1 unregistered").arg(id);
+ }
+ return s;
+}
+
+bool KGamePropertyHandler::load(QDataStream &stream)
+{
+ // Prevent direct emmiting until all is loaded
+ lockDirectEmit();
+ uint count,i;
+ stream >> count;
+ kdDebug(11001) << k_funcinfo << ": " << count << " KGameProperty objects " << endl;
+ for (i = 0; i < count; i++) {
+ processMessage(stream, id(),false);
+ }
+ Q_INT16 cookie;
+ stream >> cookie;
+ if (cookie == KPLAYERHANDLER_LOAD_COOKIE) {
+ kdDebug(11001) << " KGamePropertyHandler loaded propertly"<<endl;
+ } else {
+ kdError(11001) << "KGamePropertyHandler loading error. probably format error"<<endl;
+ }
+ // Allow direct emmiting (if no other lock still holds)
+ unlockDirectEmit();
+ return true;
+}
+
+bool KGamePropertyHandler::save(QDataStream &stream)
+{
+ kdDebug(11001) << k_funcinfo << ": " << d->mIdDict.count() << " KGameProperty objects " << endl;
+ stream << (uint)d->mIdDict.count();
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ KGamePropertyBase *base=it.current();
+ if (base) {
+ KGameMessage::createPropertyHeader(stream, base->id());
+ base->save(stream);
+ }
+ ++it;
+ }
+ stream << (Q_INT16)KPLAYERHANDLER_LOAD_COOKIE;
+ return true;
+}
+
+KGamePropertyBase::PropertyPolicy KGamePropertyHandler::policy()
+{
+// kdDebug(11001) << k_funcinfo << ": " << d->mDefaultPolicy << endl;
+ return d->mDefaultPolicy;
+}
+void KGamePropertyHandler::setPolicy(KGamePropertyBase::PropertyPolicy p,bool userspace)
+{
+ // kdDebug(11001) << k_funcinfo << ": " << p << endl;
+ d->mDefaultPolicy=p;
+ d->mDefaultUserspace=userspace;
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ if (!userspace || it.current()->id()>=KGamePropertyBase::IdUser) {
+ it.current()->setPolicy((KGamePropertyBase::PropertyPolicy)p);
+ }
+ ++it;
+ }
+}
+
+void KGamePropertyHandler::unlockProperties()
+{
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ it.current()->unlock();
+ ++it;
+ }
+}
+
+void KGamePropertyHandler::lockProperties()
+{
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ it.current()->lock();
+ ++it;
+ }
+}
+
+int KGamePropertyHandler::uniquePropertyId()
+{
+ return d->mUniqueId++;
+}
+
+void KGamePropertyHandler::flush()
+{
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ if (it.current()->isDirty()) {
+ it.current()->sendProperty();
+ }
+ ++it;
+ }
+}
+
+/* Fire all property signal changed which are collected in
+ * the queque
+ **/
+void KGamePropertyHandler::lockDirectEmit()
+{
+ d->mIndirectEmit++;
+}
+
+void KGamePropertyHandler::unlockDirectEmit()
+{
+ // If the flag is <=0 we emit the queued signals
+ d->mIndirectEmit--;
+ if (d->mIndirectEmit<=0)
+ {
+ KGamePropertyBase *prop;
+ while((prop=d->mSignalQueue.dequeue()) != 0)
+ {
+ // kdDebug(11001) << "emmiting signal for " << prop->id() << endl;
+ emit signalPropertyChanged(prop);
+ }
+ }
+}
+
+void KGamePropertyHandler::emitSignal(KGamePropertyBase *prop)
+{
+ // If the indirect flag is set (load and network transmit)
+ // we cannot emit the signals directly as it can happend that
+ // a sigal causes an access to a property which is e.g. not
+ // yet loaded or received
+
+ if (d->mIndirectEmit>0)
+ {
+ // Queque the signal
+ d->mSignalQueue.enqueue(prop);
+ }
+ else
+ {
+ // directly emit
+ emit signalPropertyChanged(prop);
+ }
+}
+
+bool KGamePropertyHandler::sendProperty(QDataStream &s)
+{
+ bool sent = false;
+ emit signalSendMessage(id(), s, &sent);
+ return sent;
+}
+
+KGamePropertyBase *KGamePropertyHandler::find(int id)
+{
+ return d->mIdDict.find(id);
+}
+
+void KGamePropertyHandler::clear()
+{
+ kdDebug(11001) << k_funcinfo << id() << endl;
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.toFirst()) {
+ KGamePropertyBase* p = it.toFirst();
+ p->unregisterData();
+ if (d->mIdDict.find(p->id())) {
+ // shouldn't happen - but if mOwner in KGamePropertyBase is NULL
+ // this might be possible
+ removeProperty(p);
+ }
+ }
+}
+
+QIntDict<KGamePropertyBase>& KGamePropertyHandler::dict() const
+{
+ return d->mIdDict;
+}
+
+QString KGamePropertyHandler::propertyValue(KGamePropertyBase* prop)
+{
+ if (!prop) {
+ return i18n("NULL pointer");
+ }
+
+ int id = prop->id();
+ QString name = propertyName(id);
+ QString value;
+
+ const type_info* t = prop->typeinfo();
+ if (*t == typeid(int)) {
+ value = QString::number(((KGamePropertyInt*)prop)->value());
+ } else if (*t == typeid(unsigned int)) {
+ value = QString::number(((KGamePropertyUInt *)prop)->value());
+ } else if (*t == typeid(long int)) {
+ value = QString::number(((KGameProperty<long int> *)prop)->value());
+ } else if (*t == typeid(unsigned long int)) {
+ value = QString::number(((KGameProperty<unsigned long int> *)prop)->value());
+ } else if (*t == typeid(QString)) {
+ value = ((KGamePropertyQString*)prop)->value();
+ } else if (*t == typeid(Q_INT8)) {
+ value = ((KGamePropertyBool*)prop)->value() ? i18n("True") : i18n("False");
+ } else {
+ emit signalRequestValue(prop, value);
+ }
+
+ if (value.isNull()) {
+ value = i18n("Unknown");
+ }
+ return value;
+}
+
+void KGamePropertyHandler::Debug()
+{
+ kdDebug(11001) << "-----------------------------------------------------------" << endl;
+ kdDebug(11001) << "KGamePropertyHandler:: Debug this=" << this << endl;
+
+ kdDebug(11001) << " Registered properties: (Policy,Lock,Emit,Optimized, Dirty)" << endl;
+ QIntDictIterator<KGamePropertyBase> it(d->mIdDict);
+ while (it.current()) {
+ KGamePropertyBase *p=it.current();
+ kdDebug(11001) << " "<< p->id() << ": p=" << p->policy()
+ << " l="<<p->isLocked()
+ << " e="<<p->isEmittingSignal()
+ << " o=" << p->isOptimized()
+ << " d="<<p->isDirty()
+ << endl;
+ ++it;
+ }
+ kdDebug(11001) << "-----------------------------------------------------------" << endl;
+}
+
+#include "kgamepropertyhandler.moc"
diff --git a/libkdegames/kgame/kgamepropertyhandler.h b/libkdegames/kgame/kgamepropertyhandler.h
new file mode 100644
index 00000000..6147c071
--- /dev/null
+++ b/libkdegames/kgame/kgamepropertyhandler.h
@@ -0,0 +1,353 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+ Copyright (C) 2001 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEPROPERTYHANDLER_H_
+#define __KGAMEPROPERTYHANDLER_H_
+
+#include <qobject.h>
+#include <qintdict.h>
+
+#include "kgameproperty.h"
+#include <kdemacros.h>
+
+class QDataStream;
+class KGame;
+class KPlayer;
+//class KGamePropertyBase;
+
+class KGamePropertyHandlerPrivate; // wow - what a name ;-)
+
+/**
+ * @short A collection class for KGameProperty objects
+ *
+ * The KGamePropertyHandler class is some kind of a collection class for
+ * KGameProperty. You usually don't have to create one yourself, as both
+ * KPlayer and KGame provide a handler. In most cases you do not even have
+ * to care about the KGamePropertHandler. KGame and KPlayer implement
+ * all features of KGamePropertyHandler so you will rather use it there.
+ *
+ * You have to use the KGamePropertyHandler as parent for all KGameProperty
+ * objects but you can also use KPlayer or KGame as parent - then
+ * KPlayer::dataHandler or KGame::dataHandler will be used.
+ *
+ * Every KGamePropertyHandler must have - just like every KGameProperty -
+ * a unique ID. This ID is provided either in the constructor or in
+ * registerHandler. The ID is used to assign an incoming message (e.g. a changed
+ * property) to the correct handler. Inside the handler the property ID is used
+ * to change the correct property.
+ *
+ * The constructor or registerHandler takes 3 addittional arguments: a
+ * receiver and two slots. The first slot is connected to
+ * signalSendMessage, the second to signalPropertyChanged. You must provide
+ * these in order to use the KGamePropertyHandler.
+ *
+ * The most important function of KGamePropertyHandler is processMessage
+ * which assigns an incoming value to the correct property.
+ *
+ * A KGamePropertyHandler is also used - indirectly using emitSignal - to
+ * emit a signal when the value of a property changes. This is done this way
+ * because a KGameProperty does not inherit QObject because of memory
+ * advantages. Many games can have dozens or even hundreds of KGameProperty
+ * objects so every additional variable in KGameProperty would be
+ * multiplied.
+ *
+ **/
+class KDE_EXPORT KGamePropertyHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Construct an unregistered KGamePropertyHandler
+ *
+ * You have to call registerHandler before you can use this
+ * handler!
+ **/
+ KGamePropertyHandler(QObject* parent = 0);
+
+ /**
+ * Construct a registered handler.
+ *
+ * @see registerHandler
+ **/
+ KGamePropertyHandler(int id, const QObject* receiver, const char* sendf, const char* emitf, QObject* parent = 0);
+ ~KGamePropertyHandler();
+
+ /**
+ * Register the handler with a parent. This is to use
+ * if the constructor without arguments has been chosen.
+ * Otherwise you need not call this.
+ *
+ * @param id The id of the message to listen for
+ * @param receiver The object that will receive the signals of
+ * KGamePropertyHandler
+ * @param send A slot that is being connected to signalSendMessage
+ * @param emit A slot that is being connected to signalPropertyChanged
+ **/
+ void registerHandler(int id, const QObject *receiver, const char * send, const char *emit);
+
+ /**
+ * Main message process function. This has to be called by
+ * the parent's message event handler. If the id of the message
+ * agrees with the id of the handler, the message is extracted
+ * and processed. Otherwise false is returned.
+ * Example:
+ * \code
+ * if (mProperties.processMessage(stream,msgid,sender==gameId())) return ;
+ * \endcode
+ *
+ * @param stream The data stream containing the message
+ * @param id the message id of the message
+ * @param isSender Whether the receiver is also the sender
+ * @return true on message processed otherwise false
+ **/
+ bool processMessage(QDataStream &stream, int id, bool isSender );
+
+ /**
+ * @return the id of the handler
+ **/
+ int id() const;
+
+ /**
+ * Adds a KGameProperty property to the handler
+ * @param data the property
+ * @param name A description of the property, which will be returned by
+ * propertyName. This is used for debugging, e.g. in KGameDebugDialog
+ * @return true on success
+ **/
+ bool addProperty(KGamePropertyBase *data, QString name=0);
+
+ /**
+ * Removes a property from the handler
+ * @param data the property
+ * @return true on success
+ **/
+ bool removeProperty(KGamePropertyBase *data);
+
+ /**
+ * returns a unique property ID starting called usually with a base of
+ * KGamePropertyBase::IdAutomatic. This is used internally by
+ * the property base to assign automtic id's. Not much need to
+ * call this yourself.
+ **/
+ int uniquePropertyId();
+
+
+ /**
+ * Loads properties from the datastream
+ *
+ * @param stream the datastream to load from
+ * @return true on success otherwise false
+ **/
+ virtual bool load(QDataStream &stream);
+
+ /**
+ * Saves properties into the datastream
+ *
+ * @param stream the datastream to save to
+ * @return true on success otherwise false
+ **/
+ virtual bool save(QDataStream &stream);
+
+ /**
+ * called by a property to send itself into the
+ * datastream. This call is simply forwarded to
+ * the parent object
+ **/
+ bool sendProperty(QDataStream &s);
+
+ void sendLocked(bool l);
+
+ /**
+ * called by a property to emit a signal
+ * This call is simply forwarded to
+ * the parent object
+ **/
+ void emitSignal(KGamePropertyBase *data);
+
+ /**
+ * @param id The ID of the property
+ * @return A name of the property which can be used for debugging. Don't
+ * depend on this function! It it possible not to provide a name or to
+ * provide the same name for multiple properties!
+ **/
+ QString propertyName(int id) const;
+
+ /**
+ * @param id The ID of the property. See KGamePropertyBase::id
+ * @return The KGameProperty this ID is assigned to
+ **/
+ KGamePropertyBase *find(int id);
+
+ /**
+ * Clear the KGamePropertyHandler. Note that the properties are
+ * <em>not</em> deleted so if you created your KGameProperty
+ * objects dynamically like
+ * \code
+ * KGamePropertyInt* myProperty = new KGamePropertyInt(id, dataHandler());
+ * \endcode
+ * you also have to delete it:
+ * \code
+ * dataHandler()->clear();
+ * delete myProperty;
+ * \endcode
+ **/
+ void clear();
+
+ /**
+ * Use id as new ID for this KGamePropertyHandler. This is used
+ * internally only.
+ **/
+ void setId(int id);//AB: TODO: make this protected in KGamePropertyHandler!!
+
+ /**
+ * Calls KGamePropertyBase::setReadOnly(false) for all properties of this
+ * player. See also lockProperties
+ **/
+ void unlockProperties();
+
+ /**
+ * Set the policy for all kgame variables which are currently registerd in
+ * the KGame proeprty handler. See KGamePropertyBase::setPolicy
+ *
+ * @param p is the new policy for all properties of this handler
+ * @param userspace if userspace is true (default) only user properties are changed.
+ * Otherwise the system properties are also changed.
+ **/
+ void setPolicy(KGamePropertyBase::PropertyPolicy p, bool userspace=true);
+
+ /**
+ * Called by the KGame or KPlayer object or the handler itself to delay
+ * emmiting of signals. Lockign keeps a counter and unlock is only achieved
+ * when every lock is canceld by an unlock.
+ * While this is set signals are quequed and only emmited after this
+ * is reset. Its deeper meaning is to prevent inconsistencies in a game
+ * load or network transfer where a emit could access a property not
+ * yet loaded or transmitted. Calling this by yourself you better know
+ * what your are doing.
+ **/
+ void lockDirectEmit();
+
+ /**
+ * Removes the lock from the emitting of property signals. Corresponds to
+ * the lockIndirectEmits
+ **/
+ void unlockDirectEmit();
+
+ /**
+ * Returns the default policy for this property handler. All properties
+ * registered newly, will have this property.
+ **/
+ KGamePropertyBase::PropertyPolicy policy();
+
+ /**
+ * Calls KGamePropertyBase::setReadOnly(true) for all properties of this
+ * handler
+ *
+ * Use with care! This will even lock the core properties, like name,
+ * group and myTurn!!
+ *
+ * @see unlockProperties
+ **/
+ void lockProperties();
+
+ /**
+ * Sends all properties which are marked dirty over the network. This will
+ * make a forced synchornisation of the properties and mark them all not dirty.
+ **/
+ void flush();
+
+ /**
+ * Reference to the internal dictionary
+ **/
+ QIntDict<KGamePropertyBase> &dict() const;
+
+ /**
+ * In several situations you just want to have a QString of a
+ * KGameProperty object. This is the case in the
+ * KGameDebugDialog where the value of all properties is displayed. This
+ * function will provide you with such a QString for all the types
+ * used inside of all KGame classes. If you have a non-standard
+ * property (probably a self defined class or something like this) you
+ * also need to connect to signalRequestValue to make this function
+ * useful.
+ * @param property Return the value of this KGameProperty
+ * @return The value of a KGameProperty
+ **/
+ QString propertyValue(KGamePropertyBase* property);
+
+
+ /**
+ * Writes some debug output to the console.
+ **/
+ void Debug();
+
+
+signals:
+ /**
+ * This is emitted by a property. KGamePropertyBase::emitSignal
+ * calls emitSignal which emits this signal.
+ *
+ * This signal is emitted whenever the property is changed. Note that
+ * you can switch off this behaviour using
+ * KGamePropertyBase::setEmittingSignal in favor of performance. Note
+ * that you won't experience any performance loss using signals unless
+ * you use dozens or hundreds of properties which change very often.
+ **/
+ void signalPropertyChanged(KGamePropertyBase *);
+
+ /**
+ * This signal is emitted when a property needs to be sent. Only the
+ * parent has to react to this.
+ * @param msgid The id of the handler
+ * @param sent set this to true if the property was sent successfully -
+ * otherwise don't touch
+ **/
+ void signalSendMessage(int msgid, QDataStream &, bool* sent); // AB shall we change bool* into bool& again?
+
+ /**
+ * If you call propertyValue with a non-standard KGameProperty
+ * it is possible that the value cannot automatically be converted into a
+ * QString. Then this signal is emitted and asks you to provide the
+ * correct value. You probably want to use something like this to achieve
+ * this:
+ * \code
+ * #include <typeinfo>
+ * void slotRequestValue(KGamePropertyBase* p, QString& value)
+ * {
+ * if (*(p->typeinfo()) == typeid(MyType) {
+ * value = QString(((KGameProperty<MyType>*)p)->value());
+ * }
+ * }
+ * \endcode
+ *
+ * @param property The KGamePropertyBase the value is requested for
+ * @param value The value of this property. You have to set this.
+ **/
+ void signalRequestValue(KGamePropertyBase* property, QString& value);
+
+private:
+ void init();
+
+private:
+ KGamePropertyHandlerPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamepropertylist.h b/libkdegames/kgame/kgamepropertylist.h
new file mode 100644
index 00000000..3a304e70
--- /dev/null
+++ b/libkdegames/kgame/kgamepropertylist.h
@@ -0,0 +1,258 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KGAMEPROPERTYLIST_H_
+#define __KGAMEPROPERTYLIST_H_
+
+#include <qvaluelist.h>
+
+#include <kdebug.h>
+
+#include "kgamemessage.h"
+#include "kgameproperty.h"
+#include "kgamepropertyhandler.h"
+
+// AB: also see README.LIB!
+
+template<class type>
+class KGamePropertyList : public QValueList<type>, public KGamePropertyBase
+{
+public:
+ /**
+ * Typedefs
+ */
+ typedef QValueListIterator<type> Iterator;
+ typedef QValueListConstIterator<type> ConstIterator;
+
+ KGamePropertyList() :QValueList<type>(), KGamePropertyBase()
+ {
+ }
+
+ KGamePropertyList( const KGamePropertyList<type> &a ) : QValueList<type>(a)
+ {
+ }
+
+ uint findIterator(Iterator me)
+ {
+ Iterator it;
+ uint cnt=0;
+ for( it = this->begin(); it != this->end(); ++it )
+ {
+ if (me==it)
+ {
+ return cnt;
+ }
+ cnt++;
+ }
+ return this->count();
+ }
+
+ Iterator insert( Iterator it, const type& d )
+ {
+ it=QValueList<type>::insert(it,d);
+
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdInsert);
+ int i=findIterator(it);
+ s << i;
+ s << d;
+ if (policy() == PolicyClean || policy() == PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy() == PolicyDirty || policy() == PolicyLocal)
+ {
+ extractProperty(b);
+ }
+ return it;
+ }
+
+ void prepend( const type& d) { insert(this->begin(),d); }
+
+ void append( const type& d )
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdAppend);
+ s << d;
+ if (policy() == PolicyClean || policy() == PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy() == PolicyDirty || policy() == PolicyLocal)
+ {
+ extractProperty(b);
+ }
+ }
+
+ Iterator erase( Iterator it )
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdRemove);
+ int i=findIterator(it);
+ s << i;
+ if (policy() == PolicyClean || policy() == PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy() == PolicyDirty || policy() == PolicyLocal)
+ {
+ extractProperty(b);
+ }
+ //TODO: return value - is it correct for PolicyLocal|PolicyDirty?
+// return QValueList<type>::remove(it);
+ return it;
+ }
+
+ Iterator remove( Iterator it )
+ {
+ return erase(it);
+ }
+
+ void remove( const type& d )
+ {
+ Iterator it=find(d);
+ remove(it);
+ }
+
+ void clear()
+ {
+ QByteArray b;
+ QDataStream s(b, IO_WriteOnly);
+ KGameMessage::createPropertyCommand(s,KGamePropertyBase::IdCommand,id(),CmdClear);
+ if (policy() == PolicyClean || policy() == PolicyDirty)
+ {
+ if (mOwner)
+ {
+ mOwner->sendProperty(s);
+ }
+ }
+ if (policy() == PolicyDirty || policy() == PolicyLocal)
+ {
+ extractProperty(b);
+ }
+ }
+
+ void load(QDataStream& s)
+ {
+ kdDebug(11001) << "KGamePropertyList load " << id() << endl;
+ QValueList<type>::clear();
+ uint size;
+ type data;
+ s >> size;
+
+ for (unsigned int i=0;i<size;i++)
+ {
+ s >> data;
+ QValueList<type>::append(data);
+ }
+ if (isEmittingSignal()) emitSignal();
+ }
+
+ void save(QDataStream &s)
+ {
+ kdDebug(11001) << "KGamePropertyList save "<<id() << endl;
+ type data;
+ uint size=this->count();
+ s << size;
+ Iterator it;
+ for( it = this->begin(); it != this->end(); ++it )
+ {
+ data=*it;
+ s << data;
+ }
+ }
+
+ void command(QDataStream &s,int cmd,bool)
+ {
+ KGamePropertyBase::command(s, cmd);
+ kdDebug(11001) << "---> LIST id="<<id()<<" got command ("<<cmd<<") !!!" <<endl;
+ Iterator it;
+ switch(cmd)
+ {
+ case CmdInsert:
+ {
+ uint i;
+ type data;
+ s >> i >> data;
+ it=this->at(i);
+ QValueList<type>::insert(it,data);
+// kdDebug(11001) << "CmdInsert:id="<<id()<<" i="<<i<<" data="<<data <<endl;
+ if (isEmittingSignal()) emitSignal();
+ break;
+ }
+ case CmdAppend:
+ {
+ type data;
+ s >> data;
+ QValueList<type>::append(data);
+// kdDebug(11001) << "CmdAppend:id=" << id() << " data=" << data << endl;
+ if (isEmittingSignal()) emitSignal();
+ break;
+ }
+ case CmdRemove:
+ {
+ uint i;
+ s >> i;
+ it=this->at(i);
+ QValueList<type>::remove(it);
+ kdDebug(11001) << "CmdRemove:id="<<id()<<" i="<<i <<endl;
+ if (isEmittingSignal()) emitSignal();
+ break;
+ }
+ case CmdClear:
+ {
+ QValueList<type>::clear();
+ kdDebug(11001) << "CmdClear:id="<<id()<<endl;
+ if (isEmittingSignal()) emitSignal();
+ break;
+ }
+ default:
+ kdDebug(11001) << "Error in KPropertyList::command: Unknown command " << cmd << endl;
+ }
+ }
+
+protected:
+ void extractProperty(const QByteArray& b)
+ // this is called for Policy[Dirty|Local] after putting the stuff into the
+ // stream
+ {
+ QDataStream s(b, IO_ReadOnly);
+ int cmd;
+ int propId;
+ KGameMessage::extractPropertyHeader(s, propId);
+ KGameMessage::extractPropertyCommand(s, propId, cmd);
+ command(s, cmd, true);
+ }
+
+};
+
+#endif
diff --git a/libkdegames/kgame/kgamesequence.cpp b/libkdegames/kgame/kgamesequence.cpp
new file mode 100644
index 00000000..984a9315
--- /dev/null
+++ b/libkdegames/kgame/kgamesequence.cpp
@@ -0,0 +1,125 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Andreas Beckermann ([email protected])
+ Copyright (C) 2003 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+#include "kgamesequence.h"
+#include "kgamesequence.moc"
+
+#include "kplayer.h"
+#include "kgame.h"
+
+KGameSequence::KGameSequence() : QObject()
+{
+ mGame = 0;
+ mCurrentPlayer = 0;
+}
+
+KGameSequence::~KGameSequence()
+{
+}
+
+void KGameSequence::setGame(KGame* game)
+{
+ mGame = game;
+}
+
+void KGameSequence::setCurrentPlayer(KPlayer* player)
+{
+ mCurrentPlayer = player;
+}
+
+KPlayer *KGameSequence::nextPlayer(KPlayer *last,bool exclusive)
+{
+ kdDebug(11001) << "=================== NEXT PLAYER =========================="<<endl;
+ if (!game())
+ {
+ kdError() << k_funcinfo << "NULL game object" << endl;
+ return 0;
+ }
+ unsigned int minId,nextId,lastId;
+ KPlayer *nextplayer, *minplayer;
+ if (last)
+ {
+ lastId = last->id();
+ }
+ else
+ {
+ lastId = 0;
+ }
+
+ kdDebug(11001) << "nextPlayer: lastId="<<lastId<<endl;
+
+ // remove when this has been checked
+ minId = 0x7fff; // we just need a very large number...properly MAX_UINT or so would be ok...
+ nextId = minId;
+ nextplayer = 0;
+ minplayer = 0;
+
+ KPlayer *player;
+ for (player = game()->playerList()->first(); player != 0; player=game()->playerList()->next() )
+ {
+ // Find the first player for a cycle
+ if (player->id() < minId)
+ {
+ minId=player->id();
+ minplayer=player;
+ }
+ if (player==last)
+ {
+ continue;
+ }
+ // Find the next player which is bigger than the current one
+ if (player->id() > lastId && player->id() < nextId)
+ {
+ nextId=player->id();
+ nextplayer=player;
+ }
+ }
+
+ // Cycle to the beginning
+ if (!nextplayer)
+ {
+ nextplayer=minplayer;
+ }
+
+ kdDebug(11001) << k_funcinfo << " ##### lastId=" << lastId << " exclusive="
+ << exclusive << " minId=" << minId << " nextid=" << nextId
+ << " count=" << game()->playerList()->count() << endl;
+ if (nextplayer)
+ {
+ nextplayer->setTurn(true,exclusive);
+ }
+ else
+ {
+ return 0;
+ }
+ return nextplayer;
+}
+
+// Per default we do not do anything
+int KGameSequence::checkGameOver(KPlayer*)
+{
+ return 0;
+}
+/*
+ * vim: et sw=2
+ */
diff --git a/libkdegames/kgame/kgamesequence.h b/libkdegames/kgame/kgamesequence.h
new file mode 100644
index 00000000..4d26ceee
--- /dev/null
+++ b/libkdegames/kgame/kgamesequence.h
@@ -0,0 +1,87 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Andreas Beckermann ([email protected])
+ Copyright (C) 2003 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMESEQUENCE_H_
+#define __KGAMESEQUENCE_H_
+
+#include <qobject.h>
+
+class KPlayer;
+class KGame;
+
+/**
+ * This class takes care of round or move management as well of the gameover
+ * condition. It is especially used for round based games. For these games @ref
+ * nextPlayer and @ref checkGameOver are the most important methods.
+ *
+ * You can subclass KGameSequence and use @ref KGame::setGameSequence to use
+ * your own rules. Note that @ref KGame will take ownership and therefore will
+ * delete the object on destruction.
+ * @short Round/move management class
+ * @author Andreas Beckermann <[email protected]>
+ **/
+class KGameSequence : public QObject
+{
+ Q_OBJECT
+public:
+ KGameSequence();
+ virtual ~KGameSequence();
+
+ /**
+ * Select the next player in a turn based game. In an asynchronous game this
+ * function has no meaning. Overwrite this function for your own game sequence.
+ * Per default it selects the next player in the playerList
+ */
+ virtual KPlayer* nextPlayer(KPlayer *last, bool exclusive = true);
+
+ virtual void setCurrentPlayer(KPlayer* p);
+
+ /**
+ * @return The @ref KGame object this sequence is for, or NULL if none.
+ **/
+ KGame* game() const { return mGame; }
+
+ KPlayer* currentPlayer() const { return mCurrentPlayer; }
+
+ /**
+ * Set the @ref KGame object for this sequence. This is called
+ * automatically by @ref KGame::setGameSequence and you should not call
+ * it.
+ **/
+ void setGame(KGame* game);
+
+ /**
+ * Check whether the game is over. The default implementation always
+ * returns 0.
+ *
+ * @param player the player who made the last move
+ * @return anything else but 0 is considered as game over
+ **/
+ virtual int checkGameOver(KPlayer *player);
+
+private:
+ KGame* mGame;
+ KPlayer* mCurrentPlayer;
+};
+
+#endif
+
diff --git a/libkdegames/kgame/kgameversion.h b/libkdegames/kgame/kgameversion.h
new file mode 100644
index 00000000..c3147525
--- /dev/null
+++ b/libkdegames/kgame/kgameversion.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2003 Andreas Beckermann ([email protected])
+ Copyright (C) 2003 Martin Heni ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+#ifndef __KGAMEVERSION_H__
+#define __KGAMEVERSION_H__
+
+/**
+ * In this file you find a couple of defines that indicate whether a specific
+ * feature or function is present in this version of the KGame library.
+ *
+ * You don't need this for KDE CVS, but for games that live outside of KDE CVS
+ * it may be very helpful and a lot easier than writing configure scripts for
+ * this task.
+ *
+ * All defines are prefixed with KGAME_ to avoid conflicts.
+ **/
+
+// KGame::savegame() didn't exist in KDE 3.0
+#define KGAME_HAVE_KGAME_SAVEGAME 1
+
+// KGameNetwork::port(), KMessageIO::peerPort() and friends were added in KDE 3.2
+#define KGAME_HAVE_KGAME_PORT 1
+
+// KGameNetwork::hostName(), KMessageIO::peerName() and friends were added in KDE 3.2
+#define KGAME_HAVE_KGAME_HOSTNAME 1
+
+// KGameSequence class was added in KDE 3.2
+#define KGAME_HAVE_KGAMESEQUENCE 1
+
+// KGame::addPlayer() needs to assign an ID to new players, otherwise network is
+// broken. this is done in KDE 3.2.
+#define KGAME_HAVE_FIXED_ADDPLAYER_ID 1
+
+#endif
+
diff --git a/libkdegames/kgame/kmessageclient.cpp b/libkdegames/kgame/kmessageclient.cpp
new file mode 100644
index 00000000..ed9cc966
--- /dev/null
+++ b/libkdegames/kgame/kmessageclient.cpp
@@ -0,0 +1,373 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <kdebug.h>
+#include <stdio.h>
+
+#include <qbuffer.h>
+#include <qtimer.h>
+
+#include "kmessageio.h"
+#include "kmessageserver.h"
+
+#include "kmessageclient.h"
+
+class KMessageClientPrivate
+{
+public:
+ KMessageClientPrivate ()
+ : adminID (0), connection (0)
+ {}
+
+ ~KMessageClientPrivate ()
+ {
+ delete connection;
+ }
+
+ Q_UINT32 adminID;
+ QValueList <Q_UINT32> clientList;
+ KMessageIO *connection;
+
+ bool isLocked;
+ QValueList <QByteArray> delayedMessages;
+};
+
+KMessageClient::KMessageClient (QObject *parent, const char *name)
+ : QObject (parent, name)
+{
+ d = new KMessageClientPrivate ();
+ d->isLocked = false;
+}
+
+KMessageClient::~KMessageClient ()
+{
+ d->delayedMessages.clear();
+ delete d;
+}
+
+// -- setServer stuff
+
+void KMessageClient::setServer (const QString &host, Q_UINT16 port)
+{
+ setServer (new KMessageSocket (host, port));
+}
+
+void KMessageClient::setServer (KMessageServer *server)
+{
+ KMessageDirect *serverIO = new KMessageDirect ();
+ setServer (new KMessageDirect (serverIO));
+ server->addClient (serverIO);
+}
+
+void KMessageClient::setServer (KMessageIO *connection)
+{
+ if (d->connection)
+ {
+ delete d->connection;
+ kdDebug (11001) << k_funcinfo << ": We are changing the server!" << endl;
+ }
+
+ d->connection = connection;
+ if (connection )
+ {
+ connect (connection, SIGNAL (received(const QByteArray &)),
+ this, SLOT (processIncomingMessage(const QByteArray &)));
+ connect (connection, SIGNAL (connectionBroken()),
+ this, SLOT (removeBrokenConnection ()));
+ }
+}
+
+// -- id stuff
+
+Q_UINT32 KMessageClient::id () const
+{
+ return (d->connection) ? d->connection->id () : 0;
+}
+
+bool KMessageClient::isAdmin () const
+{
+ return id() != 0 && id() == adminId();
+}
+
+Q_UINT32 KMessageClient::adminId () const
+{
+ return d->adminID;
+}
+
+const QValueList <Q_UINT32> &KMessageClient::clientList() const
+{
+ return d->clientList;
+}
+
+bool KMessageClient::isConnected () const
+{
+ return d->connection && d->connection->isConnected();
+}
+
+bool KMessageClient::isNetwork () const
+{
+ return isConnected() ? d->connection->isNetwork() : false;
+}
+
+Q_UINT16 KMessageClient::peerPort () const
+{
+ return d->connection ? d->connection->peerPort() : 0;
+}
+
+QString KMessageClient::peerName () const
+{
+ return d->connection ? d->connection->peerName() : QString::fromLatin1("localhost");
+}
+
+// --------------------- Sending messages
+
+void KMessageClient::sendServerMessage (const QByteArray &msg)
+{
+ if (!d->connection)
+ {
+ kdWarning (11001) << k_funcinfo << ": We have no connection yet!" << endl;
+ return;
+ }
+ d->connection->send (msg);
+}
+
+void KMessageClient::sendBroadcast (const QByteArray &msg)
+{
+ QByteArray sendBuffer;
+ QBuffer buffer (sendBuffer);
+ buffer.open (IO_WriteOnly);
+ QDataStream stream (&buffer);
+
+ stream << static_cast<Q_UINT32> ( KMessageServer::REQ_BROADCAST );
+ buffer.QIODevice::writeBlock (msg);
+ sendServerMessage (sendBuffer);
+}
+
+void KMessageClient::sendForward (const QByteArray &msg, const QValueList <Q_UINT32> &clients)
+{
+ QByteArray sendBuffer;
+ QBuffer buffer (sendBuffer);
+ buffer.open (IO_WriteOnly);
+ QDataStream stream (&buffer);
+
+ stream << static_cast<Q_UINT32>( KMessageServer::REQ_FORWARD ) << clients;
+ buffer.QIODevice::writeBlock (msg);
+ sendServerMessage (sendBuffer);
+}
+
+void KMessageClient::sendForward (const QByteArray &msg, Q_UINT32 client)
+{
+ sendForward (msg, QValueList <Q_UINT32> () << client);
+}
+
+
+// --------------------- Receiving and processing messages
+
+void KMessageClient::processIncomingMessage (const QByteArray &msg)
+{
+ if (d->isLocked)
+ {
+ d->delayedMessages.append(msg);
+ return;
+ }
+ if (d->delayedMessages.count() > 0)
+ {
+ d->delayedMessages.append (msg);
+ QByteArray first = d->delayedMessages.front();
+ d->delayedMessages.pop_front();
+ processMessage (first);
+ }
+ else
+ {
+ processMessage(msg);
+ }
+}
+
+void KMessageClient::processMessage (const QByteArray &msg)
+{
+ if (d->isLocked)
+ { // must NOT happen, since we check in processIncomingMessage as well as in processFirstMessage
+ d->delayedMessages.append(msg);
+ return;
+ }
+ QBuffer in_buffer (msg);
+ in_buffer.open (IO_ReadOnly);
+ QDataStream in_stream (&in_buffer);
+
+
+ bool unknown = false;
+
+ Q_UINT32 messageID;
+ in_stream >> messageID;
+ switch (messageID)
+ {
+ case KMessageServer::MSG_BROADCAST:
+ {
+ Q_UINT32 clientID;
+ in_stream >> clientID;
+ emit broadcastReceived (in_buffer.readAll(), clientID);
+ }
+ break;
+
+ case KMessageServer::MSG_FORWARD:
+ {
+ Q_UINT32 clientID;
+ QValueList <Q_UINT32> receivers;
+ in_stream >> clientID >> receivers;
+ emit forwardReceived (in_buffer.readAll(), clientID, receivers);
+ }
+ break;
+
+ case KMessageServer::ANS_CLIENT_ID:
+ {
+ bool old_admin = isAdmin();
+ Q_UINT32 clientID;
+ in_stream >> clientID;
+ d->connection->setId (clientID);
+ if (old_admin != isAdmin())
+ emit adminStatusChanged (isAdmin());
+ }
+ break;
+
+ case KMessageServer::ANS_ADMIN_ID:
+ {
+ bool old_admin = isAdmin();
+ in_stream >> d->adminID;
+ if (old_admin != isAdmin())
+ emit adminStatusChanged (isAdmin());
+ }
+ break;
+
+ case KMessageServer::ANS_CLIENT_LIST:
+ {
+ in_stream >> d->clientList;
+ }
+ break;
+
+ case KMessageServer::EVNT_CLIENT_CONNECTED:
+ {
+ Q_UINT32 id;
+ in_stream >> id;
+
+ if (d->clientList.contains (id))
+ kdWarning (11001) << k_funcinfo << ": Adding a client that already existed!" << endl;
+ else
+ d->clientList.append (id);
+
+ emit eventClientConnected (id);
+ }
+ break;
+
+ case KMessageServer::EVNT_CLIENT_DISCONNECTED:
+ {
+ Q_UINT32 id;
+ Q_INT8 broken;
+ in_stream >> id >> broken;
+
+ if (!d->clientList.contains (id))
+ kdWarning (11001) << k_funcinfo << ": Removing a client that doesn't exist!" << endl;
+ else
+ d->clientList.remove (id);
+
+ emit eventClientDisconnected (id, bool (broken));
+ }
+ break;
+
+ default:
+ unknown = true;
+ }
+
+ if (!unknown && !in_buffer.atEnd())
+ kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl;
+
+ emit serverMessageReceived (msg, unknown);
+
+ if (unknown)
+ kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl;
+}
+
+void KMessageClient::processFirstMessage()
+{
+ if (d->isLocked)
+ {
+ return;
+ }
+ if (d->delayedMessages.count() == 0)
+ {
+ kdDebug(11001) << k_funcinfo << ": no messages delayed" << endl;
+ return;
+ }
+ QByteArray first = d->delayedMessages.front();
+ d->delayedMessages.pop_front();
+ processMessage (first);
+}
+
+void KMessageClient::removeBrokenConnection ()
+{
+ kdDebug (11001) << k_funcinfo << ": timer single shot for removeBrokenConnection"<<this << endl;
+ // MH We cannot directly delete the socket. otherwise QSocket crashes
+ QTimer::singleShot( 0, this, SLOT(removeBrokenConnection2()) );
+ return;
+}
+
+
+void KMessageClient::removeBrokenConnection2 ()
+{
+ kdDebug (11001) << k_funcinfo << ": Broken:Deleting the connection object"<<this << endl;
+
+ emit aboutToDisconnect(id());
+ delete d->connection;
+ d->connection = 0;
+ d->adminID = 0;
+ emit connectionBroken();
+ kdDebug (11001) << k_funcinfo << ": Broken:Deleting the connection object DONE" << endl;
+}
+
+void KMessageClient::disconnect ()
+{
+ kdDebug (11001) << k_funcinfo << ": Disconnect:Deleting the connection object" << endl;
+
+ emit aboutToDisconnect(id());
+ delete d->connection;
+ d->connection = 0;
+ d->adminID = 0;
+ emit connectionBroken();
+ kdDebug (11001) << k_funcinfo << ": Disconnect:Deleting the connection object DONE" << endl;
+}
+
+void KMessageClient::lock ()
+{
+ d->isLocked = true;
+}
+
+void KMessageClient::unlock ()
+{
+ d->isLocked = false;
+ for (unsigned int i = 0; i < d->delayedMessages.count(); i++)
+ {
+ QTimer::singleShot(0, this, SLOT(processFirstMessage()));
+ }
+}
+
+unsigned int KMessageClient::delayedMessageCount() const
+{
+ return d->delayedMessages.count();
+}
+
+#include "kmessageclient.moc"
diff --git a/libkdegames/kgame/kmessageclient.h b/libkdegames/kgame/kmessageclient.h
new file mode 100644
index 00000000..90364c8d
--- /dev/null
+++ b/libkdegames/kgame/kmessageclient.h
@@ -0,0 +1,422 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KMESSAGECLIENT_H__
+#define __KMESSAGECLIENT_H__
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+class KMessageIO;
+class KMessageServer;
+class KMessageClientPrivate;
+
+/**
+ @short A client to connect to a KMessageServer
+
+ This class implements a client that can connect to a KMessageServer object.
+ It can be used to exchange messages between clients.
+
+ Usually you will connect the signals broadcastReceived and forwardReceived to
+ some specific slots. In these slot methods you can analyse the messages that are
+ sent to you from other clients.
+
+ To send messages to other clients, use the methods sendBroadcast() (to send to all
+ clients) or sendForward() (to send to a list of selected clients).
+
+ If you want to communicate with the KMessageServer object directly (on a more low
+ level base), use the method sendServerMessage to send a command to the server and
+ connect to the signal serverMessageReceived to see all the incoming messages.
+ In that case the messages must be of the format specified in KMessageServer.
+ @author Burkhard Lehner <[email protected]>
+*/
+class KMessageClient : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ Constructor.
+ Creates an unconnected KMessageClient object. Use setServer() later to connect to a
+ KMessageServer object.
+ */
+ KMessageClient (QObject *parent = 0, const char *name = 0);
+
+ /**
+ Destructor.
+ Disconnects from the server, if any connection was established.
+ */
+ ~KMessageClient ();
+
+ /**
+ @return The client ID of this client. Every client that is connected to a KMessageServer
+ has a unique ID number.
+
+ NOTE: As long as the object is not yet connected to the server, and as long as the server
+ hasn't sent the client ID, this method returns 0.
+ */
+ Q_UINT32 id () const;
+
+ /**
+ @return Whether or not this client is the server admin.
+ One of the clients connected to the server is the admin and can administrate the server
+ (set maximum number of clients, remove clients, ...).
+
+ If you use admin commands without being the admin, these commands are simply ignored by
+ the server.
+
+ NOTE: As long as you are not connected to a server, this method returns false.
+ */
+ bool isAdmin () const;
+
+ /**
+ @return The ID of the admin client on the message server.
+ */
+ Q_UINT32 adminId() const;
+
+ /**
+ @return The list of the IDs of all the message clients connected to the message server.
+ */
+ const QValueList <Q_UINT32> &clientList() const;
+
+ /**
+ Connects the client to (another) server.
+
+ Tries to connect via a TCP/IP socket to a KMessageServer object
+ on the given host, listening on the specified port.
+
+ If we were already connected, the old connection is closed.
+ @param host The name of the host to connect to. Must be either a hostname which can
+ be resolved to an IP or just an IP
+ @param port The port to connect to
+ */
+ void setServer (const QString &host, Q_UINT16 port);
+
+ /**
+ Connects the client to (another) server.
+
+ Connects to the given server, using KMessageDirect.
+ (The server object has to be in the same process.)
+
+ If we were already connected, the old connection is closed.
+ @param server The KMessageServer to connect to
+ */
+ void setServer (KMessageServer *server);
+
+ /**
+ * Corresponds to setServer(0); but also emits the connectionBroken signal
+ **/
+ void disconnect();
+
+ /**
+ Connects the client to (another) server.
+
+ To use this method, you have to create a KMessageIO object with new (indeed you must
+ create an instance of a subclass of KMessageIO, e.g. KMessageSocket or KMessageDirect).
+ This object must already be connected to the new server.
+
+ Calling this method disconnects any earlier connection, and uses the new KMessageIO
+ object instead. This object gets owned by the KMessageClient object, so don't delete
+ or manipulate it afterwards.
+
+ With this method it is possible to change the server on the fly. But be careful that
+ there are no important messages from the old server not yet delivered.
+
+ NOTE: It is very likely that we will have another client ID on the new server. The
+ value returned by clientID may be a little outdated until the new server tells us
+ our new ID.
+
+ NOTE: The two other setServer methods are for convenience. If you use them, you don't
+ have to create a KMessageIO object yourself.
+ */
+ virtual void setServer (KMessageIO *connection);
+
+ /**
+ @return True, if a connection to a KMessageServer has been started, and if the
+ connection is ready for transferring data. (It will return false e.g. as long as
+ a socket connection hasn't been established, and it will also return false after
+ a socket connection is broken.)
+ */
+ bool isConnected () const;
+
+ /**
+ @return TRUE if isConnected() is true AND this is not a local (like
+ KMessageDirect) connection.
+ */
+ bool isNetwork () const;
+
+ /**
+ @return 0 if isConnected() is FALSE, otherwise the port number this client is
+ connected to. See also KMessageIO::peerPort and QSocket::peerPort.
+ @since 3.2
+ */
+ Q_UINT16 peerPort () const;
+
+ /**
+ @since 3.2
+ @return "localhost" if isConnected() is FALSE, otherwise the hostname this client is
+ connected to. See also KMessageIO::peerName() and QSocket::peerName().
+ */
+ QString peerName() const;
+
+ /**
+ Sends a message to the KMessageServer. If we are not yet connected to one, nothing
+ happens.
+
+ Use this method to send a low level command to the server. It has to be in the
+ format specified in KMessageServer.
+
+ If you want to send messages to other clients, you should use sendBroadcast()
+ and sendForward().
+ @param msg The message to be sent to the server. Must be in the format specified in KMessageServer.
+ */
+ void sendServerMessage (const QByteArray &msg);
+
+ /**
+ Sends a message to all the clients connected to the server, including ourself.
+ The message consists of an arbitrary block of data with arbitrary length.
+
+ All the clients will receive an exact copy of this block of data, which will be
+ processed in their processBroadcast() method.
+ @param msg The message to be sent to the clients
+ */
+ //AB: processBroadcast doesn't exist!! is processIncomingMessage meant?
+ void sendBroadcast (const QByteArray &msg);
+
+ /**
+ Sends a message to all the clients in a list.
+ The message consists of an arbitrary block of data with arbitrary length.
+
+ All clients will receive an exact copy of this block of data, which will be
+ processed in their processForward() method.
+
+ If the list contains client IDs that are not defined, they are ignored. If
+ it contains an ID several times, that client will receive the message several
+ times.
+
+ To send a message to the admin of the KMessageServer, you can use 0 as clientID,
+ instead of using the real client ID.
+ @param msg The message to be sent to the clients
+ @param clients A list of clients the message should be sent to
+ */
+ //AB: processForward doesn't exist!! is processIncomingMessage meant?
+ void sendForward (const QByteArray &msg, const QValueList <Q_UINT32> &clients);
+
+ /**
+ Sends a message to a single client. This is a convenieance method. It calls
+ sendForward (const QByteArray &msg, const QValueList &ltQ_UINT32> &clients)
+ with a list containing only one client ID.
+
+ To send a message to the admin of the KMessageServer, you can use 0 as clientID,
+ instead of using the real client ID.
+ @param msg The message to be sent to the client
+ @param client The id of the client the message shall be sent to
+ */
+ void sendForward (const QByteArray &msg, Q_UINT32 client);
+
+ /**
+ Once this function is called no message will be received anymore.
+ processIncomingMessage() gets delayed until unlock() is called.
+
+ Note that all messages are still received, but their delivery (like
+ broadcastReceived()) get delayed only.
+ */
+ void lock();
+
+ /**
+ Deliver every message that was delayed by lock() and actually deliver
+ all messages that get received from now on.
+ */
+ void unlock();
+
+ /**
+ @return The number of messages that got delayed since lock() was called
+ */
+ unsigned int delayedMessageCount() const;
+
+signals:
+ /**
+ This signal is emitted when the client receives a broadcast message from the
+ KMessageServer, sent by another client. Connect to this signal to analyse the
+ received message and do the right reaction.
+
+ senderID contains the ID of the client that sent the broadcast message. You can
+ use this e.g. to send a reply message to only that client. Or you can use it
+ to ignore broadcast messages that were sent by yourself:
+
+ \code
+ void myObject::myBroadcastSlot (const QByteArray &msg, Q_UINT32 senderID)
+ {
+ if (senderID == ((KMessageClient *)sender())->id())
+ return;
+ ...
+ }
+ \endcode
+ @param msg The message that has been sent to us
+ @param senderID The ID of the client which sent the message
+ */
+ void broadcastReceived (const QByteArray &msg, Q_UINT32 senderID);
+
+ /**
+ This signal is emitted when the client receives a forward message from the
+ KMessageServer, sent by another client. Connect to this signal to analyse the
+ received message and do the right reaction.
+
+ senderID contains the ID of the client that sent the broadcast message. You can
+ use this e.g. to send a reply message to only that client.
+
+ receivers contains the list of the clients that got the message. (If this list
+ only contains one number, this will be your client ID, and it was exclusivly
+ sent to you.)
+
+ If you don't want to distinguish between broadcast and forward messages and
+ treat them the same, you can connect forwardReceived signal to the
+ broadcastReceived signal. (Yes, that's possible! You can connect a Qt signal to
+ a Qt signal, and the second one can have less parameters.)
+
+ \code
+ KMessageClient *client = new KMessageClient ();
+ connect (client, SIGNAL (forwardReceived (const QByteArray &, Q_UINT32, const QValueList <Q_UINT32>&)),
+ client, SIGNAL (broadcastReceived (const QByteArray &, Q_UINT32)));
+ \endcode
+
+ Then connect the broadcast signal to your slot that analyzes the message.
+ @param msg The message that has been sent to us
+ @param senderID The ID of the client which sent the message
+ @param receivers All clients which receive this message
+ */
+ void forwardReceived (const QByteArray &msg, Q_UINT32 senderID, const QValueList <Q_UINT32> &receivers);
+
+ /**
+ This signal is emitted when the connection to the KMessageServer is broken.
+ Reasons for this can be: a network error, a server breakdown, or you were just kicked
+ from the server.
+
+ When this signal is sent, the connection is already lost and the client is unconnected.
+ You can connect to another server by calling setServer() afterwards. But keep in mind that
+ some important messages might have vanished.
+ */
+ void connectionBroken ();
+
+ /**
+ This signal is emitted right before the client disconnects. It can be used
+ to this store the id of the client which is about to be lost.
+ */
+ void aboutToDisconnect(Q_UINT32 id);
+
+ /**
+ This signal is emitted when this client becomes the admin client or when it loses
+ the admin client status. Connect to this signal if you have to do any initialization
+ or cleanup.
+ @param isAdmin Whether we are now admin or not
+ */
+ void adminStatusChanged (bool isAdmin);
+
+ /**
+ This signal is emitted when another client has connected
+ to the server. Connect to this method if that clients needs initialization.
+ This should usually only be done in one client, e.g. the admin client.
+ @param clientID The ID of the client that has newly connectd.
+ */
+ void eventClientConnected (Q_UINT32 clientID);
+
+ /**
+ This signal is emitted when the server has lost the
+ connection to one of the clients (This could be because of a bad internet connection
+ or because the client disconnected on purpose).
+ @param clientID The ID of the client that has disconnected
+ @param broken true if it was disconnected because of a network error
+ */
+ void eventClientDisconnected (Q_UINT32 clientID, bool broken);
+
+ /**
+ This signal is emitted on every message that came from the server. You can connect to this
+ signal to see the messages directly. They are in the format specified in KMessageServer.
+
+ @param msg The message that has been sent to us
+ @param unknown True when KMessageClient didn't recognize the message, i.e. it contained an unknown
+ message ID. If you want to add additional message types to the client, connect to this signal,
+ and if unknown is true, analyse the message by yourself. If you recognized the message,
+ set unknown to false (Otherwise a debug message will be printed).
+ */
+ //AB: maybe add a setNoEmit() so that the other signals can be deactivated?
+ //Could be a performance benefit (note: KMessageClient is a time critical
+ //class!!!)
+ void serverMessageReceived (const QByteArray &msg, bool &unknown);
+
+protected:
+ /**
+ This slot is called from processIncomingMessage or
+ processFirstMessage, depending on whether the client is locked or a delayed
+ message is still here (see lock)
+
+ It processes the message and analyses it. If it is a broadcast or a forward message from
+ another client, it emits the signal processBroadcast or processForward accordingly.
+
+ If you want to treat additional server messages, you can overwrite this method. Don't
+ forget to call processIncomingMessage of your superclass!
+
+ At the moment, the following server messages are interpreted:
+
+ MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST
+ @param msg The incoming message
+ */
+
+ virtual void processMessage (const QByteArray& msg);
+
+protected slots:
+ /**
+ This slot is called from the signal KMessageIO::received whenever a message from the
+ KMessageServer arrives.
+
+ It processes the message and analyses it. If it is a broadcast or a forward message from
+ another client, it emits the signal processBroadcast or processForward accordingly.
+
+ If you want to treat additional server messages, you can overwrite this method. Don't
+ forget to call processIncomingMessage() of your superclass!
+
+ At the moment, the following server messages are interpreted:
+
+ MSG_BROADCAST, MSG_FORWARD, ANS_CLIENT_ID, ANS_ADMIN_ID, ANS_CLIENT_LIST
+ @param msg The incoming message
+ */
+ virtual void processIncomingMessage (const QByteArray &msg);
+
+ /**
+ Called from unlock() (using QTimer::singleShot) until all delayed
+ messages are delivered.
+ */
+ void processFirstMessage();
+
+ /**
+ This slot is called from the signal KMessageIO::connectionBroken.
+
+ It deletes the internal KMessageIO object, and resets the client to default
+ values. To connect again to another server, use setServer.
+ */
+ virtual void removeBrokenConnection ();
+ void removeBrokenConnection2 ();
+
+private:
+ KMessageClientPrivate *d;
+};
+
+#endif
diff --git a/libkdegames/kgame/kmessageio.cpp b/libkdegames/kgame/kmessageio.cpp
new file mode 100644
index 00000000..f3353277
--- /dev/null
+++ b/libkdegames/kgame/kmessageio.cpp
@@ -0,0 +1,482 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*
+ KMessageIO class and subclasses KMessageSocket and KMessageDirect
+*/
+
+#include "kmessageio.h"
+#include <qsocket.h>
+#include <kdebug.h>
+#include <kprocess.h>
+#include <qfile.h>
+
+// ----------------------- KMessageIO -------------------------
+
+KMessageIO::KMessageIO (QObject *parent, const char *name)
+ : QObject (parent, name), m_id (0)
+{}
+
+KMessageIO::~KMessageIO ()
+{}
+
+void KMessageIO::setId (Q_UINT32 id)
+{
+ m_id = id;
+}
+
+Q_UINT32 KMessageIO::id ()
+{
+ return m_id;
+}
+
+// ----------------------KMessageSocket -----------------------
+
+KMessageSocket::KMessageSocket (QString host, Q_UINT16 port, QObject *parent,
+const char *name)
+ : KMessageIO (parent, name)
+{
+ mSocket = new QSocket ();
+ mSocket->connectToHost (host, port);
+ initSocket ();
+}
+
+KMessageSocket::KMessageSocket (QHostAddress host, Q_UINT16 port, QObject
+*parent, const char *name)
+ : KMessageIO (parent, name)
+{
+ mSocket = new QSocket ();
+ mSocket->connectToHost (host.toString(), port);
+ initSocket ();
+}
+
+KMessageSocket::KMessageSocket (QSocket *socket, QObject *parent, const char
+*name)
+ : KMessageIO (parent, name)
+{
+ mSocket = socket;
+ initSocket ();
+}
+
+KMessageSocket::KMessageSocket (int socketFD, QObject *parent, const char
+*name)
+ : KMessageIO (parent, name)
+{
+ mSocket = new QSocket ();
+ mSocket->setSocket (socketFD);
+ initSocket ();
+}
+
+KMessageSocket::~KMessageSocket ()
+{
+ delete mSocket;
+}
+
+bool KMessageSocket::isConnected () const
+{
+ return mSocket->state() == QSocket::Connection;
+}
+
+void KMessageSocket::send (const QByteArray &msg)
+{
+ QDataStream str (mSocket);
+ str << Q_UINT8 ('M'); // magic number for begin of message
+ str.writeBytes (msg.data(), msg.size()); // writes the length (as Q_UINT32) and the data
+}
+
+void KMessageSocket::processNewData ()
+{
+ if (isRecursive)
+ return;
+ isRecursive = true;
+
+ QDataStream str (mSocket);
+ while (mSocket->bytesAvailable() > 0)
+ {
+ if (mAwaitingHeader)
+ {
+ // Header = magic number + packet length = 5 bytes
+ if (mSocket->bytesAvailable() < 5)
+ {
+ isRecursive = false;
+ return;
+ }
+
+ // Read the magic number first. If something unexpected is found,
+ // start over again, ignoring the data that was read up to then.
+
+ Q_UINT8 v;
+ str >> v;
+ if (v != 'M')
+ {
+ kdWarning(11001) << k_funcinfo << ": Received unexpected data, magic number wrong!" << endl;
+ continue;
+ }
+
+ str >> mNextBlockLength;
+ mAwaitingHeader = false;
+ }
+ else
+ {
+ // Data not completely read => wait for more
+ if (mSocket->bytesAvailable() < (Q_ULONG) mNextBlockLength)
+ {
+ isRecursive = false;
+ return;
+ }
+
+ QByteArray msg (mNextBlockLength);
+ str.readRawBytes (msg.data(), mNextBlockLength);
+
+ // send the received message
+ emit received (msg);
+
+ // Waiting for the header of the next message
+ mAwaitingHeader = true;
+ }
+ }
+
+ isRecursive = false;
+}
+
+void KMessageSocket::initSocket ()
+{
+ connect (mSocket, SIGNAL (error(int)), SIGNAL (connectionBroken()));
+ connect (mSocket, SIGNAL (connectionClosed()), SIGNAL (connectionBroken()));
+ connect (mSocket, SIGNAL (readyRead()), SLOT (processNewData()));
+ mAwaitingHeader = true;
+ mNextBlockLength = 0;
+ isRecursive = false;
+}
+
+Q_UINT16 KMessageSocket::peerPort () const
+{
+ return mSocket->peerPort();
+}
+
+QString KMessageSocket::peerName () const
+{
+ return mSocket->peerName();
+}
+
+// ----------------------KMessageDirect -----------------------
+
+KMessageDirect::KMessageDirect (KMessageDirect *partner, QObject *parent,
+const char *name)
+ : KMessageIO (parent, name), mPartner (0)
+{
+ // 0 as first parameter leaves the object unconnected
+ if (!partner)
+ return;
+
+ // Check if the other object is already connected
+ if (partner && partner->mPartner)
+ {
+ kdWarning(11001) << k_funcinfo << ": Object is already connected!" << endl;
+ return;
+ }
+
+ // Connect from us to that object
+ mPartner = partner;
+
+ // Connect the other object to us
+ partner->mPartner = this;
+}
+
+KMessageDirect::~KMessageDirect ()
+{
+ if (mPartner)
+ {
+ mPartner->mPartner = 0;
+ emit mPartner->connectionBroken();
+ }
+}
+
+bool KMessageDirect::isConnected () const
+{
+ return mPartner != 0;
+}
+
+void KMessageDirect::send (const QByteArray &msg)
+{
+ if (mPartner)
+ emit mPartner->received (msg);
+ else
+ kdError(11001) << k_funcinfo << ": Not yet connected!" << endl;
+}
+
+
+// ----------------------- KMessageProcess ---------------------------
+
+KMessageProcess::~KMessageProcess()
+{
+ kdDebug(11001) << "@@@KMessageProcess::Delete process" << endl;
+ if (mProcess)
+ {
+ mProcess->kill();
+ delete mProcess;
+ mProcess=0;
+ // Remove not send buffers
+ mQueue.setAutoDelete(true);
+ mQueue.clear();
+ // Maybe todo: delete mSendBuffer
+ }
+}
+KMessageProcess::KMessageProcess(QObject *parent, QString file) : KMessageIO(parent,0)
+{
+ // Start process
+ kdDebug(11001) << "@@@KMessageProcess::Start process" << endl;
+ mProcessName=file;
+ mProcess=new KProcess;
+ int id=0;
+ *mProcess << mProcessName << QString("%1").arg(id);
+ kdDebug(11001) << "@@@KMessageProcess::Init:Id= " << id << endl;
+ kdDebug(11001) << "@@@KMessgeProcess::Init:Processname: " << mProcessName << endl;
+ connect(mProcess, SIGNAL(receivedStdout(KProcess *, char *, int )),
+ this, SLOT(slotReceivedStdout(KProcess *, char * , int )));
+ connect(mProcess, SIGNAL(receivedStderr(KProcess *, char *, int )),
+ this, SLOT(slotReceivedStderr(KProcess *, char * , int )));
+ connect(mProcess, SIGNAL(processExited(KProcess *)),
+ this, SLOT(slotProcessExited(KProcess *)));
+ connect(mProcess, SIGNAL(wroteStdin(KProcess *)),
+ this, SLOT(slotWroteStdin(KProcess *)));
+ mProcess->start(KProcess::NotifyOnExit,KProcess::All);
+ mSendBuffer=0;
+ mReceiveCount=0;
+ mReceiveBuffer.resize(1024);
+}
+bool KMessageProcess::isConnected() const
+{
+ kdDebug(11001) << "@@@KMessageProcess::Is conencted" << endl;
+ if (!mProcess) return false;
+ return mProcess->isRunning();
+}
+void KMessageProcess::send(const QByteArray &msg)
+{
+ kdDebug(11001) << "@@@KMessageProcess:: SEND("<<msg.size()<<") to process" << endl;
+ unsigned int size=msg.size()+2*sizeof(long);
+
+ char *tmpbuffer=new char[size];
+ long *p1=(long *)tmpbuffer;
+ long *p2=p1+1;
+ kdDebug(11001) << "p1="<<p1 << "p2="<< p2 << endl;
+ memcpy(tmpbuffer+2*sizeof(long),msg.data(),msg.size());
+ *p1=0x4242aeae;
+ *p2=size;
+
+ QByteArray *buffer=new QByteArray();
+ buffer->assign(tmpbuffer,size);
+ // buffer->duplicate(msg);
+ mQueue.enqueue(buffer);
+ writeToProcess();
+}
+void KMessageProcess::writeToProcess()
+{
+ // Previous send ok and item in queue
+ if (mSendBuffer || mQueue.isEmpty()) return ;
+ mSendBuffer=mQueue.dequeue();
+ if (!mSendBuffer) return ;
+
+ // write it out to the process
+ // kdDebug(11001) << " @@@@@@ writeToProcess::SEND to process " << mSendBuffer->size() << " BYTE " << endl;
+ // char *p=mSendBuffer->data();
+ // for (int i=0;i<16;i++) printf("%02x ",(unsigned char)(*(p+i)));printf("\n");
+ mProcess->writeStdin(mSendBuffer->data(),mSendBuffer->size());
+
+}
+void KMessageProcess::slotWroteStdin(KProcess * )
+{
+ kdDebug(11001) << k_funcinfo << endl;
+ if (mSendBuffer)
+ {
+ delete mSendBuffer;
+ mSendBuffer=0;
+ }
+ writeToProcess();
+}
+
+void KMessageProcess::slotReceivedStderr(KProcess * proc, char *buffer, int buflen)
+{
+ int pid=0;
+ int len;
+ char *p;
+ char *pos;
+// kdDebug(11001)<<"############# Got stderr " << buflen << " bytes" << endl;
+
+ if (!buffer || buflen==0) return ;
+ if (proc) pid=proc->pid();
+
+
+ pos=buffer;
+ do
+ {
+ p=(char *)memchr(pos,'\n',buflen);
+ if (!p) len=buflen;
+ else len=p-pos;
+
+ QByteArray a;
+ a.setRawData(pos,len);
+ QString s(a);
+ kdDebug(11001) << "PID" <<pid<< ":" << s << endl;
+ a.resetRawData(pos,len);
+ if (p) pos=p+1;
+ buflen-=len+1;
+ }while(buflen>0);
+}
+
+
+void KMessageProcess::slotReceivedStdout(KProcess * , char *buffer, int buflen)
+{
+ kdDebug(11001) << "$$$$$$ " << k_funcinfo << ": Received " << buflen << " bytes over inter process communication" << endl;
+
+ // TODO Make a plausibility check on buflen to avoid memory overflow
+ while (mReceiveCount+buflen>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024);
+ memcpy(mReceiveBuffer.data()+mReceiveCount,buffer,buflen);
+ mReceiveCount+=buflen;
+
+ // Possbile message
+ while (mReceiveCount>2*sizeof(long))
+ {
+ long *p1=(long *)mReceiveBuffer.data();
+ long *p2=p1+1;
+ unsigned int len;
+ if (*p1!=0x4242aeae)
+ {
+ kdDebug(11001) << k_funcinfo << ": Cookie error...transmission failure...serious problem..." << endl;
+// for (int i=0;i<mReceiveCount;i++) fprintf(stderr,"%02x ",mReceiveBuffer[i]);fprintf(stderr,"\n");
+ }
+ len=(int)(*p2);
+ if (len<2*sizeof(long))
+ {
+ kdDebug(11001) << k_funcinfo << ": Message size error" << endl;
+ break;
+ }
+ if (len<=mReceiveCount)
+ {
+ kdDebug(11001) << k_funcinfo << ": Got message with len " << len << endl;
+
+ QByteArray msg;
+ // msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ emit received(msg);
+ // msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ // Shift buffer
+ if (len<mReceiveCount)
+ {
+ memmove(mReceiveBuffer.data(),mReceiveBuffer.data()+len,mReceiveCount-len);
+ }
+ mReceiveCount-=len;
+ }
+ else break;
+ }
+}
+
+void KMessageProcess::slotProcessExited(KProcess * /*p*/)
+{
+ kdDebug(11001) << "Process exited (slot)" << endl;
+ emit connectionBroken();
+ delete mProcess;
+ mProcess=0;
+}
+
+
+// ----------------------- KMessageFilePipe ---------------------------
+KMessageFilePipe::KMessageFilePipe(QObject *parent,QFile *readfile,QFile *writefile) : KMessageIO(parent,0)
+{
+ mReadFile=readfile;
+ mWriteFile=writefile;
+ mReceiveCount=0;
+ mReceiveBuffer.resize(1024);
+}
+
+KMessageFilePipe::~KMessageFilePipe()
+{
+}
+
+bool KMessageFilePipe::isConnected () const
+{
+ return (mReadFile!=0)&&(mWriteFile!=0);
+}
+
+void KMessageFilePipe::send(const QByteArray &msg)
+{
+ unsigned int size=msg.size()+2*sizeof(long);
+
+ char *tmpbuffer=new char[size];
+ long *p1=(long *)tmpbuffer;
+ long *p2=p1+1;
+ memcpy(tmpbuffer+2*sizeof(long),msg.data(),msg.size());
+ *p1=0x4242aeae;
+ *p2=size;
+
+ QByteArray buffer;
+ buffer.assign(tmpbuffer,size);
+ mWriteFile->writeBlock(buffer);
+ mWriteFile->flush();
+ /*
+ fprintf(stderr,"+++ KMessageFilePipe:: SEND(%d to parent) realsize=%d\n",msg.size(),buffer.size());
+ for (int i=0;i<buffer.size();i++) fprintf(stderr,"%02x ",buffer[i]);fprintf(stderr,"\n");
+ fflush(stderr);
+ */
+}
+
+void KMessageFilePipe::exec()
+{
+
+ // According to BL: Blocking read is ok
+ // while(mReadFile->atEnd()) { usleep(100); }
+
+ int ch=mReadFile->getch();
+
+ while (mReceiveCount>=mReceiveBuffer.size()) mReceiveBuffer.resize(mReceiveBuffer.size()+1024);
+ mReceiveBuffer[mReceiveCount]=(char)ch;
+ mReceiveCount++;
+
+ // Change for message
+ if (mReceiveCount>=2*sizeof(long))
+ {
+ long *p1=(long *)mReceiveBuffer.data();
+ long *p2=p1+1;
+ unsigned int len;
+ if (*p1!=0x4242aeae)
+ {
+ fprintf(stderr,"KMessageFilePipe::exec:: Cookie error...transmission failure...serious problem...\n");
+// for (int i=0;i<16;i++) fprintf(stderr,"%02x ",mReceiveBuffer[i]);fprintf(stderr,"\n");
+ }
+ len=(int)(*p2);
+ if (len==mReceiveCount)
+ {
+ //fprintf(stderr,"KMessageFilePipe::exec:: Got Message with len %d\n",len);
+
+ QByteArray msg;
+ //msg.setRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ msg.duplicate(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ emit received(msg);
+ //msg.resetRawData(mReceiveBuffer.data()+2*sizeof(long),len-2*sizeof(long));
+ mReceiveCount=0;
+ }
+ }
+
+
+ return ;
+
+
+}
+
+#include "kmessageio.moc"
diff --git a/libkdegames/kgame/kmessageio.h b/libkdegames/kgame/kmessageio.h
new file mode 100644
index 00000000..37cf35cd
--- /dev/null
+++ b/libkdegames/kgame/kmessageio.h
@@ -0,0 +1,416 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*
+ KMessageIO class and subclasses KMessageSocket and KMessageDirect
+*/
+
+#ifndef _KMESSAGEIO_H_
+#define _KMESSAGEIO_H_
+
+#include <qcstring.h>
+#include <qhostaddress.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qptrqueue.h>
+#include <qfile.h>
+#include <kdebug.h>
+
+class QSocket;
+class KProcess;
+//class QFile;
+
+
+/**
+ This abstract base class represents one end of a message connections
+ between two clients. Each client has one object of a subclass of
+ KMessageIO. Calling /e send() on one object will emit the signal
+ /e received() on the other object, and vice versa.
+
+ For each type of connection (TCP/IP socket, COM port, direct connection
+ within the same class) a subclass of KMessageIO must be defined that
+ implements the pure virtual methods /e send() and /e isConnected(),
+ and sends the signals. (See /e KMessageSocket for an example implementation.)
+
+ Two subclasses are already included: /e KMessageSocket (connection using
+ TCP/IP sockets) and /e KMessageDirect (connection using method calls, both
+ sides must be within the same process).
+*/
+
+class KMessageIO : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * The usual QObject constructor, does nothing else.
+ **/
+ KMessageIO (QObject *parent = 0, const char *name = 0);
+
+ /**
+ * The usual destructor, does nothing special.
+ **/
+ ~KMessageIO ();
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 0;}
+
+ /**
+ * @return Whether this KMessageIO is a network IO or not.
+ **/
+ //virtual bool isNetwork () const = 0;
+ virtual bool isNetwork () const
+ {
+ kdError(11001) << "Calling PURE virtual isNetwork...BAD" << endl;
+ return false;
+ }
+
+ /**
+ This method returns the status of the object, whether it is already
+ (or still) connected to another KMessageIO object or not.
+
+ This is a pure virtual method, so it has to be implemented in a subclass
+ of KMessageIO.
+ */
+ //virtual bool isConnected () const = 0;
+ virtual bool isConnected () const
+ {
+ kdError(11001) << "Calling PURE virtual isConencted...BAD" << endl;
+ return false;
+ }
+
+ /**
+ Sets the ID number of this object. This number can for example be used to
+ distinguish several objects in a server.
+
+ NOTE: Sometimes it is useful to let two connected KMessageIO objects
+ have the same ID number. You have to do so yourself, KMessageIO doesn't
+ change this value on its own!
+ */
+ void setId (Q_UINT32 id);
+
+ /**
+ Queries the ID of this object.
+ */
+ Q_UINT32 id ();
+
+ /**
+ @since 3.2
+ @return 0 in the default implementation. Reimplemented in @ref KMessageSocket.
+ */
+ virtual Q_UINT16 peerPort () const { return 0; }
+
+ /**
+ @since 3.2
+ @return "localhost" in the default implementation. Reimplemented in @ref KMessageSocket
+ */
+ virtual QString peerName () const { return QString::fromLatin1("localhost"); }
+
+
+signals:
+ /**
+ This signal is emitted when /e send() on the connected KMessageIO
+ object is called. The parameter contains the same data array in /e msg
+ as was used in /e send().
+ */
+ void received (const QByteArray &msg);
+
+ /**
+ This signal is emitted when the connection is closed. This can be caused
+ by a hardware error (e.g. broken internet connection) or because the other
+ object was killed.
+
+ Note: Sometimes a broken connection can be undetected for a long time,
+ or may never be detected at all. So don't rely on this signal!
+ */
+ void connectionBroken ();
+
+public slots:
+
+ /**
+ This slot sends the data block in /e msg to the connected object, that will
+ emit /e received().
+
+ For a concrete class, you have to subclass /e KMessageIO and overwrite this
+ method. In the subclass, implement this method as an ordinary method, not
+ as a slot! (Otherwise another slot would be defined. It would work, but uses
+ more memory and time.) See /e KMessageSocket for an example implementation.
+ */
+ virtual void send (const QByteArray &msg) = 0;
+
+protected:
+ Q_UINT32 m_id;
+};
+
+
+/**
+ This class implements the message communication using a TCP/IP socket. The
+ object can connect to a server socket, or can use an already connected socket.
+*/
+
+class KMessageSocket : public KMessageIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ Connects to a server socket on /e host with /e port. host can be an
+ numerical (e.g. "192.168.0.212") or symbolic (e.g. "wave.peter.org")
+ IP address. You can immediately use the /e sendSystem() and
+ /e sendBroadcast() methods. The messages are stored and sent to the
+ receiver after the connection is established.
+
+ If the connection could not be established (e.g. unknown host or no server
+ socket at this port), the signal /e connectionBroken is emitted.
+ */
+ KMessageSocket (QString host, Q_UINT16 port, QObject *parent = 0,
+ const char *name = 0);
+
+ /**
+ Connects to a server socket on /e host with /e port. You can immediately
+ use the /e sendSystem() and /e sendBroadcast() methods. The messages are
+ stored and sent to the receiver after the connection is established.
+
+ If the connection could not be established (e.g. unknown host or no server
+ socket at this port), the signal /e connectionBroken is emitted.
+ */
+ KMessageSocket (QHostAddress host, Q_UINT16 port, QObject *parent = 0,
+ const char *name = 0);
+
+ /**
+ Uses /e socket to do the communication.
+
+ The socket should already be connected, or at least be in /e connecting
+ state.
+
+ Note: The /e socket object is then owned by the /e KMessageSocket object.
+ So don't use it otherwise any more and don't delete it. It is deleted
+ together with this KMessageSocket object. (Use 0 as parent for the QSocket
+ object t ensure it is not deleted.)
+ */
+ KMessageSocket (QSocket *socket, QObject *parent = 0, const char *name = 0);
+
+ /**
+ Uses the socket specified by the socket descriptor socketFD to do the
+ communication. The socket must already be connected.
+
+ This constructor can be used with a QServerSocket within the (pure
+ virtual) method /e newConnection.
+
+ Note: The socket is then owned by the /e KMessageSocket object. So don't
+ manipulate the socket afterwards, especially don't close it. The socket is
+ automatically closed when KMessageSocket is deleted.
+ */
+ KMessageSocket (int socketFD, QObject *parent = 0, const char *name = 0);
+
+ /**
+ Destructor, closes the socket.
+ */
+ ~KMessageSocket ();
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 1;}
+
+ /**
+ @since 3.2
+ @return The port that this object is connected to. See QSocket::peerPort
+ */
+ virtual Q_UINT16 peerPort () const;
+
+ /**
+ @since 3.2
+ @return The hostname this object is connected to. See QSocket::peerName.
+ */
+ virtual QString peerName () const;
+
+ /**
+ @return TRUE as this is a network IO.
+ */
+ bool isNetwork() const { return true; }
+
+ /**
+ Returns true if the socket is in state /e connected.
+ */
+ bool isConnected () const;
+
+ /**
+ Overwritten slot method from KMessageIO.
+
+ Note: It is not declared as a slot method, since the slot is already
+ defined in KMessageIO as a virtual method.
+ */
+ void send (const QByteArray &msg);
+
+protected slots:
+ virtual void processNewData ();
+
+protected:
+ void initSocket ();
+ QSocket *mSocket;
+ bool mAwaitingHeader;
+ Q_UINT32 mNextBlockLength;
+
+ bool isRecursive; // workaround for "bug" in QSocket, Qt 2.2.3 or older
+};
+
+
+/**
+ This class implements the message communication using function calls
+ directly. It can only be used when both sides of the message pipe are
+ within the same process. The communication is very fast.
+
+ To establish a communication, you have to create two instances of
+ KMessageDirect, the first one with no parameters in the constructor,
+ the second one with the first as parameter:
+
+ /code
+ KMessageDirect *peer1, *peer2;
+ peer1 = new KMessageDirect (); // unconnected
+ peer2 = new KMessageDirect (peer1); // connect with peer1
+ /endcode
+
+ The connection is only closed when one of the instances is deleted.
+*/
+
+class KMessageDirect : public KMessageIO
+{
+ Q_OBJECT
+
+public:
+ /**
+ Creates an object and connects it to the object given in the first
+ parameter. Use 0 as first parameter to create an unconnected object,
+ that is later connected.
+
+ If that object is already connected, the object remains unconnected.
+ */
+ KMessageDirect (KMessageDirect *partner = 0, QObject *parent = 0, const char
+*name = 0);
+
+ /**
+ Destructor, closes the connection.
+ */
+ ~KMessageDirect ();
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 2;}
+
+
+ /**
+ @return FALSE as this is no network IO.
+ */
+ bool isNetwork() const { return false; }
+
+ /**
+ Returns true, if the object is connected to another instance.
+
+ If you use the first constructor, the object is unconnected unless another
+ object is created with this one as parameter.
+
+ The connection can only be closed by deleting one of the objects.
+ */
+ bool isConnected () const;
+
+ /**
+ Overwritten slot method from KMessageIO.
+
+ Note: It is not declared as a slot method, since the slot is already
+ defined in KMessageIO as a virtual method.
+ */
+ void send (const QByteArray &msg);
+
+protected:
+ KMessageDirect *mPartner;
+};
+
+class KMessageProcess : public KMessageIO
+{
+ Q_OBJECT
+
+ public:
+ KMessageProcess(QObject *parent, QString file);
+ ~KMessageProcess();
+ bool isConnected() const;
+ void send (const QByteArray &msg);
+ void writeToProcess();
+
+ /**
+ @return FALSE as this is no network IO.
+ */
+ bool isNetwork() const { return false; }
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 3;}
+
+
+
+ public slots:
+ void slotReceivedStdout(KProcess *proc, char *buffer, int buflen);
+ void slotReceivedStderr(KProcess *proc, char *buffer, int buflen);
+ void slotProcessExited(KProcess *p);
+ void slotWroteStdin(KProcess *p);
+
+ private:
+ QString mProcessName;
+ KProcess *mProcess;
+ QPtrQueue <QByteArray> mQueue;
+ QByteArray *mSendBuffer;
+ QByteArray mReceiveBuffer;
+ unsigned int mReceiveCount;
+};
+
+class KMessageFilePipe : public KMessageIO
+{
+ Q_OBJECT
+
+ public:
+ KMessageFilePipe(QObject *parent,QFile *readFile,QFile *writeFile);
+ ~KMessageFilePipe();
+ bool isConnected() const;
+ void send (const QByteArray &msg);
+ void exec();
+
+ /**
+ @return FALSE as this is no network IO.
+ */
+ bool isNetwork() const { return false; }
+
+ /**
+ * The runtime idendifcation
+ */
+ virtual int rtti() const {return 4;}
+
+
+
+ private:
+ QFile *mReadFile;
+ QFile *mWriteFile;
+ QByteArray mReceiveBuffer;
+ unsigned int mReceiveCount;
+};
+
+#endif
diff --git a/libkdegames/kgame/kmessageserver.cpp b/libkdegames/kgame/kmessageserver.cpp
new file mode 100644
index 00000000..e857ea31
--- /dev/null
+++ b/libkdegames/kgame/kmessageserver.cpp
@@ -0,0 +1,515 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <qiodevice.h>
+#include <qbuffer.h>
+#include <qptrlist.h>
+#include <qptrqueue.h>
+#include <qtimer.h>
+#include <qvaluelist.h>
+
+#include <kdebug.h>
+
+#include "kmessageio.h"
+#include "kmessageserver.h"
+
+// --------------- internal class KMessageServerSocket
+
+KMessageServerSocket::KMessageServerSocket (Q_UINT16 port, QObject *parent)
+ : QServerSocket (port, 0, parent)
+{
+}
+
+KMessageServerSocket::~KMessageServerSocket ()
+{
+}
+
+void KMessageServerSocket::newConnection (int socket)
+{
+ emit newClientConnected (new KMessageSocket (socket));
+}
+
+// ---------------- class for storing an incoming message
+
+class MessageBuffer
+{
+ public:
+ MessageBuffer (Q_UINT32 clientID, const QByteArray &messageData)
+ : id (clientID), data (messageData) { }
+ ~MessageBuffer () {}
+ Q_UINT32 id;
+ QByteArray data;
+};
+
+// ---------------- KMessageServer's private class
+
+class KMessageServerPrivate
+{
+public:
+ KMessageServerPrivate()
+ : mMaxClients (-1), mGameId (1), mUniqueClientNumber (1), mAdminID (0), mServerSocket (0)
+ {
+ mClientList.setAutoDelete (true);
+ mMessageQueue.setAutoDelete (true);
+ }
+
+ int mMaxClients;
+ int mGameId;
+ Q_UINT16 mCookie;
+ Q_UINT32 mUniqueClientNumber;
+ Q_UINT32 mAdminID;
+
+ KMessageServerSocket* mServerSocket;
+
+ QPtrList <KMessageIO> mClientList;
+ QPtrQueue <MessageBuffer> mMessageQueue;
+ QTimer mTimer;
+ bool mIsRecursive;
+};
+
+
+// ------------------ KMessageServer
+
+KMessageServer::KMessageServer (Q_UINT16 cookie,QObject* parent)
+ : QObject(parent, 0)
+{
+ d = new KMessageServerPrivate;
+ d->mIsRecursive=false;
+ d->mCookie=cookie;
+ connect (&(d->mTimer), SIGNAL (timeout()),
+ this, SLOT (processOneMessage()));
+ kdDebug(11001) << "CREATE(KMessageServer="
+ << this
+ << ") cookie="
+ << d->mCookie
+ << " sizeof(this)="
+ << sizeof(KMessageServer)
+ << endl;
+}
+
+KMessageServer::~KMessageServer()
+{
+ kdDebug(11001) << k_funcinfo << "this=" << this << endl;
+ Debug();
+ stopNetwork();
+ deleteClients();
+ delete d;
+ kdDebug(11001) << k_funcinfo << " done" << endl;
+}
+
+//------------------------------------- TCP/IP server stuff
+
+bool KMessageServer::initNetwork (Q_UINT16 port)
+{
+ kdDebug(11001) << k_funcinfo << endl;
+
+ if (d->mServerSocket)
+ {
+ kdDebug (11001) << k_funcinfo << ": We were already offering connections!" << endl;
+ delete d->mServerSocket;
+ }
+
+ d->mServerSocket = new KMessageServerSocket (port);
+ d->mIsRecursive = false;
+
+ if (!d->mServerSocket || !d->mServerSocket->ok())
+ {
+ kdError(11001) << k_funcinfo << ": Serversocket::ok() == false" << endl;
+ delete d->mServerSocket;
+ d->mServerSocket=0;
+ return false;
+ }
+
+ kdDebug (11001) << k_funcinfo << ": Now listening to port "
+ << d->mServerSocket->port() << endl;
+ connect (d->mServerSocket, SIGNAL (newClientConnected (KMessageIO*)),
+ this, SLOT (addClient (KMessageIO*)));
+ return true;
+}
+
+Q_UINT16 KMessageServer::serverPort () const
+{
+ if (d->mServerSocket)
+ return d->mServerSocket->port();
+ else
+ return 0;
+}
+
+void KMessageServer::stopNetwork()
+{
+ if (d->mServerSocket)
+ {
+ delete d->mServerSocket;
+ d->mServerSocket = 0;
+ }
+}
+
+bool KMessageServer::isOfferingConnections() const
+{
+ return d->mServerSocket != 0;
+}
+
+//----------------------------------------------- adding / removing clients
+
+void KMessageServer::addClient (KMessageIO* client)
+{
+ QByteArray msg;
+
+ // maximum number of clients reached?
+ if (d->mMaxClients >= 0 && d->mMaxClients <= clientCount())
+ {
+ kdError (11001) << k_funcinfo << ": Maximum number of clients reached!" << endl;
+ return;
+ }
+
+ // give it a unique ID
+ client->setId (uniqueClientNumber());
+ kdDebug (11001) << k_funcinfo << ": " << client->id() << endl;
+
+ // connect its signals
+ connect (client, SIGNAL (connectionBroken()),
+ this, SLOT (removeBrokenClient()));
+ connect (client, SIGNAL (received (const QByteArray &)),
+ this, SLOT (getReceivedMessage (const QByteArray &)));
+
+ // Tell everyone about the new guest
+ // Note: The new client doesn't get this message!
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_CONNECTED) << client->id();
+ broadcastMessage (msg);
+
+ // add to our list
+ d->mClientList.append (client);
+
+ // tell it its ID
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_ID) << client->id();
+ client->send (msg);
+
+ // Give it the complete list of client IDs
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs();
+ client->send (msg);
+
+
+ if (clientCount() == 1)
+ {
+ // if it is the first client, it becomes the admin
+ setAdmin (client->id());
+ }
+ else
+ {
+ // otherwise tell it who is the admin
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID();
+ client->send (msg);
+ }
+
+ emit clientConnected (client);
+}
+
+void KMessageServer::removeClient (KMessageIO* client, bool broken)
+{
+ Q_UINT32 clientID = client->id();
+ if (!d->mClientList.removeRef (client))
+ {
+ kdError(11001) << k_funcinfo << ": Deleting client that wasn't added before!" << endl;
+ return;
+ }
+
+ // tell everyone about the removed client
+ QByteArray msg;
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (EVNT_CLIENT_DISCONNECTED) << client->id() << (Q_INT8)broken;
+ broadcastMessage (msg);
+
+ // If it was the admin, select a new admin.
+ if (clientID == adminID())
+ {
+ if (!d->mClientList.isEmpty())
+ setAdmin (d->mClientList.first()->id());
+ else
+ setAdmin (0);
+ }
+}
+
+void KMessageServer::deleteClients()
+{
+ d->mClientList.clear();
+ d->mAdminID = 0;
+}
+
+void KMessageServer::removeBrokenClient ()
+{
+ if (!sender()->inherits ("KMessageIO"))
+ {
+ kdError (11001) << k_funcinfo << ": sender of the signal was not a KMessageIO object!" << endl;
+ return;
+ }
+
+ KMessageIO *client = (KMessageIO *) sender();
+
+ emit connectionLost (client);
+ removeClient (client, true);
+}
+
+
+void KMessageServer::setMaxClients(int c)
+{
+ d->mMaxClients = c;
+}
+
+int KMessageServer::maxClients() const
+{
+ return d->mMaxClients;
+}
+
+int KMessageServer::clientCount() const
+{
+ return d->mClientList.count();
+}
+
+QValueList <Q_UINT32> KMessageServer::clientIDs () const
+{
+ QValueList <Q_UINT32> list;
+ for (QPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter)
+ list.append ((*iter)->id());
+ return list;
+}
+
+KMessageIO* KMessageServer::findClient (Q_UINT32 no) const
+{
+ if (no == 0)
+ no = d->mAdminID;
+
+ QPtrListIterator <KMessageIO> iter (d->mClientList);
+ while (*iter)
+ {
+ if ((*iter)->id() == no)
+ return (*iter);
+ ++iter;
+ }
+ return 0;
+}
+
+Q_UINT32 KMessageServer::adminID () const
+{
+ return d->mAdminID;
+}
+
+void KMessageServer::setAdmin (Q_UINT32 adminID)
+{
+ // Trying to set the the client that is already admin => nothing to do
+ if (adminID == d->mAdminID)
+ return;
+
+ if (adminID > 0 && findClient (adminID) == 0)
+ {
+ kdWarning (11001) << "Trying to set a new admin that doesn't exist!" << endl;
+ return;
+ }
+
+ d->mAdminID = adminID;
+
+ QByteArray msg;
+ QDataStream (msg, IO_WriteOnly) << Q_UINT32 (ANS_ADMIN_ID) << adminID;
+
+ // Tell everyone about the new master
+ broadcastMessage (msg);
+}
+
+
+//------------------------------------------- ID stuff
+
+Q_UINT32 KMessageServer::uniqueClientNumber() const
+{
+ return d->mUniqueClientNumber++;
+}
+
+// --------------------- Messages ---------------------------
+
+void KMessageServer::broadcastMessage (const QByteArray &msg)
+{
+ for (QPtrListIterator <KMessageIO> iter (d->mClientList); *iter; ++iter)
+ (*iter)->send (msg);
+}
+
+void KMessageServer::sendMessage (Q_UINT32 id, const QByteArray &msg)
+{
+ KMessageIO *client = findClient (id);
+ if (client)
+ client->send (msg);
+}
+
+void KMessageServer::sendMessage (const QValueList <Q_UINT32> &ids, const QByteArray &msg)
+{
+ for (QValueListConstIterator <Q_UINT32> iter = ids.begin(); iter != ids.end(); ++iter)
+ sendMessage (*iter, msg);
+}
+
+void KMessageServer::getReceivedMessage (const QByteArray &msg)
+{
+ if (!sender() || !sender()->inherits("KMessageIO"))
+ {
+ kdError (11001) << k_funcinfo << ": slot was not called from KMessageIO!" << endl;
+ return;
+ }
+ //kdDebug(11001) << k_funcinfo << ": size=" << msg.size() << endl;
+ KMessageIO *client = (KMessageIO *) sender();
+ Q_UINT32 clientID = client->id();
+
+ //QByteArray *ta=new QByteArray;
+ //ta->duplicate(msg);
+ //d->mMessageQueue.enqueue (new MessageBuffer (clientID, *ta));
+
+
+ d->mMessageQueue.enqueue (new MessageBuffer (clientID, msg));
+ if (!d->mTimer.isActive())
+ d->mTimer.start(0); // AB: should be , TRUE i guess
+}
+
+void KMessageServer::processOneMessage ()
+{
+ // This shouldn't happen, since the timer should be stopped before. But only to be sure!
+ if (d->mMessageQueue.isEmpty())
+ {
+ d->mTimer.stop();
+ return;
+ }
+ if (d->mIsRecursive)
+ {
+ return;
+ }
+ d->mIsRecursive = true;
+
+ MessageBuffer *msg_buf = d->mMessageQueue.head();
+
+ Q_UINT32 clientID = msg_buf->id;
+ QBuffer in_buffer (msg_buf->data);
+ in_buffer.open (IO_ReadOnly);
+ QDataStream in_stream (&in_buffer);
+
+ QByteArray out_msg;
+ QBuffer out_buffer (out_msg);
+ out_buffer.open (IO_WriteOnly);
+ QDataStream out_stream (&out_buffer);
+
+ bool unknown = false;
+
+ QByteArray ttt=in_buffer.buffer();
+ Q_UINT32 messageID;
+ in_stream >> messageID;
+ //kdDebug(11001) << k_funcinfo << ": got message with messageID=" << messageID << endl;
+ switch (messageID)
+ {
+ case REQ_BROADCAST:
+ out_stream << Q_UINT32 (MSG_BROADCAST) << clientID;
+ // FIXME, compiler bug?
+ // this should be okay, since QBuffer is subclass of QIODevice! :
+ // out_buffer.writeBlock (in_buffer.readAll());
+ out_buffer.QIODevice::writeBlock (in_buffer.readAll());
+ broadcastMessage (out_msg);
+ break;
+
+ case REQ_FORWARD:
+ {
+ QValueList <Q_UINT32> clients;
+ in_stream >> clients;
+ out_stream << Q_UINT32 (MSG_FORWARD) << clientID << clients;
+ // see above!
+ out_buffer.QIODevice::writeBlock (in_buffer.readAll());
+ sendMessage (clients, out_msg);
+ }
+ break;
+
+ case REQ_CLIENT_ID:
+ out_stream << Q_UINT32 (ANS_CLIENT_ID) << clientID;
+ sendMessage (clientID, out_msg);
+ break;
+
+ case REQ_ADMIN_ID:
+ out_stream << Q_UINT32 (ANS_ADMIN_ID) << d->mAdminID;
+ sendMessage (clientID, out_msg);
+ break;
+
+ case REQ_ADMIN_CHANGE:
+ if (clientID == d->mAdminID)
+ {
+ Q_UINT32 newAdmin;
+ in_stream >> newAdmin;
+ setAdmin (newAdmin);
+ }
+ break;
+
+ case REQ_REMOVE_CLIENT:
+ if (clientID == d->mAdminID)
+ {
+ QValueList <Q_UINT32> client_list;
+ in_stream >> client_list;
+ for (QValueListIterator <Q_UINT32> iter = client_list.begin(); iter != client_list.end(); ++iter)
+ {
+ KMessageIO *client = findClient (*iter);
+ if (client)
+ removeClient (client, false);
+ else
+ kdWarning (11001) << k_funcinfo << ": removing non-existing clientID" << endl;
+ }
+ }
+ break;
+
+ case REQ_MAX_NUM_CLIENTS:
+ if (clientID == d->mAdminID)
+ {
+ Q_INT32 maximum_clients;
+ in_stream >> maximum_clients;
+ setMaxClients (maximum_clients);
+ }
+ break;
+
+ case REQ_CLIENT_LIST:
+ {
+ out_stream << Q_UINT32 (ANS_CLIENT_LIST) << clientIDs();
+ sendMessage (clientID, out_msg);
+ }
+ break;
+
+ default:
+ unknown = true;
+ }
+
+ // check if all the data has been used
+ if (!unknown && !in_buffer.atEnd())
+ kdWarning (11001) << k_funcinfo << ": Extra data received for message ID " << messageID << endl;
+
+ emit messageReceived (msg_buf->data, clientID, unknown);
+
+ if (unknown)
+ kdWarning (11001) << k_funcinfo << ": received unknown message ID " << messageID << endl;
+
+ // remove the message, since we are ready with it
+ d->mMessageQueue.remove();
+ if (d->mMessageQueue.isEmpty())
+ d->mTimer.stop();
+ d->mIsRecursive = false;
+}
+
+void KMessageServer::Debug()
+{
+ kdDebug(11001) << "------------------ KMESSAGESERVER -----------------------" << endl;
+ kdDebug(11001) << "MaxClients : " << maxClients() << endl;
+ kdDebug(11001) << "NoOfClients : " << clientCount() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+#include "kmessageserver.moc"
diff --git a/libkdegames/kgame/kmessageserver.h b/libkdegames/kgame/kmessageserver.h
new file mode 100644
index 00000000..0049a2e7
--- /dev/null
+++ b/libkdegames/kgame/kmessageserver.h
@@ -0,0 +1,492 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Burkhard Lehner ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KMESSAGESERVER_H__
+#define __KMESSAGESERVER_H__
+
+#include <qobject.h>
+#include <qserversocket.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+class KMessageIO;
+class KMessageServerPrivate;
+
+/**
+ @short A server for message sending and broadcasting, using TCP/IP connections.
+
+ An object of this class listens for incoming connections via TCP/IP sockets and
+ creates KMessageSocket objects for every established connection. It receives
+ messages from the "clients", analyses them and processes an appropriate
+ reaction.
+
+ You can also use other KMessageIO objects with KMessageServer, not only
+ TCP/IP socket based ones. Use addClient to connect via an object of any
+ KMessageIO subclass. (For clients within the same process, you can e.g. use
+ KMessageDirect.) This object already has to be connected.
+
+ The messages are always packages of an arbitrary length. The format of the messages
+ is given below. All the data is stored and received with QDataStream, to be
+ platform independant.
+
+ Setting up a KMessageServer can be done like this:
+
+ \code
+ KMessageServer *server = new KMessageServer ();
+ server->initNetwork (TCP/IP-Portnumber);
+ \endcode
+
+ Usually that is everything you will do. There are a lot of public methods to
+ administrate the object (maximum number of clients, finding clients, removing
+ clients, setting the admin client, ...), but this functionality can also
+ be done by messages from the clients. So you can administrate the object completely
+ on remote.
+
+ If you want to extend the Server for your own needs (e.g. additional message types),
+ you can either create a subclass and overwrite the method processOneMessage.
+ (But don't forget to call the method of the superclass!) Or you can connect to
+ the signal messageReceived, and analyse the messages there.
+
+ Every client has a unique ID, so that messages can be sent to another dedicated
+ client or a list of clients.
+
+ One of the clients (the admin) has a special administration right. Some of the
+ administration messages can only be used with him. The admin can give the admin
+ status to another client. You can send a message to the admin by using clientID 0.
+ This is always interpreted as the admin client, independant of its real clientID.
+
+ Here is a list of the messages the KMessageServer understands:
+ &lt;&lt; means, the value is inserted into the QByteArray using QDataStream. The
+ messageIDs (REQ_BROADCAST, ...) are of type Q_UINT32.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_BROADCAST ) << raw_data
+
+ When the server receives this message, it sends the following message to
+ ALL connected clients (a broadcast), where the raw_data is left unchanged:
+ QByteArray << static_cast &lt;Q_UINT32>( MSG_BROADCAST ) << clientID << raw_data
+ Q_UINT32 clientID; // the ID of the client that sent the broadcast request
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_FORWARD ) << client_list << raw_data
+ QValueList &lt;Q_UINT32> client_list; // list of receivers
+
+ When the server receives this message, it sends the following message to
+ the clients in client_list:
+ QByteArray << static_cast&lt;Q_UINT32>( MSG_FORWARD ) << senderID << client_list << raw_data
+ Q_UINT32 senderID; // the sender of the forward request
+ QValueList &lt;Q_UINT32> client_list; // a copy of the receiver list
+
+ Note: Every client receives the message as many times as he is in the client_list.
+ Note: Since the client_list is sent to all the clients, every client can see who else
+ got the message. If you want to prevent this, send a single REQ_FORWARD
+ message for every receiver.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_CLIENT_ID )
+
+ When the server receives this message, it sends the following message to
+ the asking client:
+ QByteArray << static_cast&lt;Q_UINT32>( ANS_CLIENT_ID ) << clientID
+ Q_UINT32 clientID; // The ID of the client who asked for it
+
+ Note: This answer is also automatically sent to a new connected client, so that he
+ can store his ID. The ID of a client doesn't change during his lifetime, and is
+ unique for this KMessageServer.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_ADMIN_ID )
+
+ When the server receives this message, it sends the following message to
+ the asking client:
+ QByteArray << ANS_ADMIN_ID << adminID
+ Q_UINT32 adminID; // The ID of the admin
+
+ Note: This answer is also automatically sent to a new connected client, so that he
+ can see if he is the admin or not. It will also be sent to all connected clients
+ when a new admin is set (see REQ_ADMIN_CHANGE).
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_ADMIN_CHANGE ) << new_admin
+ Q_UINT32 new_admin; // the ID of the new admin, or 0 for no admin
+
+ When the server receives this message, it sets the admin to the new ID. If no client
+ with that ID exists, nothing happens. With new_admin == 0 no client is a admin.
+ ONLY THE ADMIN ITSELF CAN USE THIS MESSAGE!
+
+ Note: The server sends a ANS_ADMIN_ID message to every connected client.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_REMOVE_CLIENT ) << client_list
+ QValueList &lt;Q_UINT32> client_list; // The list of clients to be removed
+
+ When the server receives this message, it removes the clients with the ids stored in
+ client_list, disconnecting the connection to them.
+ ONLY THE ADMIN CAN USE THIS MESSAGE!
+
+ Note: If one of the clients is the admin himself, he will also be deleted.
+ Another client (if any left) will become the new admin.
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_MAX_NUM_CLIENTS ) << maximum_clients
+ Q_INT32 maximum_clients; // The maximum of clients connected, or infinite if -1
+
+ When the server receives this message, it limits the number of clients to the number given,
+ or sets it unlimited for maximum_clients == -1.
+ ONLY THE ADMIN CAN USE THIS MESSAGE!
+
+ Note: If there are already more clients, they are not affected. It only prevents new Clients
+ to be added. To assure this limit, remove clients afterwards (REQ_REMOVE_CLIENT)
+
+ - QByteArray << static_cast&lt;Q_UINT32>( REQ_CLIENT_LIST )
+
+ When the server receives this message, it answers by sending a list of IDs of all the clients
+ that are connected at the moment. So it sends the following message to the asking client:
+ QByteArray << static_cast&lt;Q_UINT32>( ANS_CLIENT_LIST ) << clientList
+ QValueList &lt;Q_UINT32> clientList; // The IDs of the connected clients
+
+ Note: This message is also sent to every new connected client, so that he knows the other
+ clients.
+
+ There are two more messages that are sent from the server to the every client automatically
+ when a new client connects or a connection to a client is lost:
+
+ QByteArray << static_cast&lt;Q_UINT32>( EVNT_CLIENT_CONNECTED ) << clientID;
+ Q_UINT32 clientID; // the ID of the new connected client
+
+ QByteArray << static_cast&lt;Q_UINT32>( EVNT_CLIENT_DISCONNECTED ) << clientID;
+ Q_UINT32 clientID; // the ID of the client that lost the connection
+ Q_UINT8 broken; // 1 if the network connection was closed, 0 if it was disconnected
+ // on purpose
+
+
+ @author Andreas Beckermann <[email protected]>, Burkhard Lehner <[email protected]>
+ @version $Id$
+*/
+class KMessageServer : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ MessageIDs for messages from a client to the message server.
+ */
+ enum {
+ REQ_BROADCAST = 1,
+ REQ_FORWARD,
+ REQ_CLIENT_ID,
+ REQ_ADMIN_ID,
+ REQ_ADMIN_CHANGE,
+ REQ_REMOVE_CLIENT,
+ REQ_MAX_NUM_CLIENTS,
+ REQ_CLIENT_LIST,
+ REQ_MAX_REQ = 0xffff };
+
+ /**
+ * MessageIDs for messages from the message server to a client.
+ **/
+ enum {
+ MSG_BROADCAST = 101,
+ MSG_FORWARD,
+ ANS_CLIENT_ID,
+ ANS_ADMIN_ID,
+ ANS_CLIENT_LIST,
+ EVNT_CLIENT_CONNECTED,
+ EVNT_CLIENT_DISCONNECTED,
+ EVNT_MAX_EVNT = 0xffff
+ };
+
+ /**
+ * Create a KGameNetwork object
+ **/
+ KMessageServer(Q_UINT16 cookie = 42, QObject* parent = 0);
+
+ ~KMessageServer();
+
+ /**
+ * Gives debug output of the game status
+ **/
+ virtual void Debug();
+
+//---------------------------------- TCP/IP server stuff
+
+ /**
+ * Starts the Communication server to listen for incoming TCP/IP connections.
+ *
+ * @param port The port on which the service is offered, or 0 to let the
+ * system pick a free port
+ * @return true if it worked
+ */
+ bool initNetwork (Q_UINT16 port = 0);
+
+ /**
+ * Returns the TCP/IP port number we are listening to for incoming connections.
+ * (This has to be known by other clients so that they can connect to us. It's
+ * especially necessary if you used 0 as port number in initNetwork().
+ * @return the port number
+ **/
+ Q_UINT16 serverPort () const;
+
+ /**
+ * Stops listening for connections. The already running connections are
+ * not affected.
+ * To listen for connections again call initNetwork again.
+ **/
+ void stopNetwork();
+
+ /**
+ * Are we still offer offering server connections?
+ * @return true, if we are still listening to connections requests
+ **/
+ bool isOfferingConnections() const;
+
+//---------------------------------- adding / removing clients
+
+public slots:
+ /**
+ * Adds a new @ref KMessageIO object to the communication server. This "client"
+ * gets a unique ID.
+ *
+ * This slot method is automatically called for any incoming TCP/IP
+ * connection. You can use it to add other types of connections, e.g.
+ * local connections (KMessageDirect) to the server manually.
+ *
+ * NOTE: The @ref KMessageIO object gets owned by the KMessageServer,
+ * so don't delete or manipulate it afterwards. It is automatically deleted
+ * when the connection is broken or the communication server is deleted.
+ * So, add a @ref KMessageIO object to just ONE KMessageServer.
+ **/
+ void addClient (KMessageIO *);
+
+ /**
+ * Removes the KMessageIO object from the client list and deletes it.
+ * This destroys the connection, if it already was up.
+ * Does NOT emit connectionLost.
+ * Sends an info message to the other clients, that contains the ID of
+ * the removed client and the value of the parameter broken.
+ *
+ * @param io the object to delete and to remove from the client list
+ * @param broken true if the client has lost connection
+ * Mostly used internally. You will probably not need this.
+ **/
+ void removeClient (KMessageIO *io, bool broken);
+
+ /**
+ Deletes all connections to the clients.
+ */
+ void deleteClients();
+
+private slots:
+ /**
+ * Removes the sender object of the signal that called this slot. It is
+ * automatically connected to @ref KMessageIO::connectionBroken.
+ * Emits @ref connectionLost (KMessageIO*), and deletes the @ref KMessageIO object.
+ * Don't call it directly!
+ **/
+ void removeBrokenClient ();
+
+public:
+ /**
+ * sets the maximum number of clients which can connect.
+ * If this number is reached, no more clients can be added.
+ * Setting this number to -1 means unlimited number of clients.
+ *
+ * NOTE: Existing connections are not affected.
+ * So, clientCount > maxClients is possible, if there were already
+ * more clients than allowed before reducing this value.
+ *
+ * @param maxnumber the number of clients
+ **/
+ void setMaxClients(int maxnumber);
+
+ /**
+ * returns the maximum number of clients
+ *
+ * @return the number of clients
+ **/
+ int maxClients() const;
+
+ /**
+ * returns the current number of connected clients.
+ *
+ * @return the number of clients
+ **/
+ int clientCount() const;
+
+ /**
+ * returns a list of the unique IDs of all clients.
+ **/
+ QValueList <Q_UINT32> clientIDs() const;
+
+ /**
+ * Find the @ref KMessageIO object to the given client number.
+ * @param no the client number to look for, or 0 to look for the admin
+ * @return address of the client, or 0 if no client with that number exists
+ **/
+ KMessageIO *findClient (Q_UINT32 no) const;
+
+ /**
+ * Returns the clientID of the admin, if there is a admin, 0 otherwise.
+ *
+ * NOTE: Most often you don't need to know that id, since you can
+ * use clientID 0 to specify the admin.
+ **/
+ Q_UINT32 adminID() const;
+
+ /**
+ * Sets the admin to a new client with the given ID.
+ * The old admin (if existed) and the new admin will get the ANS_ADMIN message.
+ * If you use 0 as new adminID, no client will be admin.
+ **/
+ void setAdmin (Q_UINT32 adminID);
+
+
+//------------------------------ ID stuff
+
+ /*
+ * The unique ID of this game
+ *
+ * @return int id
+ **/
+// int gameId() const;
+
+ /*
+ * Application cookie. this idendifies the game application. It
+ * help to distinguish between e.g. KPoker and KWin4
+ *
+ * @return the application cookie
+ **/
+// int cookie() const;
+
+//------------------------------ Message stuff
+
+public:
+ /**
+ * Sends a message to all connected clients.
+ * The message is NOT translated in any way. This method calls
+ * @ref KMessageIO::send for every client added.
+ **/
+ virtual void broadcastMessage (const QByteArray &msg);
+
+ /**
+ * Sends a message to a single client with the given ID.
+ * The message is NOT translated in any way.
+ * If no client with the given id exists, nothing is done.
+ * This is just a convenience method. You could also call
+ * @ref findClient (id)->send(msg) manually, but this method checks for
+ * errors.
+ **/
+ virtual void sendMessage (Q_UINT32 id, const QByteArray &msg);
+
+ /**
+ * Sends a message to a list of clients. Their ID is given in ids. If
+ * a client id is given more than once in the list, the message is also
+ * sent several times to that client.
+ * This is just a convenience method. You could also iterate over the
+ * list of IDs.
+ **/
+ virtual void sendMessage (const QValueList <Q_UINT32> &ids, const QByteArray &msg);
+
+protected slots:
+ /**
+ * This slot receives all the messages from the @ref KMessageIO::received signals.
+ * It stores the messages in a queue. The messages are later taken out of the queue
+ * by @ref getReceivedMessage.
+ *
+ * NOTE: It is important that this slot may only be called from the signal
+ * @ref KMessageIO::received, since the sender() object is used to find out
+ * the client that sent the message!
+ **/
+ virtual void getReceivedMessage (const QByteArray &msg);
+
+ /**
+ * This slot is called whenever there are elements in the message queue. This queue
+ * is filled by @ref getReceivedMessage.
+ * This slot takes one message out of the queue and analyses processes it,
+ * if it recognizes it. (See message types in the description of the class.)
+ * After that, the signal @ref messageReceived is emitted. Connect to that signal if
+ * you want to process other types of messages.
+ **/
+ virtual void processOneMessage ();
+
+//---------------------------- Signals
+
+signals:
+ /**
+ * A new client connected to the game
+ * @param client the client object that connected
+ **/
+ void clientConnected (KMessageIO *client);
+
+ /**
+ * A network connection got broken. Note that the client will automatically get deleted
+ * after this signal is emitted. The signal is not emitted when the client was removed
+ * regularly.
+ *
+ * @param client the client which left the game
+ **/
+ void connectionLost (KMessageIO *client);
+
+ /**
+ * This signal is always emitted when a message from a client is received.
+ *
+ * You can use this signal to extend the communication server without subclassing.
+ * Just connect to this signal and analyse the message, if unknown is true.
+ * If you recognize a message and process it, set unknown to false, otherwise
+ * a warning message is printed.
+ *
+ * @param data the message data
+ * @param clientID the ID of the KMessageIO object that received the message
+ * @param unknown true, if the message type is not known by the KMessageServer
+ **/
+ void messageReceived (const QByteArray &data, Q_UINT32 clientID, bool &unknown);
+
+protected:
+ /**
+ * @return A unique number which can be used as the id of a @ref KMessageIO. It is
+ * incremented after every call so if you need the id twice you have to save
+ * it anywhere. It's currently used to initialize newly connected clints only.
+ **/
+ Q_UINT32 uniqueClientNumber() const;
+
+private:
+ KMessageServerPrivate* d;
+};
+
+
+/**
+ Internal class of KMessageServer. Creates a server socket and waits for
+ connections.
+
+ NOTE: This has to be here in the header file, because it is a subclass from
+ QObject and has to go through the moc.
+
+ @short An internal class for KServerSocket
+ @author Burkhard Lehner <[email protected]>
+*/
+class KMessageServerSocket : public QServerSocket
+{
+ Q_OBJECT
+
+public:
+ KMessageServerSocket (Q_UINT16 port, QObject *parent = 0);
+ ~KMessageServerSocket ();
+
+ void newConnection (int socket);
+
+signals:
+ void newClientConnected (KMessageIO *client);
+};
+
+
+
+#endif
diff --git a/libkdegames/kgame/kmessageserver.png b/libkdegames/kgame/kmessageserver.png
new file mode 100644
index 00000000..07fba6c5
--- /dev/null
+++ b/libkdegames/kgame/kmessageserver.png
Binary files differ
diff --git a/libkdegames/kgame/kplayer.cpp b/libkdegames/kgame/kplayer.cpp
new file mode 100644
index 00000000..0f8ea184
--- /dev/null
+++ b/libkdegames/kgame/kplayer.cpp
@@ -0,0 +1,446 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+/*
+ $Id$
+*/
+
+
+#include "kgame.h"
+#include "kgameio.h"
+#include "kplayer.h"
+#include "kgamemessage.h"
+#include "kgamepropertyhandler.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qbuffer.h>
+
+#include <stdio.h>
+#include <assert.h>
+
+#define KPLAYER_LOAD_COOKIE 7285
+
+class KPlayerPrivate
+{
+public:
+ KPlayerPrivate()
+ {
+ mNetworkPlayer = 0;
+ }
+
+ Q_UINT32 mId;
+ bool mVirtual; // virtual player
+ int mPriority; // tag for replacement
+
+ KPlayer* mNetworkPlayer; // replacement player
+
+ KGamePropertyHandler mProperties;
+
+// Playerdata
+ KGamePropertyQString mName;
+ KGamePropertyQString mGroup;
+};
+
+KPlayer::KPlayer() : QObject(0,0)
+{
+ init();
+}
+
+KPlayer::KPlayer(KGame* game) : QObject(0, 0)
+{
+ init();
+ game->addPlayer(this);
+}
+
+void KPlayer::init()
+{
+// note that NO KGame object exists here! so we cannot use KGameProperty::send!
+ kdDebug(11001) << k_funcinfo << ": this=" << this << ", sizeof(this)="<<sizeof(KPlayer) << endl;
+ kdDebug(11001) << "sizeof(m_Group)="<<sizeof(d->mGroup)<<endl;
+ d = new KPlayerPrivate;
+
+ d->mProperties.registerHandler(KGameMessage::IdPlayerProperty,
+ this,SLOT(sendProperty(int, QDataStream&, bool*)),
+ SLOT(emitSignal(KGamePropertyBase *)));
+ d->mVirtual=false;
+ mActive=true;
+ mGame=0;
+ d->mId=0; // "0" is always an invalid ID!
+ d->mPriority=0;
+ // I guess we cannot translate the group otherwise no
+ // international conenctions are possible
+
+ mUserId.registerData(KGamePropertyBase::IdUserId, this, i18n("UserId"));
+ mUserId.setLocal(0);
+ d->mGroup.registerData(KGamePropertyBase::IdGroup, this, i18n("Group"));
+ d->mGroup.setLocal(i18n("default"));
+ d->mName.registerData(KGamePropertyBase::IdName, this, i18n("Name"));
+ d->mName.setLocal(i18n("default"));
+
+ mAsyncInput.registerData(KGamePropertyBase::IdAsyncInput, this, i18n("AsyncInput"));
+ mAsyncInput.setLocal(false);
+ mMyTurn.registerData(KGamePropertyBase::IdTurn, this, i18n("myTurn"));
+ mMyTurn.setLocal(false);
+ mMyTurn.setEmittingSignal(true);
+ mMyTurn.setOptimized(false);
+}
+
+KPlayer::~KPlayer()
+{
+ kdDebug(11001) << k_funcinfo << ": this=" << this <<", id=" << this->id() << endl;
+
+ // Delete IODevices
+ KGameIO *input;
+ while((input=mInputList.first()))
+ {
+ delete input;
+ }
+ if (game())
+ {
+ game()->playerDeleted(this);
+ }
+
+// note: mProperties does not use autoDelete or so - user must delete objects
+// himself
+ d->mProperties.clear();
+ delete d;
+// kdDebug(11001) << k_funcinfo << " done" << endl;
+}
+
+bool KPlayer::forwardMessage(QDataStream &msg,int msgid,Q_UINT32 receiver,Q_UINT32 sender)
+{
+ if (!isActive())
+ {
+ return false;
+ }
+ if (!game())
+ {
+ return false;
+ }
+ kdDebug(11001) << k_funcinfo << ": to game sender="<<sender<<"" << "recv="<<receiver <<"msgid="<<msgid << endl;
+ return game()->sendSystemMessage(msg,msgid,receiver,sender);
+}
+
+bool KPlayer::forwardInput(QDataStream &msg,bool transmit,Q_UINT32 sender)
+{
+ if (!isActive())
+ {
+ return false;
+ }
+ if (!game())
+ {
+ return false;
+ }
+
+ kdDebug(11001) << k_funcinfo << ": to game playerInput(sender="<<sender<<")" << endl;
+ if (!asyncInput() && !myTurn())
+ {
+ kdDebug(11001) << k_funcinfo << ": rejected cause it is not our turn" << endl;
+ return false;
+ }
+
+ // AB: I hope I remember the usage correctly:
+ // this function is called twice (on sender side) - once with transmit = true
+ // where it sends the input to the comserver and once with transmit = false
+ // where it really looks at the input
+ if (transmit)
+ {
+ kdDebug(11001) << "indirect playerInput" << endl;
+ return game()->sendPlayerInput(msg,this,sender);
+ }
+ else
+ {
+ kdDebug(11001) << "direct playerInput" << endl;
+ return game()->systemPlayerInput(msg,this,sender);
+ }
+}
+
+void KPlayer::setId(Q_UINT32 newid)
+{
+ // Needs to be after the sendProcess
+ d->mId=newid;
+}
+
+
+void KPlayer::setGroup(const QString& group)
+{ d->mGroup = group; }
+
+const QString& KPlayer::group() const
+{ return d->mGroup.value(); }
+
+void KPlayer::setName(const QString& name)
+{ d->mName = name; }
+
+const QString& KPlayer::name() const
+{ return d->mName.value(); }
+
+Q_UINT32 KPlayer::id() const
+{ return d->mId; }
+
+KGamePropertyHandler * KPlayer::dataHandler()
+{ return &d->mProperties; }
+
+void KPlayer::setVirtual(bool v)
+{ d->mVirtual = v; }
+
+bool KPlayer::isVirtual() const
+{ return d->mVirtual;}
+
+void KPlayer::setNetworkPlayer(KPlayer* p)
+{ d->mNetworkPlayer = p; }
+
+KPlayer* KPlayer::networkPlayer() const
+{ return d->mNetworkPlayer; }
+
+int KPlayer::networkPriority() const
+{ return d->mPriority; }
+
+void KPlayer::setNetworkPriority(int p)
+{ d->mPriority = p; }
+
+bool KPlayer::addGameIO(KGameIO *input)
+{
+ if (!input)
+ {
+ return false;
+ }
+ mInputList.append(input);
+ input->initIO(this); // set player and init device
+ return true;
+}
+
+// input=0, remove all
+bool KPlayer::removeGameIO(KGameIO *targetinput,bool deleteit)
+{
+ kdDebug(11001) << k_funcinfo << ": " << targetinput << " delete=" << deleteit<< endl;
+ bool result=true;
+ if (!targetinput) // delete all
+ {
+ KGameIO *input;
+ while((input=mInputList.first()))
+ {
+ if (input) removeGameIO(input,deleteit);
+ }
+ }
+ else
+ {
+// kdDebug(11001) << "remove IO " << targetinput << endl;
+ if (deleteit)
+ {
+ delete targetinput;
+ }
+ else
+ {
+ targetinput->setPlayer(0);
+ result=mInputList.remove(targetinput);
+ }
+ }
+ return result;
+}
+
+KGameIO * KPlayer::findRttiIO(int rtti) const
+{
+ QPtrListIterator<KGameIO> it(mInputList);
+ while (it.current())
+ {
+ if (it.current()->rtti() == rtti)
+ {
+ return it.current();
+ }
+ ++it;
+ }
+ return 0;
+}
+
+int KPlayer::calcIOValue()
+{
+ int value=0;
+ QPtrListIterator<KGameIO> it(mInputList);
+ while (it.current())
+ {
+ value|=it.current()->rtti();
+ ++it;
+ }
+ return value;
+}
+
+bool KPlayer::setTurn(bool b, bool exclusive)
+{
+ kdDebug(11001) << k_funcinfo << ": " << id() << " (" << this << ") to " << b << endl;
+ if (!isActive())
+ {
+ return false;
+ }
+
+ // if we get to do an exclusive turn all other players are disallowed
+ if (exclusive && b && game())
+ {
+ KPlayer *player;
+ KGame::KGamePlayerList *list=game()->playerList();
+ for ( player=list->first(); player != 0; player=list->next() )
+ {
+ if (player==this)
+ {
+ continue;
+ }
+ player->setTurn(false,false);
+ }
+ }
+
+ // Return if nothing changed
+ mMyTurn = b;
+
+ return true;
+}
+
+bool KPlayer::load(QDataStream &stream)
+{
+ Q_INT32 id,priority;
+ stream >> id >> priority;
+ setId(id);
+ setNetworkPriority(priority);
+
+ // Load Player Data
+ //FIXME: maybe set all properties setEmitSignal(false) before?
+ d->mProperties.load(stream);
+
+ Q_INT16 cookie;
+ stream >> cookie;
+ if (cookie==KPLAYER_LOAD_COOKIE)
+ {
+ kdDebug(11001) << " Player loaded propertly"<<endl;
+ }
+ else
+ {
+ kdError(11001) << " Player loading error. probably format error"<<endl;
+ }
+
+ // emit signalLoad(stream);
+ return true;
+}
+
+bool KPlayer::save(QDataStream &stream)
+{
+ stream << (Q_INT32)id() << (Q_INT32)networkPriority();
+
+ d->mProperties.save(stream);
+
+ stream << (Q_INT16)KPLAYER_LOAD_COOKIE;
+
+ //emit signalSave(stream);
+ return true;
+}
+
+
+void KPlayer::networkTransmission(QDataStream &stream,int msgid,Q_UINT32 sender)
+{
+ //kdDebug(11001) << k_funcinfo ": msgid=" << msgid << " sender=" << sender << " we are=" << id() << endl;
+ // PlayerProperties processed
+ bool issender;
+ if (game())
+ {
+ issender=sender==game()->gameId();
+ }
+ else
+ {
+ issender=true;
+ }
+ if (d->mProperties.processMessage(stream,msgid,issender))
+ {
+ return ;
+ }
+ switch(msgid)
+ {
+ case KGameMessage::IdPlayerInput:
+ {
+ kdDebug(11001) << k_funcinfo << ": Got player move "
+ << "KPlayer (virtual) forwards it to the game object" << endl;
+ forwardInput(stream,false);
+ }
+ break;
+ default:
+ emit signalNetworkData(msgid - KGameMessage::IdUser,
+ ((QBuffer*)stream.device())->readAll(),sender,this);
+ kdDebug(11001) << k_funcinfo << ": "
+ << "User data msgid " << msgid << endl;
+ break;
+ }
+
+}
+
+KGamePropertyBase* KPlayer::findProperty(int id) const
+{
+ return d->mProperties.find(id);
+}
+
+bool KPlayer::addProperty(KGamePropertyBase* data)
+{
+ return d->mProperties.addProperty(data);
+}
+
+void KPlayer::sendProperty(int msgid, QDataStream& stream, bool* sent)
+{
+ if (game())
+ {
+ bool s = game()->sendPlayerProperty(msgid, stream, id());
+ if (s)
+ {
+ *sent = true;
+ }
+ }
+}
+
+void KPlayer::emitSignal(KGamePropertyBase *me)
+{
+ // Notify KGameIO (Process) for a new turn
+ if (me->id()==KGamePropertyBase::IdTurn)
+ {
+ //kdDebug(11001) << k_funcinfo << ": for KGamePropertyBase::IdTurn " << endl;
+ QPtrListIterator<KGameIO> it(mInputList);
+ while (it.current())
+ {
+ it.current()->notifyTurn(mMyTurn.value());
+ ++it;
+ }
+ }
+ emit signalPropertyChanged(me,this);
+}
+
+// --------------------- DEBUG --------------------
+void KPlayer::Debug()
+{
+ kdDebug(11001) << "------------------- KPLAYER -----------------------" << endl;
+ kdDebug(11001) << "this: " << this << endl;
+ kdDebug(11001) << "rtti: " << rtti() << endl;
+ kdDebug(11001) << "id : " << id() << endl;
+ kdDebug(11001) << "Name : " << name() << endl;
+ kdDebug(11001) << "Group: " << group() << endl;
+ kdDebug(11001) << "Async: " << asyncInput() << endl;
+ kdDebug(11001) << "myTurn: " << myTurn() << endl;
+ kdDebug(11001) << "Virtual: " << isVirtual() << endl;
+ kdDebug(11001) << "Active: " << isActive() << endl;
+ kdDebug(11001) << "Priority:" << networkPriority() << endl;
+ kdDebug(11001) << "Game : " << game() << endl;
+ kdDebug(11001) << "#IOs: " << mInputList.count() << endl;
+ kdDebug(11001) << "---------------------------------------------------" << endl;
+}
+
+#include "kplayer.moc"
diff --git a/libkdegames/kgame/kplayer.h b/libkdegames/kgame/kplayer.h
new file mode 100644
index 00000000..0e511ac3
--- /dev/null
+++ b/libkdegames/kgame/kplayer.h
@@ -0,0 +1,471 @@
+/*
+ This file is part of the KDE games library
+ Copyright (C) 2001 Martin Heni ([email protected])
+ Copyright (C) 2001 Andreas Beckermann ([email protected])
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KPLAYER_H_
+#define __KPLAYER_H_
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+#include "kgameproperty.h"
+#include <kdemacros.h>
+
+class KGame;
+class KGameIO;
+class KGamePropertyBase;
+class KGamePropertyHandler;
+
+class KPlayerPrivate;
+
+/**
+ * @short Base class for a game player
+ *
+ * The KPlayer class is the central player object. It holds
+ * information about the player and is responsible for any
+ * input the player does. For this arbitrary many KGameIO
+ * modules can be plugged into it. Main features are:
+ * - Handling of IO devices
+ * - load/save (mostly handled by KGamePropertyHandler)
+ * - Turn handling (turn based, asynchronous)
+ *
+ * A KPlayer depends on a KGame object. Call KGame::addPlayer() to plug
+ * a KPlayer into a KGame object. Note that you cannot do much with a
+ * KPlayer object before it has been plugged into a KGame. This is because
+ * most properties of KPlayer are KGameProperty which need to send messages
+ * through a KGame object to be changed.
+ *
+ * A KGameIO represents the input methods of a player and you should make all
+ * player inputs through it. So call something like playerInput->move(4);
+ * instead which should call KGameIO::sendInput() to actually move. This way
+ * you gain a *very* big advantage: you can exchange a KGameIO whenever you
+ * want! You can e.g. remove the KGameIO of a local (human) player and just
+ * replace it by a computerIO on the fly! So from that point on all playerInputs
+ * are done by the computerIO instead of the human player. You also can replace
+ * all network players by computer players when the network connection is broken
+ * or a player wants to quit.
+ * So remember: use KGameIO whenever possible! A KPlayer should just
+ * contain all data of the player (KGameIO must not!) and several common
+ * functions which are shared by all of your KGameIOs.
+ *
+ */
+class KDE_EXPORT KPlayer : public QObject
+{
+ Q_OBJECT
+
+public:
+ typedef QPtrList<KGameIO> KGameIOList;
+
+ // KPlayer(KGame *,KGameIO * input=0);
+ /**
+ * Create a new player object. It will be automatically
+ * deleted if the game it belongs to is deleted.
+ */
+ KPlayer();
+
+ /**
+ * Create a new player object. It will be automatically
+ * deleted if the game it belongs to is deleted. This constructor
+ * automatically adds the player to the game using KGame::addPlayer()
+ */
+ KPlayer(KGame* game);
+
+ virtual ~KPlayer();
+
+ /**
+ * The idendification of the player. Overwrite this in
+ * classes inherting KPlayer to run time identify them.
+ *
+ * @return 0 for default KPlayer.
+ */
+ virtual int rtti() const {return 0;}
+
+ /**
+ * Gives debug output of the game status
+ */
+ void Debug();
+
+ // properties
+ /**
+ * Returns a list of input devices
+ *
+ * @return list of devices
+ */
+ KGameIOList *ioList() {return &mInputList;}
+
+ /**
+ * sets the game the player belongs to. This
+ * is usually automatically done when adding a
+ * player
+ *
+ * @param game the game
+ */
+ void setGame(KGame *game) {mGame=game;}
+
+ /**
+ * Query to which game the player belongs to
+ *
+ * @return the game
+ */
+ KGame *game() const {return mGame;}
+
+ /**
+ * Set whether this player can make turns/input
+ * all the time (true) or only when it is its
+ * turn (false) as it is used in turn based games
+ *
+ * @param a async=true turn based=false
+ */
+ void setAsyncInput(bool a) {mAsyncInput = a;}
+
+ /**
+ * Query whether this player does asynchronous
+ * input
+ *
+ * @return true/false
+ */
+ bool asyncInput() const {return mAsyncInput.value();}
+
+ /**
+ * Is this player a virtual player, ie is it
+ * created by mirroring a real player from another
+ * network game. This mirroring is done autmatically
+ * as soon as a network connection is build and it affects
+ * all players regardless what type
+ *
+ * @return true/false
+ */
+ bool isVirtual() const;
+
+ /**
+ * @internal
+ * Sets whether this player is virtual. This is internally
+ * called
+ *
+ * @param v virtual true/false
+ */
+ void setVirtual(bool v);
+
+ /**
+ * Is this player an active player. An player is usually
+ * inactivated if it is replaced by a network connection.
+ * But this could also be called manually
+ *
+ * @return true/false
+ */
+ bool isActive() const {return mActive;}
+
+ /**
+ * Set an player as active (true) or inactive (false)
+ *
+ * @param v true=active, false=inactive
+ */
+ void setActive(bool v) {mActive=v;}
+
+ /**
+ * Returns the id of the player
+ *
+ * @return the player id
+ */
+ Q_UINT32 id() const;
+
+ /* Set the players id. This is done automatically by
+ * the game object when adding a new player!
+ *
+ * @param i the player id
+ */
+ void setId(Q_UINT32 i);
+
+ /**
+ * Returns the user defined id of the player
+ * This value can be used arbitrary by you to
+ * have some user idendification for your player,
+ * e.g. 0 for a white chess player, 1 for a black
+ * one. This value is more reliable than the player
+ * id whcih can even change when you make a network
+ * connection.
+ *
+ * @return the user defined player id
+ */
+ int userId() const {return mUserId.value();}
+
+ /* Set the user defined players id.
+ *
+ * @param i the user defined player id
+ */
+ void setUserId(int i) {mUserId = i;}
+
+ /**
+ * Returns whether this player can be replaced by a network
+ * connection player. The name of this function can be
+ * improved ;-) If you do not overwrite the function to
+ * select what players shall play in a network the KGame
+ * does an automatic selection based on the networkPriority
+ * This is not a terrible important function at the moment.
+ *
+ * @return true/false
+ */
+ int networkPriority() const;
+
+ /**
+ * Set whether this player can be replaced by a network
+ * player. There are to possible games. The first type
+ * of game has arbitrary many players. As soon as a network
+ * players connects the game runs with more players (not tagged
+ * situation). The other type is e.g. games like chess which
+ * require a constant player number. In a network game situation
+ * you would tag one or both players of all participants. As
+ * soon as the connect the tagged player will then be replaced
+ * by the network partner and it is then controlled over the network.
+ * On connection loss the old situation is automatically restored.
+ *
+ * The name of this function can be improved;-)
+ *
+ * @param b should this player be tagged
+ */
+ void setNetworkPriority(int b);
+
+ /**
+ * Returns the player which got inactivated to allow
+ * this player to be set up via network. Mostly internal
+ * function
+ */
+ KPlayer *networkPlayer() const;
+
+ /**
+ * Sets this network player replacement. Internal stuff
+ */
+ void setNetworkPlayer(KPlayer *p);
+
+ // A name and group the player belongs to
+ /**
+ * A group the player belongs to. This
+ * Can be set arbitrary by you.
+ */
+ void setGroup(const QString& group);
+
+ /**
+ * Query the group the player belongs to.
+ */
+ virtual const QString& group() const;
+
+ /**
+ * Sets the name of the player.
+ * This can be chosen arbitrary.
+ * @param name The player's name
+ */
+ void setName(const QString& name);
+
+ /**
+ * @return The name of the player.
+ */
+ virtual const QString& name() const;
+
+
+ // set devices
+ /**
+ * Adds an IO device for the player. Possible KGameIO devices
+ * can either be taken from the existing ones or be self written.
+ * Existing are e.g. Keyboard, Mouse, Computerplayer
+ *
+ * @param input the inut device
+ * @return true if ok
+ */
+ bool addGameIO(KGameIO *input);
+
+ /**
+ * remove (and delete) a game IO device
+ *
+ * The remove IO(s) is/are deleted by default. If
+ * you do not want this set the parameter deleteit to false
+ *
+ * @param input the device to be removed or 0 for all devices
+ * @param deleteit true (default) to delete the device otherwisse just remove it
+ * @return true on ok
+ */
+ bool removeGameIO(KGameIO *input=0,bool deleteit=true);
+
+ /**
+ * Finds the KGameIO devies with the given rtti code.
+ * E.g. find the mouse or network device
+ *
+ * @param rtti the rtti code to be searched for
+ * @return the KGameIO device
+ */
+ KGameIO *findRttiIO(int rtti) const;
+
+ /**
+ * Checks whether this player has a IO device of the
+ * given rtti type
+ *
+ * @param rtti the rtti typed to be checked for
+ * @return true if it exists
+ */
+ bool hasRtti(int rtti) const {return findRttiIO(rtti)!=0;}
+
+ // Message exchange
+ /**
+ * Forwards input to the game object..internal use only
+ *
+ * This method is used by KGameIO::sendInput(). Use that function
+ * instead to send player inputs!
+ *
+ * This function forwards a player input (see KGameIO classes) to the
+ * game object, see KGame, either to KGame::sendPlayerInput() (if
+ * transmit=true, ie the message has just been created) or to
+ * KGame::playerInput() (if player=false, ie the message *was* sent through
+ * KGame::sendPlayerInput).
+ */
+ virtual bool forwardInput(QDataStream &msg,bool transmit=true, Q_UINT32 sender=0);
+
+ /**
+ * Forwards Message to the game object..internal use only
+ */
+ virtual bool forwardMessage(QDataStream &msg,int msgid,Q_UINT32 receiver=0,Q_UINT32 sender=0);
+
+ // Game logic
+ /**
+ * is it my turn to go
+ *
+ * @return true/false
+ */
+ bool myTurn() const {return mMyTurn.value();}
+
+ /**
+ * Sets whether this player is the next to turn.
+ * If exclusive is given all other players are set
+ * to setTurn(false) and only this player can move
+ *
+ * @param b true/false
+ * @param exclusive true (default)/ false
+ * @return should be void
+ */
+ bool setTurn(bool b,bool exclusive=true);
+
+
+ // load/save
+ /**
+ * Load a saved player, from file OR network. By default all
+ * KGameProperty objects in the dataHandler of this player are loaded
+ * and saved when using load or save. If you need to save/load more
+ * you have to replace this function (and save). You will probably
+ * still want to call the default implementation additionally!
+ *
+ * @param stream a data stream where you can stream the player from
+ *
+ * @return true?
+ */
+ virtual bool load(QDataStream &stream);
+
+ /**
+ * Save a player to a file OR to network. See also load
+ *
+ * @param stream a data stream to load the player from
+ *
+ * @return true?
+ */
+ virtual bool save(QDataStream &stream);
+
+ /**
+ * Receives a message
+ * @param msgid The kind of the message. See messages.txt for further
+ * information
+ * @param stream The message itself
+ * @param sender
+ **/
+ void networkTransmission(QDataStream &stream,int msgid,Q_UINT32 sender);
+
+ /**
+ * Searches for a property of the player given its id.
+ * @param id The id of the property
+ * @return The property with the specified id
+ **/
+ KGamePropertyBase* findProperty(int id) const;
+
+ /**
+ * Adds a property to a player. You would add all
+ * your player specific game data as KGameProperty and
+ * they are automatically saved and exchanged over network.
+ *
+ * @param data The property to be added. Must have an unique id!
+ * @return false if the given id is not valid (ie another property owns
+ * the id) or true if the property could be added successfully
+ **/
+ bool addProperty(KGamePropertyBase* data);
+
+ /**
+ * Calculates a checksum over the IO devices. Can be used to
+ * restore the IO handlers. The value returned is the 'or'ed
+ * value of the KGameIO rtti's.
+ * this is itnernally used for saving and restorign a player.
+ */
+ int calcIOValue();
+
+ /**
+ * @return the property handler
+ */
+ KGamePropertyHandler* dataHandler();
+
+signals:
+ /**
+ * The player object got a message which was targeted
+ * at it but has no default method to process it. This
+ * means probably a user message. Connecting to this signal
+ * allowed to process it.
+ */
+ void signalNetworkData(int msgid, const QByteArray& buffer, Q_UINT32 sender, KPlayer *me);
+
+ /**
+ * This signal is emmited if a player property changes its value and
+ * the property is set to notify this change. This is an
+ * important signal as you should base the actions on a reaction
+ * to this property changes.
+ */
+ void signalPropertyChanged(KGamePropertyBase *property,KPlayer *me);
+
+protected slots:
+ /**
+ * Called by KGameProperty only! Internal function!
+ **/
+ void sendProperty(int msgid, QDataStream& stream, bool* sent);
+ /**
+ * Called by KGameProperty only! Internal function!
+ **/
+ void emitSignal(KGamePropertyBase *me);
+
+
+private:
+ void init();
+
+private:
+ KGame *mGame;
+ bool mActive; // active player
+ KGameIOList mInputList;
+
+ // GameProperty // AB: I think we can't move them to KPlayerPrivate - inline
+ // makes sense here
+ KGamePropertyBool mAsyncInput; // async input allowed
+ KGamePropertyBool mMyTurn; // Is it my turn to play (only useful if not async)?
+ KGamePropertyInt mUserId; // a user defined id
+
+ KPlayerPrivate* d;
+};
+
+#endif
diff --git a/libkdegames/kgame/libkdegames.html b/libkdegames/kgame/libkdegames.html
new file mode 100644
index 00000000..66206c47
--- /dev/null
+++ b/libkdegames/kgame/libkdegames.html
@@ -0,0 +1,187 @@
+<html>
+ <head>
+ <title>Documentation for libkdegames</title>
+ <meta content="">
+ <style></style>
+ </head>
+ <body>
+ <H1>Documentation for the classes in libkdegames</H1>
+<!-------------------------------------------------------------------------------->
+ <H3>Design Principles</H3>
+ The library <em>kdegames</em> contains a collection of classes that can be used
+ to develop games using the KDE environment very easily. There are a few
+ principles that were used when developing the library:<P>
+
+ <UL>
+ <LI><b>usable for a big variety of games</b><br>
+ The class <em>KGame</em> provides many features that are needed in many games.
+ It can be used for board games, card games, maze games, simulation games, strategy games and
+ many more.<br>
+ It does not (yet) include special features for realtime games, but can be used there, too.
+ <LI><b>features one-player and multi-player games</b></br>
+ A game developed with this library can easily support any number of simultaneous players.
+ So use it for one-player games (like Tetris or KSame), for two-player games (like TicTacToe
+ or chess) or for games with an arbitrary number of players.
+ <LI><b>computer players can easily be developed</b><br>
+ The class <em>KPlayer</em> represents an abstract player in a game. This can be a
+ human player that gets the input from the mouse or the keyboard. Or it can be a computer
+ player that makes moves by random or with artificial intelligence. All this can be achieved
+ subclassing KPlayer.
+ <LI><b>support for network games transparently</b><br>
+ The class <em>KGame</em> contains lots of features for network game support. Developing
+ a network game using a TCP/IP connection is very easy this way. But the default
+ case is still the local game. So the user should not need to connect to the internet
+ or to select a game server to play a game.
+ <LI><b>support for turn based and for asynchronous games</b><br>
+ You can use this library for turn based games, when only one player can make a move at a time
+ (like most bord games or card games), but also for asynchronous games, when every player can
+ make a move any time (like many action games).
+ </UL>
+<!-------------------------------------------------------------------------------->
+ <H3>The central game class: <em>KGame</em></H3>
+
+ When you want to develop your own KDE game using the KDE games library, you most likely want
+ to use the <em>KGame</em> class. There are two possible ways to extend it to your own needs:
+ Create a subclass and overwrite the virtual methods, or simply create an instance of KGame
+ and connect to the appropriate signals.<P>
+
+ &lt;&lt;more code about KGame, an easy example&gt;&gt;
+
+<!-------------------------------------------------------------------------------->
+ <H3>Network games and related classes</H3>
+
+ One of the main principles in the design of <em>KGame</em> was to make network games possible
+ with a minimum of effort for the game developer.<P>
+
+ A network game is a game with usually several players, that are on different computers. These
+ computers are usually connected to the internet, and all the moves a player does are exchanged
+ over this network.<P>
+
+ The exchange of moves and other information is done using the class <em>KMessageServer</em>.
+ An object of this class is a server that waits for connections. Someone who wants to take part
+ in the game has to connect to this server - usually using an internet socket connection. He does
+ this by creating a <em>KMessageClient</em> object. This object connects to the message server.<P>
+
+ The transfer of data is realised by subclasses of the abstract class <em>KMessageIO</em>. One object
+ of this class is created on the client side, one on the server side. Different types of networks can
+ be supported by creating new subclasses of KMessageIO. There are already two subclasses of KMessageIO:
+ <em>KMessageSocket</em> uses a internet socket connection to transfer the data from the message client
+ to the message server or vice versa. <em>KMessageDirect</em> can be used if both the message server and
+ the message client are within the same process. The data blocks are copied directly to the other side,
+ so the transfer is faster and needs no network bandwidth.<P>
+
+ A typical network game situation could look like this:<P>
+
+ <IMG SRC="kmessageserver.png"><P>
+
+ Here, three KGame object (called message clients) are connected to the KMessageServer object. One
+ is in the same process, so it uses KMessageDirect. The other two use KMessageSocket, so an internet
+ socket connection is used. KGame doesn't talk directly to the message server, but uses a KMessageClient
+ object instead. One of the KMessageClient objects is the admin of the message server. This client has some
+ priviledges. It may e.g. kill the connection to other message clients, or limit the number of clients
+ that may connect.<P>
+
+ The KGame objects are by default all equal. So the usual approach will be that every KGame object will
+ store the complete status of the game, and any change or move will be broadcasted to the other KGame
+ objects, so that they all change the status in identical ways. Sometimes it may be necessary (or just
+ easier to implement) that one KGame object is a <em>game server</em> (i.e. he is repsonsible for everything,
+ he coordinates the complete game and stores the game status), whereas the other KGame objects are
+ only dumb stubs that are used to contact the <em>game server</em>. You can implement both approaches
+ using the message server structure. If you need to elect the KGame object that shall be
+ the game server, you may e.g. use the one that has the KMessageClient that is the admin of the message
+ server. (Of course this is only a suggestion, you can use other approaches.)<P>
+
+ The main principle when developing the message server/client structure was, that the message server
+ doesn't have <em>any</em> idea of the game and its rules that is played. The message server only forwards
+ messages from one message client to the others without interpreting or manipulating the data. So always
+ keep in mind that the message server is <em>not</em> a game server! It does not store any data about
+ the game status. It is only a server for network connections and message broadcasting, <em>not</em>
+ for game purposes. The reason for this principle is, that <em>any</em> game can be played using a
+ KMessageServer on any computer. The computer being the message server doesn't need to know anything
+ about the game that is played on it. So you don't have to install new versions of the game there. Only
+ the clients need to be game specific.<P>
+
+ Usually you don't need to create <em>KMessageServer</em> or <em>KMessageClient</em> objects in your game,
+ since <em>KGame</em> does this for you. There are three different scenarios fo network games that are
+ all supported in <em>KGame</em>:<P>
+
+ <b>Scenario 1: local game</b><P>
+
+ The local game should always be the default state a game should be in. To avoid having this scenario
+ as a special case, <em>KGame</em> automatically creates a KMessageServer object and a KMessageClient
+ object. So every change and every move is sent to the message server and is returned to the KGame
+ object before it is processed. Since the connection between the message client and the message server
+ uses KMessageDirect the data transfer is very fast and wont hurt in most cases.<P>
+
+ <IMG SRC="scenario0.png"><P>
+
+ This is the default situation right after creating the <em>KGame</em> object.<P>
+
+ <b>Scenario 2: network game, started by one player</b><P>
+
+ If one user is bored of playing alone, he can open his game for connections from the outside world.
+ He listens to a TCP/IP socket port (i.e. a number between 0 and 65535). Other players can create
+ KGame objects of their own and connect to this port. They need to know the IP address of that computer
+ and the port number. This situation will have this structure:
+
+ <IMG SRC="scenario1.png"><P>
+
+ The first player has to do something like:<P>
+
+ <PRE>
+ KGame *myGame = new KGame ();
+ // wait for connections on port 12345
+ myGame->offerConnections (12345);
+ </PRE>
+
+ And the other players have to do something like:<P>
+
+ <PRE>
+ KGame *myGame = new KGame ();
+ // connect to the message server
+ myGame->connectToServer ("theServer.theDomain.com", 12345);
+ </PRE>
+
+ This automatically removes the message server in these KGame objects and connects to the given
+ one instead.<P>
+
+ <b>Scenario 3: network game, using a stand alone message server</b><P>
+
+ Sometimes it is not possible to let the message server run on one of the players computer. If e.g. all
+ the players have their computer in a local network that uses masquerading to contact the internet,
+ other computers cannot connect to them since the computer doesn't have a IP address to the outside
+ world. Then the only way to play a network game is to have a standalone KMessageServer object on
+ another server computer (somthing like "games.kde.org" e.g.). Since the KMessageServer isn't game
+ specific at all, every game can be played using it. There doesn't have to be any special software
+ installed on that server computer, only the program creating a KMessageServer object.<P>
+
+ This scenario has some more advantages: The message server can be a well known meeting point to
+ start a game. This way one could play games against other players you never knew before. Furthermore
+ the game is stopped brutally when the program that contains the message server in scenario 2 is
+ quitted. (Migration of message servers is not yet implemented, but may be in the future.) Using a
+ stand alone message server, the players may enter and leave the game as they want.
+
+ <IMG SRC="scenario2.png"><P>
+
+ To create this scenario, a special KMessageServer program has to be started on the computer
+ that shall be the stand alone message server:<P>
+
+ <PRE>
+ % kmessageserver -port=12345
+ </PRE>
+
+ The other games that want to connect have to do this (supposed the stand alone message server
+ has the IP address "games.kde.org"):<P>
+
+ <PRE>
+ KGame *myGame = new KGame ();
+ // connect to the message server
+ myGame->connectToServer ("games.kde.org", 12345);
+ </PRE>
+
+
+
+
+<!-------------------------------------------------------------------------------->
+ </body>
+</html> \ No newline at end of file
diff --git a/libkdegames/kgame/messages.txt b/libkdegames/kgame/messages.txt
new file mode 100644
index 00000000..151196d5
--- /dev/null
+++ b/libkdegames/kgame/messages.txt
@@ -0,0 +1,93 @@
+Message formats of libkdegames:
+-------------------------------
+
+There are two different communication layers, using their own protocols:
+
+ - the message layer (KMessageIO, KMessageServer, KMessageClient)
+
+ This is used to send messages from one client (i.e. KGame object)
+ to an other one, to a group of other clients, or to all the clients
+ connected to the KMessageServer. The messages are arbitrary blocks
+ of data, of an arbitrary length.
+ This layer is an underlying protocol that isn't game specific at all.
+ You shouldn't need to know the message format of the packets. If you
+ want to extend the protocol, have a look into KMessageServer API
+ reference for a complete list of message types.
+
+ - the game layer (KGame, KGameIO, KPlayer)
+
+ This layer uses the message layer to send its specific message packets
+ between the objects of the game.
+ This layer contains the game specific messages (e.g. addPlayer, setupGame).
+ The rest of this file describes this layer.
+
+
+Game Layer Messages:
+--------------------
+
+ Application Cookie 16 Bit
+ Version 8 Bit
+ MsgId 16 Bit
+ SenderId 16 Bit
+ ReceiverId 16 Bit
+ Userdata
+
+The format of the messages is used internally and there is usually no reason why
+you have to know what it is used for. But as usually != always here are some
+comments on the format. Note that KGame is under development and the content of
+this file could be obsolete. Please result the sourcecode for up-to-date
+information.
+Application Cookie is used to identify the application. This prevents a
+chess game from talking to a poker game.
+Version is the version of KNetworkGame, sender and receiver must be of the same
+version.
+ library note: this could be a limitation, as KGame should be backward
+ compatible. Maybe change to version must be >= KNETWORKGAME or something less
+ restrictive
+MsgId specifies the kind of the message data (see below).
+SenderId is the id of the KGame/KPlayer object which sent the message, it is
+coded like this: the lower 10 bits specify a player and the upper bit
+represent the game id shifted by 10 bits. So we get
+Id=playerId | (gameId<<10);
+ReceiverId is the id of the receiver of the message. It can be either
+a player, a game or a broadcast. For a broadcast the Id is set to 0
+in the other cases the coding is as with the senderId
+Userdata is the data of the user (wow ;-))
+
+
+MsgId UserData
+---------------------------------------------------------
+IdMessage user defined
+
+IdSetupGame Q_INT32 isServer
+ Q_INT32 maxPlayers
+ Q_INT32 newid (id for the new game)
+ Q_INT32 cntR (virtual player nunmber)
+ Q_INT32 cntT (tagged player number)
+ TODO: Changed
+
+IdContinueSetup: TODO
+
+IdSendPlayer Q_INT32 omit how many tagged players for replacement
+ TODO: Changed
+
+IdGameSave Save(msg)->Load(msg)
+
+IdAddPlayer rtti
+ gameid() of the owner
+ player->Save(msg) -> player->Load(msg)
+
+IdRemovePlayer Q_INT16 playerid
+
+IdError Q_INT32 errorcode
+ QString errortext
+
+IdGameStatus Q_INT32 status
+
+IdPlayerProperty Q_INT16 propertyId
+ user defined -> the property
+
+IdGameProperty Q_INT16 propertyId
+ user defined -> the property
+
+IdPlayerInput user defined
diff --git a/libkdegames/kgame/scenario0.png b/libkdegames/kgame/scenario0.png
new file mode 100644
index 00000000..4de99b52
--- /dev/null
+++ b/libkdegames/kgame/scenario0.png
Binary files differ
diff --git a/libkdegames/kgame/scenario1.png b/libkdegames/kgame/scenario1.png
new file mode 100644
index 00000000..74af4f6f
--- /dev/null
+++ b/libkdegames/kgame/scenario1.png
Binary files differ
diff --git a/libkdegames/kgame/scenario2.png b/libkdegames/kgame/scenario2.png
new file mode 100644
index 00000000..14ea0a3c
--- /dev/null
+++ b/libkdegames/kgame/scenario2.png
Binary files differ