1 /***************************************************************************
2 **
3 ** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 ** * Redistributions of source code must retain the above copyright
25 ** notice, this list of conditions and the following disclaimer.
26 ** * Redistributions in binary form must reproduce the above copyright
27 ** notice, this list of conditions and the following disclaimer in
28 ** the documentation and/or other materials provided with the
29 ** distribution.
30 ** * Neither the name of The Qt Company Ltd nor the names of its
31 ** contributors may be used to endorse or promote products derived
32 ** from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50
51 #include "pingpong.h"
52 #include <QDebug>
53 #ifdef Q_OS_ANDROID
54 #include <QtAndroid>
55 #endif
56
PingPong()57 PingPong::PingPong():
58 m_serverInfo(0), socket(0), discoveryAgent(0), interval(5), m_resultLeft(0), m_resultRight(0),
59 m_showDialog(false), m_role(0), m_proportionX(0), m_proportionY(0), m_serviceFound(false)
60 {
61 m_timer = new QTimer(this);
62 connect(m_timer, &QTimer::timeout, this, &PingPong::update);
63 }
64
~PingPong()65 PingPong::~PingPong()
66 {
67 delete m_timer;
68 delete m_serverInfo;
69 delete socket;
70 delete discoveryAgent;
71 }
72
startGame()73 void PingPong::startGame()
74 {
75 m_showDialog = false;
76 emit showDialogChanged();
77 //! [Start the game]
78 if (m_role == 1)
79 updateDirection();
80
81 m_timer->start(50);
82 //! [Start the game]
83 }
84
update()85 void PingPong::update()
86 {
87 QByteArray size;
88 // Server is only updating the coordinates
89 //! [Updating coordinates]
90 if (m_role == 1) {
91 checkBoundaries();
92 m_ballPreviousX = m_ballX;
93 m_ballPreviousY = m_ballY;
94 m_ballY = m_direction*(m_ballX+interval) - m_direction*m_ballX + m_ballY;
95 m_ballX = m_ballX + interval;
96
97 size.setNum(m_ballX);
98 size.append(' ');
99 QByteArray size1;
100 size1.setNum(m_ballY);
101 size.append(size1);
102 size.append(' ');
103 size1.setNum(m_leftBlockY);
104 size.append(size1);
105 size.append(" \n");
106 socket->write(size.constData());
107 emit ballChanged();
108 }
109 else if (m_role == 2) {
110 size.setNum(m_rightBlockY);
111 size.append(" \n");
112 socket->write(size.constData());
113 }
114 //! [Updating coordinates]
115 }
116
117
118
setSize(const float & x,const float & y)119 void PingPong::setSize(const float &x, const float &y)
120 {
121 m_boardWidth = x;
122 m_boardHeight = y;
123 m_targetX = m_boardWidth;
124 m_targetY = m_boardHeight/2;
125 m_ballPreviousX = m_ballX = m_boardWidth/2;
126 m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
127 emit ballChanged();
128 }
129
updateBall(const float & bX,const float & bY)130 void PingPong::updateBall(const float &bX, const float &bY)
131 {
132 m_ballX = bX;
133 m_ballY = bY;
134 }
135
updateLeftBlock(const float & lY)136 void PingPong::updateLeftBlock(const float &lY)
137 {
138 m_leftBlockY = lY;
139 }
140
updateRightBlock(const float & rY)141 void PingPong::updateRightBlock(const float &rY)
142 {
143 m_rightBlockY = rY;
144 }
145
checkBoundaries()146 void PingPong::checkBoundaries()
147 {
148 float ballWidth = m_boardWidth/54;
149 float blockSize = m_boardWidth/27;
150 float blockHeight = m_boardHeight/5;
151 //! [Checking the boundaries]
152 if (((m_ballX + ballWidth) > (m_boardWidth - blockSize)) && ((m_ballY + ballWidth) < (m_rightBlockY + blockHeight))
153 && (m_ballY > m_rightBlockY)) {
154 m_targetY = 2 * m_ballY - m_ballPreviousY;
155 m_targetX = m_ballPreviousX;
156 interval = -5;
157 updateDirection();
158 }
159 else if ((m_ballX < blockSize) && ((m_ballY + ballWidth) < (m_leftBlockY + blockHeight))
160 && (m_ballY > m_leftBlockY)) {
161 m_targetY = 2 * m_ballY - m_ballPreviousY;
162 m_targetX = m_ballPreviousX;
163 interval = 5;
164 updateDirection();
165 }
166 else if (m_ballY < 0 || (m_ballY + ballWidth > m_boardHeight)) {
167 m_targetY = m_ballPreviousY;
168 m_targetX = m_ballX + interval;
169 updateDirection();
170 }
171 //! [Checking the boundaries]
172 else if ((m_ballX + ballWidth) > m_boardWidth) {
173 m_resultLeft++;
174 m_targetX = m_boardWidth;
175 m_targetY = m_boardHeight/2;
176 m_ballPreviousX = m_ballX = m_boardWidth/2;
177 m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
178
179 updateDirection();
180 checkResult();
181 QByteArray result;
182 result.append("result ");
183 QByteArray res;
184 res.setNum(m_resultLeft);
185 result.append(res);
186 result.append(' ');
187 res.setNum(m_resultRight);
188 result.append(res);
189 result.append(" \n");
190 socket->write(result);
191 qDebug() << result;
192 emit resultChanged();
193 }
194 else if (m_ballX < 0) {
195 m_resultRight++;
196 m_targetX = 0;
197 m_targetY = m_boardHeight/2;
198 m_ballPreviousX = m_ballX = m_boardWidth/2;
199 m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
200 updateDirection();
201 checkResult();
202 QByteArray result;
203 result.append("result ");
204 QByteArray res;
205 res.setNum(m_resultLeft);
206 result.append(res);
207 result.append(' ');
208 res.setNum(m_resultRight);
209 result.append(res);
210 result.append(" \n");
211 socket->write(result);
212 emit resultChanged();
213 }
214 }
215
checkResult()216 void PingPong::checkResult()
217 {
218 if (m_resultRight == 10 && m_role == 2) {
219 setMessage("Game over. You win!");
220 m_timer->stop();
221 }
222 else if (m_resultRight == 10 && m_role == 1) {
223 setMessage("Game over. You lose!");
224 m_timer->stop();
225 }
226 else if (m_resultLeft == 10 && m_role == 1) {
227 setMessage("Game over. You win!");
228 m_timer->stop();
229 }
230 else if (m_resultLeft == 10 && m_role == 2) {
231 setMessage("Game over. You lose!");
232 m_timer->stop();
233 }
234 }
235
updateDirection()236 void PingPong::updateDirection()
237 {
238 m_direction = (m_targetY - m_ballY)/(m_targetX - m_ballX);
239 }
240
startServer()241 void PingPong::startServer()
242 {
243 setMessage(QStringLiteral("Starting the server"));
244 //! [Starting the server]
245 m_serverInfo = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this);
246 connect(m_serverInfo, &QBluetoothServer::newConnection,
247 this, &PingPong::clientConnected);
248 connect(m_serverInfo, QOverload<QBluetoothServer::Error>::of(&QBluetoothServer::error),
249 this, &PingPong::serverError);
250 const QBluetoothUuid uuid(serviceUuid);
251
252 m_serverInfo->listen(uuid, QStringLiteral("PingPong server"));
253 //! [Starting the server]
254 setMessage(QStringLiteral("Server started, waiting for the client. You are the left player."));
255 // m_role is set to 1 if it is a server
256 m_role = 1;
257 emit roleChanged();
258 }
259
startClient()260 void PingPong::startClient()
261 {
262 //! [Searching for the service]
263 discoveryAgent = new QBluetoothServiceDiscoveryAgent(QBluetoothAddress());
264
265 connect(discoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered,
266 this, &PingPong::addService);
267 connect(discoveryAgent, &QBluetoothServiceDiscoveryAgent::finished,
268 this, &PingPong::done);
269 connect(discoveryAgent, QOverload<QBluetoothServiceDiscoveryAgent::Error>::of(&QBluetoothServiceDiscoveryAgent::error),
270 this, &PingPong::serviceScanError);
271 #ifdef Q_OS_ANDROID //see QTBUG-61392
272 if (QtAndroid::androidSdkVersion() >= 23)
273 discoveryAgent->setUuidFilter(QBluetoothUuid(androidUuid));
274 else
275 discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid));
276 #else
277 discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid));
278 #endif
279 discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);
280
281 //! [Searching for the service]
282 setMessage(QStringLiteral("Starting server discovery. You are the right player"));
283 // m_role is set to 2 if it is a client
284 m_role = 2;
285 emit roleChanged();
286 }
287
clientConnected()288 void PingPong::clientConnected()
289 {
290 //! [Initiating server socket]
291 if (!m_serverInfo->hasPendingConnections()) {
292 setMessage("FAIL: expected pending server connection");
293 return;
294 }
295 socket = m_serverInfo->nextPendingConnection();
296 if (!socket)
297 return;
298 socket->setParent(this);
299 connect(socket, &QBluetoothSocket::readyRead,
300 this, &PingPong::readSocket);
301 connect(socket, &QBluetoothSocket::disconnected,
302 this, &PingPong::clientDisconnected);
303 connect(socket, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::error),
304 this, &PingPong::socketError);
305
306 //! [Initiating server socket]
307 setMessage(QStringLiteral("Client connected."));
308
309 QByteArray size;
310 size.setNum(m_boardWidth);
311 size.append(' ');
312 QByteArray size1;
313 size1.setNum(m_boardHeight);
314 size.append(size1);
315 size.append(" \n");
316 socket->write(size.constData());
317
318 }
319
clientDisconnected()320 void PingPong::clientDisconnected()
321 {
322 setMessage(QStringLiteral("Client disconnected"));
323 m_timer->stop();
324 }
325
socketError(QBluetoothSocket::SocketError error)326 void PingPong::socketError(QBluetoothSocket::SocketError error)
327 {
328 Q_UNUSED(error);
329 m_timer->stop();
330 }
331
serverError(QBluetoothServer::Error error)332 void PingPong::serverError(QBluetoothServer::Error error)
333 {
334 Q_UNUSED(error);
335 m_timer->stop();
336 }
337
done()338 void PingPong::done()
339 {
340 qDebug() << "Service scan done";
341 if (!m_serviceFound)
342 setMessage("PingPong service not found");
343 }
344
addService(const QBluetoothServiceInfo & service)345 void PingPong::addService(const QBluetoothServiceInfo &service)
346 {
347 setMessage("Service found. Setting parameters...");
348 //! [Connecting the socket]
349 socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
350 socket->connectToService(service);
351
352 connect(socket, &QBluetoothSocket::readyRead, this, &PingPong::readSocket);
353 connect(socket, &QBluetoothSocket::connected, this, &PingPong::serverConnected);
354 connect(socket, &QBluetoothSocket::disconnected, this, &PingPong::serverDisconnected);
355 //! [Connecting the socket]
356 m_serviceFound = true;
357 }
358
serviceScanError(QBluetoothServiceDiscoveryAgent::Error error)359 void PingPong::serviceScanError(QBluetoothServiceDiscoveryAgent::Error error)
360 {
361 setMessage(QStringLiteral("Scanning error") + error);
362 }
363
showDialog() const364 bool PingPong::showDialog() const
365 {
366 return m_showDialog;
367 }
368
message() const369 QString PingPong::message() const
370 {
371 return m_message;
372 }
373
serverConnected()374 void PingPong::serverConnected()
375 {
376 setMessage("Server Connected");
377 QByteArray size;
378 size.setNum(m_boardWidth);
379 size.append(' ');
380 QByteArray size1;
381 size1.setNum(m_boardHeight);
382 size.append(size1);
383 size.append(" \n");
384 socket->write(size.constData());
385 }
386
serverDisconnected()387 void PingPong::serverDisconnected()
388 {
389 setMessage("Server Disconnected");
390 m_timer->stop();
391 }
392
readSocket()393 void PingPong::readSocket()
394 {
395 if (!socket)
396 return;
397 const char sep = ' ';
398 QByteArray line;
399 while (socket->canReadLine()) {
400 line = socket->readLine();
401 //qDebug() << QString::fromUtf8(line.constData(), line.length());
402 if (line.contains("result")) {
403 QList<QByteArray> result = line.split(sep);
404 if (result.size() > 2) {
405 QByteArray leftSide = result.at(1);
406 QByteArray rightSide = result.at(2);
407 m_resultLeft = leftSide.toInt();
408 m_resultRight = rightSide.toInt();
409 emit resultChanged();
410 checkResult();
411 }
412 }
413 }
414 if ((m_proportionX == 0 || m_proportionY == 0)) {
415 QList<QByteArray> boardSize = line.split(sep);
416 if (boardSize.size() > 1) {
417 QByteArray boardWidth = boardSize.at(0);
418 QByteArray boardHeight = boardSize.at(1);
419 m_proportionX = m_boardWidth/boardWidth.toFloat();
420 m_proportionY = m_boardHeight/boardHeight.toFloat();
421 setMessage("Screen adjusted. Get ready!");
422 QTimer::singleShot(3000, this, SLOT(startGame()));
423 }
424 }
425 else if (m_role == 1) {
426 QList<QByteArray> boardSize = line.split(sep);
427 if (boardSize.size() > 1) {
428 QByteArray rightBlockY = boardSize.at(0);
429 m_rightBlockY = m_proportionY * rightBlockY.toFloat();
430 emit rightBlockChanged();
431 }
432 }
433 else if (m_role == 2) {
434 QList<QByteArray> boardSize = line.split(sep);
435 if (boardSize.size() > 2) {
436 QByteArray ballX = boardSize.at(0);
437 QByteArray ballY = boardSize.at(1);
438 QByteArray leftBlockY = boardSize.at(2);
439 m_ballX = m_proportionX * ballX.toFloat();
440 m_ballY = m_proportionY * ballY.toFloat();
441 m_leftBlockY = m_proportionY * leftBlockY.toFloat();
442 emit leftBlockChanged();
443 emit ballChanged();
444 }
445 }
446 }
447
setMessage(const QString & message)448 void PingPong::setMessage(const QString &message)
449 {
450 m_showDialog = true;
451 m_message = message;
452 emit showDialogChanged();
453 }
454
role() const455 int PingPong::role() const
456 {
457 return m_role;
458 }
459
leftResult() const460 int PingPong::leftResult() const
461 {
462 return m_resultLeft;
463 }
464
rightResult() const465 int PingPong::rightResult() const
466 {
467 return m_resultRight;
468 }
469
ballX() const470 float PingPong::ballX() const
471 {
472 return m_ballX;
473 }
474
ballY() const475 float PingPong::ballY() const
476 {
477 return m_ballY;
478 }
479
480
leftBlockY() const481 float PingPong::leftBlockY() const
482 {
483 return m_leftBlockY;
484 }
485
rightBlockY() const486 float PingPong::rightBlockY() const
487 {
488 return m_rightBlockY;
489 }
490