Finished implementing error handling in GameServer.
[GoMoku3D.git] / src / network / GameServer.cpp
blobb6a8870cc8901f7daa334fa71b9b052738130889
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"
27 #include "GameLoop.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;
38 _history = history;
39 _chat = 0;
40 _turn = -1;
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") {
48 // Human player
49 _localPlayerName = players.at(i).name();
50 _names.append(_localPlayerName);
51 _localPlayer = i;
52 } else if (players.at(i).type() == "A") {
53 // AI player
54 _names.append(players.at(i).name());
55 } else {
56 // Remote player
57 _names.append("");
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());
81 checkStartGame();
84 GameServer::~GameServer()
86 ServerSocket *s;
87 foreach (s, _remotePlayers) {
88 if (s) {
89 delete s;
93 qDeleteAll(_spectators);
94 _spectators.clear();
95 qDeleteAll(_pendingConnections);
96 _pendingConnections.clear();
98 delete _listener;
99 delete _settings;
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) {
111 if (socket) {
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());
126 if (s) {
127 QMutexLocker lock(SyncSharedCondition::instance());
128 SyncSharedCondition::instance()->notifyMove(m.point());
129 broadcast = true;
132 // GameLoop sent the move
133 GameLoop *gl = qobject_cast<GameLoop*>(sender());
134 if (gl) {
135 QString playerType = _settings->playersInfo().at(m.playerId()).type();
136 if (playerType == "H" || playerType == "A") {
137 broadcast = true;
141 if (broadcast) {
142 // send the move to remote players and spectators
143 ServerSocket *socket;
144 foreach(socket, _remotePlayers) {
145 if (socket && socket != s) {
146 socket->sendMove(m);
149 foreach(socket, _spectators) {
150 socket->sendMove(m);
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);
161 } else {
162 broadcastMove(m);
164 return m.point();
167 void GameServer::setupChat(ChatWidget *widget)
169 if (!widget) {
170 return;
173 _chat = widget;
174 connect(_chat, SIGNAL(textEntered(QString, QString)), this, SLOT(broadcastChatMessage(QString, QString)));
176 ServerSocket *s;
177 foreach (s, _remotePlayers) {
178 if (s) {
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);
196 if (!client) {
197 return;
200 // send settings
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
216 int i = 0;
217 for (; i < _numberOfPlayers && !_names.at(i).isEmpty(); i++) {}
218 if (i == _numberOfPlayers) {
219 // start the game
220 _gameInProgress = true;
221 ServerSocket *s;
222 foreach (s, _remotePlayers) {
223 if (s) {
224 s->sendStartGame();
227 foreach (s, _spectators) {
228 s->sendStartGame();
230 emit startGame();
234 void GameServer::handleError(QString errorMessage)
236 ServerSocket *socket = qobject_cast<ServerSocket*>(sender());
237 if (!socket) {
238 return;
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);
247 if (i >= 0) {
248 _spectators.removeAt(i);
249 _names.removeAt(i + _numberOfPlayers);
250 socket->deleteLater();
252 i = _remotePlayers.indexOf(socket);
253 if (i >= 0) {
254 _remotePlayers.removeAt(i);
255 _names[i] = "";
256 if (_gameInProgress) {
257 emit error(errorMessage);
258 } else {
259 emit playerLeft(i);
260 ServerSocket *s;
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());
288 if (!requester) {
289 return;
292 if (name == _localPlayerName || _names.contains(name)) {
293 requester->refuseJoin(2);
294 } else if (mode == "player") {
295 if (_gameInProgress) {
296 requester->refuseJoin(1);
297 } else {
298 // find first free id
299 int freePos = -1;
300 for (int i = 0; i < _numberOfPlayers && freePos == -1; i++) {
301 if (_names.at(i).isEmpty()) {
302 freePos = i;
305 if (freePos >= 0) {
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)));
311 if (_chat) {
312 connect(requester, SIGNAL(receivedChatMessage(QString, QString)), this, SLOT(broadcastChatMessage(QString, QString)));
314 requester->acceptJoin(freePos);
315 emit playerJoined(freePos, name, "R");
316 ServerSocket *s;
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");
325 checkStartGame();
326 } else {
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);
337 _names.append(name);
338 if (_chat) {
339 connect(requester, SIGNAL(receivedChatMessage(QString, QString)), this, SLOT(broadcastChatMessage(QString, QString)));
341 requester->acceptJoin(-1);
342 if (_gameInProgress) {
343 requester->sendStartGame();
345 } else {
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);
356 _turn = playerId;