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