Other updates to the Network component.
[GoMoku3D.git] / src / network / StreamSocket.cpp
blobc6d20738d7ea749856733351c3fcd5cf4e751e4d
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 <QVariant>
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()); \
32 SET_HANDLER(#token)
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");
41 _socket = socket;
42 _socket->setParent(this);
43 _state = Unconnected;
45 connect(_socket, SIGNAL(readyRead()), this, SLOT(parseData()));
46 connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(handleError(QAbstractSocket::SocketError)));
49 StreamSocket::~StreamSocket()
51 closeStream();
52 delete _socket;
55 void StreamSocket::changeState(ProtocolState state)
57 _state = state;
60 StreamSocket::ProtocolState StreamSocket::state() const
62 return _state;
65 QString StreamSocket::stateString() const
67 switch(_state) {
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()) {
90 return Point();
91 } else {
92 return _buffer.takeFirst().point();
96 void StreamSocket::sendChatMessage(QString msg)
98 if (msg.isEmpty()) {
99 return;
101 if (_localPlayerName.isEmpty()) {
102 LOG("trying to send <chatMessage> with empty sender");
103 return;
106 writeStartElement("chatMessage");
107 writeAttribute("from", _localPlayerName);
108 writeCharacters(msg);
109 writeEndElement();
112 void StreamSocket::sendMove(Move move)
114 serialize(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()
127 writeEndElement();
128 writeEndDocument();
129 _socket->disconnectFromHost();
130 changeState(Closed);
133 void StreamSocket::handleError(QAbstractSocket::SocketError error)
135 // TODO
136 Q_UNUSED(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()));
147 writeEndElement();
148 writeEndElement();
151 bool StreamSocket::parse(QString elementName)
153 if (elementName.isEmpty()) {
154 return false;
157 QString methodName = "parse_" + elementName;
158 if (QMetaObject::invokeMethod(this, methodName.toUtf8().constData(), Qt::DirectConnection)) {
159 return true;
160 } else {
161 WARN(tr("unknown protocol message %1").arg("<" + elementName + ">"));
162 return false;
166 void StreamSocket::parseData()
168 while (!atEnd()) {
169 if (parse(_currentHandler)) {
170 continue;
173 QXmlStreamReader::TokenType token = readNext();
175 switch (token) {
176 case StartElement:
177 parse(name().toString());
178 break;
179 default:
180 LOG("unable to handle unexpected token");
184 if (hasError()) {
185 // TODO
189 BEGIN_TERMINAL_HANDLER(chatMessage)
190 QString from = attributes().value("from").toString();
191 if (atEnd()) return;
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);
201 } else {
202 LOG("dropping <chatMessage> (state = " + stateString() + ")");
204 RESTORE_HANDLER("")
205 END_TERMINAL_HANDLER
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) {
213 _buffer.append(m);
214 } else {
215 LOG("dropping <move> (state = " + stateString() + ")");
217 END_NONTERMINAL_HANDLER(move)
219 BEGIN_TERMINAL_HANDLER(player)
220 bool ok;
221 int id = readElementText().toInt(&ok);
222 if (!ok) {
223 WARN(tr("invalid <player> value in <move> : %1").arg(readElementText()));
224 id = -1;
226 setProperty("move/player", QVariant(id));
227 RESTORE_HANDLER("move")
228 END_TERMINAL_HANDLER
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)
238 bool ok;
239 int value = readElementText().toInt(&ok);
240 if (!ok) {
241 WARN(tr("invalid <x> value in <point> : %1").arg(readElementText()));
242 value = -1;
244 setProperty("move/point/x", QVariant(value));
245 RESTORE_HANDLER("point")
246 END_TERMINAL_HANDLER
248 BEGIN_TERMINAL_HANDLER(y)
249 bool ok;
250 int value = readElementText().toInt(&ok);
251 if (!ok) {
252 WARN(tr("invalid <y> value in <point> : %1").arg(readElementText()));
253 value = -1;
255 setProperty("move/point/y", QVariant(value));
256 RESTORE_HANDLER("point")
257 END_TERMINAL_HANDLER
259 BEGIN_TERMINAL_HANDLER(z)
260 bool ok;
261 int value = readElementText().toInt(&ok);
262 if (!ok) {
263 WARN(tr("invalid <z> value in <point> : %1").arg(readElementText()));
264 value = -1;
266 setProperty("move/point/z", QVariant(value));
267 RESTORE_HANDLER("point")
268 END_TERMINAL_HANDLER
270 HANDLER_SIGNATURE(startGame)
271 while (!atEnd()) {
272 readNext();
273 elementName = name().toString();
274 if (isEndElement()) {
275 if (elementName == "startGame" && state() == AwaitingGameStart) {
276 emit startGame();
278 END_NONTERMINAL_HANDLER(startGame)