1 /********************************************************************
3 * Copyright (C) 2008 Davide Pesavento
5 * This file is part of GoMoku3D.
7 * GoMoku3D is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * GoMoku3D is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with GoMoku3D. If not, see <http://www.gnu.org/licenses/>.
20 *******************************************************************/
22 #include <QMutexLocker>
24 #include "GameServer.h"
25 #include "ServerSettings.h"
26 #include "SyncSharedCondition.h"
28 #include "ChatWidget.h"
29 #include "HistoryModel.h"
31 GameServer::GameServer(QWidget
*gui
, HistoryModel
*history
) : Network(gui
)
33 Q_ASSERT_X(gui
!= 0, "GameClient::GameClient()", "gui must not be null");
34 Q_ASSERT_X(history
!= 0, "GameClient::GameClient()", "history must not be null");
36 _listener
= new QTcpServer(this);
37 _gameInProgress
= false;
41 _localPlayerName
= "";
42 _settings
= new ServerSettings();
43 _numberOfPlayers
= _settings
->numberOfPlayers();
44 QList
<PlayerInfo
> players
= _settings
->playersInfo();
45 for (int i
= 0; i
< _numberOfPlayers
; i
++) {
46 _remotePlayers
.append(0);
47 if (players
.at(i
).type() == "H") {
49 _localPlayerName
= players
.at(i
).name();
50 _names
.append(_localPlayerName
);
52 } else if (players
.at(i
).type() == "A") {
54 _names
.append(players
.at(i
).name());
60 if (_localPlayerName
.isEmpty()) {
61 _localPlayerName
= _settings
->myName();
64 // check that lists have been correctly populated
65 Q_ASSERT(_remotePlayers
.size() == _numberOfPlayers
);
66 Q_ASSERT(_spectators
.size() == 0);
67 Q_ASSERT(_pendingConnections
.size() == 0);
68 Q_ASSERT(_names
.size() == _numberOfPlayers
);
70 connect(this, SIGNAL(error(QString
)), gui
, SLOT(networkError(QString
)));
71 connect(this, SIGNAL(playerJoined(int, QString
, QString
)), gui
, SLOT(addPlayer(int, QString
, QString
)));
72 connect(this, SIGNAL(playerLeft(int)), gui
, SLOT(removePlayer(int)));
73 connect(this, SIGNAL(startGame()), gui
, SLOT(gameStarted()));
75 // start listening for incoming connections
76 connect(_listener
, SIGNAL(newConnection()), this, SLOT(handleIncomingConnection()));
77 if (!_listener
->listen(QHostAddress::Any
, _settings
->serverPort())) {
78 emit
error(_listener
->errorString());
84 GameServer::~GameServer()
87 foreach (s
, _remotePlayers
) {
93 qDeleteAll(_spectators
);
95 qDeleteAll(_pendingConnections
);
96 _pendingConnections
.clear();
102 void GameServer::broadcastChatMessage(QString sender
, QString message
)
104 if (sender
.isEmpty()) {
105 sender
= _localPlayerName
;
107 _chat
->displayMessage(sender
, message
);
109 ServerSocket
*socket
;
110 foreach (socket
, _remotePlayers
) {
112 socket
->sendChatMessage(sender
, message
);
115 foreach (socket
, _spectators
) {
116 socket
->sendChatMessage(sender
, message
);
120 void GameServer::broadcastMove(Move m
)
122 bool broadcast
= false;
124 // a client sent the move
125 ServerSocket
*s
= qobject_cast
<ServerSocket
*>(sender());
127 QMutexLocker
lock(SyncSharedCondition::instance());
128 SyncSharedCondition::instance()->notifyMove(m
.point());
132 // GameLoop sent the move
133 GameLoop
*gl
= qobject_cast
<GameLoop
*>(sender());
135 QString playerType
= _settings
->playersInfo().at(m
.playerId()).type();
136 if (playerType
== "H" || playerType
== "A") {
142 // send the move to remote players and spectators
143 ServerSocket
*socket
;
144 foreach(socket
, _remotePlayers
) {
145 if (socket
&& socket
!= s
) {
149 foreach(socket
, _spectators
) {
155 Point
GameServer::requestMove()
157 QMutexLocker
lock(&_mutex
);
158 Move m
= _remotePlayers
.at(_turn
)->takeFirstMove();
159 if (m
.point().isNull()) {
160 _remotePlayers
.at(_turn
)->changeState(StreamSocket::AwaitingMove
);
167 void GameServer::setupChat(ChatWidget
*widget
)
174 connect(_chat
, SIGNAL(textEntered(QString
, QString
)), this, SLOT(broadcastChatMessage(QString
, QString
)));
177 foreach (s
, _remotePlayers
) {
179 connect(s
, SIGNAL(receivedChatMessage(QString
, QString
)), this, SLOT(broadcastChatMessage(QString
, QString
)));
182 foreach (s
, _spectators
) {
183 connect(s
, SIGNAL(receivedChatMessage(QString
, QString
)), this, SLOT(broadcastChatMessage(QString
, QString
)));
187 void GameServer::setupGameLoop(GameLoop
*gameLoop
)
189 connect(gameLoop
, SIGNAL(turn(int)), this, SLOT(setTurn(int)), Qt::DirectConnection
);
190 connect(gameLoop
, SIGNAL(moved(Move
)), this, SLOT(broadcastMove(Move
)));
193 void GameServer::bootstrapClient(StreamSocket
*socket
)
195 ServerSocket
*client
= qobject_cast
<ServerSocket
*>(socket
);
201 client
->sendGameSettings(_settings
->difficulty1(), _settings
->difficulty2(), _numberOfPlayers
,
202 _settings
->timerDuration(), _gameInProgress
);
204 // send a list of players that have already joined
205 for (int i
= 0; i
< _numberOfPlayers
; i
++) {
206 if (!_names
.at(i
).isEmpty()) {
207 QString type
= _settings
->playersInfo().at(i
).type();
208 client
->sendPlayerJoined(i
, _names
.at(i
), (type
== "H" ? "R" : type
));
213 void GameServer::checkStartGame()
215 // check to see if all players have joined
217 for (; i
< _numberOfPlayers
&& !_names
.at(i
).isEmpty(); i
++) {}
218 if (i
== _numberOfPlayers
) {
220 _gameInProgress
= true;
222 foreach (s
, _remotePlayers
) {
227 foreach (s
, _spectators
) {
234 void GameServer::handleError(QString errorMessage
)
236 ServerSocket
*socket
= qobject_cast
<ServerSocket
*>(sender());
241 QLinkedList
<ServerSocket
*>::iterator it
= qFind(_pendingConnections
.begin(), _pendingConnections
.end(), socket
);
242 if (it
!= _pendingConnections
.end()) {
243 _pendingConnections
.erase(it
);
244 socket
->deleteLater();
246 int i
= _spectators
.indexOf(socket
);
248 _spectators
.removeAt(i
);
249 _names
.removeAt(i
+ _numberOfPlayers
);
250 socket
->deleteLater();
252 i
= _remotePlayers
.indexOf(socket
);
254 _remotePlayers
.removeAt(i
);
256 if (_gameInProgress
) {
257 emit
error(errorMessage
);
261 foreach (s
, _remotePlayers
) {
262 if (s
&& s
!= socket
) {
263 s
->sendPlayerLeft(i
);
266 foreach (s
, _spectators
) {
267 s
->sendPlayerLeft(i
);
270 socket
->deleteLater();
274 void GameServer::handleIncomingConnection()
276 while (_listener
->hasPendingConnections()) {
277 ServerSocket
*s
= new ServerSocket(_listener
->nextPendingConnection());
278 connect(s
, SIGNAL(protocolError(QString
)), this, SLOT(handleError(QString
)));
279 connect(s
, SIGNAL(handshakeCompleted(StreamSocket
*)), this, SLOT(bootstrapClient(StreamSocket
*)));
280 connect(s
, SIGNAL(joinRequested(QString
, QString
)), this, SLOT(handleJoinRequest(QString
, QString
)));
281 _pendingConnections
.append(s
);
285 void GameServer::handleJoinRequest(QString mode
, QString name
)
287 ServerSocket
*requester
= qobject_cast
<ServerSocket
*>(sender());
292 if (name
== _localPlayerName
|| _names
.contains(name
)) {
293 requester
->refuseJoin(2);
294 } else if (mode
== "player") {
295 if (_gameInProgress
) {
296 requester
->refuseJoin(1);
298 // find first free id
300 for (int i
= 0; i
< _numberOfPlayers
&& freePos
== -1; i
++) {
301 if (_names
.at(i
).isEmpty()) {
306 // accept player, moving its socket from _pendingConnections to _remotePlayers
307 _pendingConnections
.removeAll(requester
);
308 _remotePlayers
[freePos
] = requester
;
309 _names
[freePos
] = name
;
310 connect(requester
, SIGNAL(receivedMove(Move
)), this, SLOT(broadcastMove(Move
)));
312 connect(requester
, SIGNAL(receivedChatMessage(QString
, QString
)), this, SLOT(broadcastChatMessage(QString
, QString
)));
314 requester
->acceptJoin(freePos
);
315 emit
playerJoined(freePos
, name
, "R");
317 foreach (s
, _remotePlayers
) {
318 if (s
&& s
!= requester
) {
319 s
->sendPlayerJoined(freePos
, name
, "R");
322 foreach (s
, _spectators
) {
323 s
->sendPlayerJoined(freePos
, name
, "R");
327 qWarning() << "GameServer::handleJoinRequest() : all players seem to have joined but game hasn't started!";
330 } else if (mode
== "spectator") {
331 // accept new spectator
332 if (_gameInProgress
) {
333 requester
->sendHistory(_history
->moveList());
335 _pendingConnections
.removeAll(requester
);
336 _spectators
.append(requester
);
339 connect(requester
, SIGNAL(receivedChatMessage(QString
, QString
)), this, SLOT(broadcastChatMessage(QString
, QString
)));
341 requester
->acceptJoin(-1);
342 if (_gameInProgress
) {
343 requester
->sendStartGame();
346 qDebug() << "GameServer::handleJoinRequest() : invalid mode value" << mode
;
350 void GameServer::setTurn(int playerId
)
352 Q_ASSERT_X(playerId
>= 0 && playerId
< _numberOfPlayers
, "GameServer::setTurn()", "invalid id");
353 Q_ASSERT(_gameInProgress
== true);
355 QMutexLocker
lock(&_mutex
);