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 *******************************************************************/
24 #include "StreamSocket.h"
26 #define HANDLER_SIGNATURE(token) \
27 void StreamSocket::parse_##token() { \
28 QString elementName = name().toString(); \
29 Q_ASSERT_X(elementName == #token, \
30 (QString("StreamSocket::parse_") + #token + "()").toUtf8().constData(), \
31 ("called while parsing " + elementName).toUtf8().constData()); \
34 const QString
StreamSocket::_supportedProtocolVersion
= "1.0";
36 StreamSocket::StreamSocket(QTcpSocket
*socket
)
37 : QXmlStreamReader(socket
), QXmlStreamWriter(socket
)
39 Q_ASSERT_X(socket
!= 0, "StreamSocket::StreamSocket()", "socket must not be null");
42 _socket
->setParent(this);
45 connect(_socket
, SIGNAL(readyRead()), this, SLOT(parseData()));
46 connect(_socket
, SIGNAL(error(QAbstractSocket::SocketError
)), this, SLOT(handleError(QAbstractSocket::SocketError
)));
49 StreamSocket::~StreamSocket()
55 void StreamSocket::changeState(ProtocolState state
)
60 StreamSocket::ProtocolState
StreamSocket::state() const
65 QString
StreamSocket::stateString() const
68 case Unconnected
: return "Unconnected";
69 case Listening
: return "Listening";
70 case Connecting
: return "Connecting";
71 case Connected
: return "Connected";
72 case OpeningStream
: return "OpeningStream";
73 case FullyOpened
: return "FullyOpened";
74 case Idle
: return "Idle";
75 case AwaitingJoinRequest
: return "AwaitingJoinRequest";
76 case AwaitingJoinAnswer
: return "AwaitingJoinAnswer";
77 case AwaitingPlayers
: return "AwaitingPlayers";
78 case AwaitingGameStart
: return "AwaitingGameStart";
79 case Playing
: return "Playing";
80 case AwaitingMove
: return "AwaitingMove";
81 case Closing
: return "Closing";
82 case Closed
: return "Closed";
83 default: return "Unknown";
87 Point
StreamSocket::takeFirstMove()
89 if (_buffer
.isEmpty()) {
92 return _buffer
.takeFirst().point();
96 void StreamSocket::sendChatMessage(QString msg
)
101 if (_localPlayerName
.isEmpty()) {
102 LOG("trying to send <chatMessage> with empty sender");
106 writeStartElement("chatMessage");
107 writeAttribute("from", _localPlayerName
);
108 writeCharacters(msg
);
112 void StreamSocket::sendMove(Move move
)
117 void StreamSocket::openStream()
119 writeStartDocument();
120 writeDefaultNamespace("http://www.itworks.org/GoMoku3D/NetworkProtocol");
121 writeStartElement("stream");
122 writeAttribute("version", _supportedProtocolVersion
);
125 void StreamSocket::closeStream()
129 _socket
->disconnectFromHost();
133 void StreamSocket::handleError(QAbstractSocket::SocketError error
)
139 void StreamSocket::serialize(Move m
)
141 writeStartElement("move");
142 writeTextElement("player", QString::number(m
.playerId()));
143 writeStartElement("point");
144 writeTextElement("x", QString::number(m
.point().x()));
145 writeTextElement("y", QString::number(m
.point().y()));
146 writeTextElement("z", QString::number(m
.point().z()));
151 bool StreamSocket::parse(QString elementName
)
153 if (elementName
.isEmpty()) {
157 QString methodName
= "parse_" + elementName
;
158 if (QMetaObject::invokeMethod(this, methodName
.toUtf8().constData(), Qt::DirectConnection
)) {
161 WARN(tr("unknown protocol message %1").arg("<" + elementName
+ ">"));
166 void StreamSocket::parseData()
169 if (parse(_currentHandler
)) {
173 QXmlStreamReader::TokenType token
= readNext();
177 parse(name().toString());
180 LOG("unable to handle unexpected token");
189 BEGIN_TERMINAL_HANDLER(chatMessage
)
190 QString from
= attributes().value("from").toString();
192 QString msg
= readElementText();
193 if (from
.isEmpty() && msg
.isEmpty()) {
194 LOG("dropping empty <chatMessage>");
195 } else if (from
.isEmpty()) {
196 LOG("dropping <chatMessage> without sender");
197 } else if (msg
.isEmpty()) {
198 LOG("dropping <chatMessage> with empty message");
199 } else if (state() == Playing
|| state() == AwaitingMove
) {
200 emit
receivedChatMessage("<" + from
+ "> " + msg
);
202 LOG("dropping <chatMessage> (state = " + stateString() + ")");
207 BEGIN_NONTERMINAL_HANDLER(move
)
208 Move
m(property("move/player").toInt(),
209 property("move/point").value
<Point
>());
210 if (state() == AwaitingMove
) {
211 emit
receivedMove(m
);
212 } else if (state() == Playing
) {
215 LOG("dropping <move> (state = " + stateString() + ")");
217 END_NONTERMINAL_HANDLER(move
)
219 BEGIN_TERMINAL_HANDLER(player
)
221 int id
= readElementText().toInt(&ok
);
223 WARN(tr("invalid <player> value in <move> : %1").arg(readElementText()));
226 setProperty("move/player", QVariant(id
));
227 RESTORE_HANDLER("move")
230 BEGIN_NONTERMINAL_HANDLER(point
)
231 Point
p(property("move/point/x").toInt(),
232 property("move/point/y").toInt(),
233 property("move/point/z").toInt());
234 setProperty("move/point", QVariant::fromValue(p
));
235 END_NONTERMINAL_HANDLER(point
) //FIXME: e' giusto cosi' o devo ripristinare "move" come handler?
237 BEGIN_TERMINAL_HANDLER(x
)
239 int value
= readElementText().toInt(&ok
);
241 WARN(tr("invalid <x> value in <point> : %1").arg(readElementText()));
244 setProperty("move/point/x", QVariant(value
));
245 RESTORE_HANDLER("point")
248 BEGIN_TERMINAL_HANDLER(y
)
250 int value
= readElementText().toInt(&ok
);
252 WARN(tr("invalid <y> value in <point> : %1").arg(readElementText()));
255 setProperty("move/point/y", QVariant(value
));
256 RESTORE_HANDLER("point")
259 BEGIN_TERMINAL_HANDLER(z
)
261 int value
= readElementText().toInt(&ok
);
263 WARN(tr("invalid <z> value in <point> : %1").arg(readElementText()));
266 setProperty("move/point/z", QVariant(value
));
267 RESTORE_HANDLER("point")
270 HANDLER_SIGNATURE(startGame
)
273 elementName
= name().toString();
274 if (isEndElement()) {
275 if (elementName
== "startGame" && state() == AwaitingGameStart
) {
278 END_NONTERMINAL_HANDLER(startGame
)