pingpong.cpp Example File

pingpong/pingpong.cpp
 /***************************************************************************
 **
 ** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
 ** Contact: https://www.qt.io/licensing/
 **
 ** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** Commercial License Usage
 ** Licensees holding valid commercial Qt licenses may use this file in
 ** accordance with the commercial license agreement provided with the
 ** Software or, alternatively, in accordance with the terms contained in
 ** a written agreement between you and The Qt Company. For licensing terms
 ** and conditions see https://www.qt.io/terms-conditions. For further
 ** information use the contact form at https://www.qt.io/contact-us.
 **
 ** BSD License Usage
 ** Alternatively, you may use this file under the terms of the BSD license
 ** as follows:
 **
 ** "Redistribution and use in source and binary forms, with or without
 ** modification, are permitted provided that the following conditions are
 ** met:
 **   * Redistributions of source code must retain the above copyright
 **     notice, this list of conditions and the following disclaimer.
 **   * Redistributions in binary form must reproduce the above copyright
 **     notice, this list of conditions and the following disclaimer in
 **     the documentation and/or other materials provided with the
 **     distribution.
 **   * Neither the name of The Qt Company Ltd nor the names of its
 **     contributors may be used to endorse or promote products derived
 **     from this software without specific prior written permission.
 **
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 **
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

 #include "pingpong.h"
 #include <QDebug>
 #ifdef Q_OS_ANDROID
 #include <QtAndroid>
 #endif

 PingPong::PingPong():
     m_serverInfo(0), socket(0), discoveryAgent(0), interval(5), m_resultLeft(0), m_resultRight(0),
     m_showDialog(false), m_role(0), m_proportionX(0), m_proportionY(0), m_serviceFound(false)
 {
     m_timer = new QTimer(this);
     connect(m_timer, &QTimer::timeout, this, &PingPong::update);
 }

 PingPong::~PingPong()
 {
     delete m_timer;
     delete m_serverInfo;
     delete socket;
     delete discoveryAgent;
 }

 void PingPong::startGame()
 {
     m_showDialog = false;
     emit showDialogChanged();
     if (m_role == 1)
         updateDirection();

     m_timer->start(50);
 }

 void PingPong::update()
 {
     QByteArray size;
     // Server is only updating the coordinates
     if (m_role == 1) {
         checkBoundaries();
         m_ballPreviousX = m_ballX;
         m_ballPreviousY = m_ballY;
         m_ballY = m_direction*(m_ballX+interval) - m_direction*m_ballX + m_ballY;
         m_ballX = m_ballX + interval;

         size.setNum(m_ballX);
         size.append(' ');
         QByteArray size1;
         size1.setNum(m_ballY);
         size.append(size1);
         size.append(' ');
         size1.setNum(m_leftBlockY);
         size.append(size1);
         size.append(" \n");
         socket->write(size.constData());
         emit ballChanged();
     }
     else if (m_role == 2) {
         size.setNum(m_rightBlockY);
         size.append(" \n");
         socket->write(size.constData());
     }
 }

 void PingPong::setSize(const float &x, const float &y)
 {
     m_boardWidth = x;
     m_boardHeight = y;
     m_targetX = m_boardWidth;
     m_targetY = m_boardHeight/2;
     m_ballPreviousX = m_ballX = m_boardWidth/2;
     m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
     emit ballChanged();
 }

 void PingPong::updateBall(const float &bX, const float &bY)
 {
     m_ballX = bX;
     m_ballY = bY;
 }

 void PingPong::updateLeftBlock(const float &lY)
 {
     m_leftBlockY = lY;
 }

 void PingPong::updateRightBlock(const float &rY)
 {
     m_rightBlockY = rY;
 }

 void PingPong::checkBoundaries()
 {
     float ballWidth = m_boardWidth/54;
     float blockSize = m_boardWidth/27;
     float blockHeight = m_boardHeight/5;
     if (((m_ballX + ballWidth) > (m_boardWidth - blockSize)) && ((m_ballY + ballWidth) < (m_rightBlockY + blockHeight))
             && (m_ballY > m_rightBlockY)) {
         m_targetY = 2 * m_ballY - m_ballPreviousY;
         m_targetX = m_ballPreviousX;
         interval = -5;
         updateDirection();
     }
     else if ((m_ballX < blockSize) && ((m_ballY + ballWidth) < (m_leftBlockY + blockHeight))
              && (m_ballY > m_leftBlockY)) {
         m_targetY = 2 * m_ballY - m_ballPreviousY;
         m_targetX = m_ballPreviousX;
         interval = 5;
         updateDirection();
     }
     else if (m_ballY < 0 || (m_ballY + ballWidth > m_boardHeight)) {
         m_targetY = m_ballPreviousY;
         m_targetX = m_ballX + interval;
         updateDirection();
     }
     else if ((m_ballX + ballWidth) > m_boardWidth) {
         m_resultLeft++;
         m_targetX = m_boardWidth;
         m_targetY = m_boardHeight/2;
         m_ballPreviousX = m_ballX = m_boardWidth/2;
         m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;

         updateDirection();
         checkResult();
         QByteArray result;
         result.append("result ");
         QByteArray res;
         res.setNum(m_resultLeft);
         result.append(res);
         result.append(' ');
         res.setNum(m_resultRight);
         result.append(res);
         result.append(" \n");
         socket->write(result);
         qDebug() << result;
         emit resultChanged();
     }
     else if (m_ballX < 0) {
         m_resultRight++;
         m_targetX = 0;
         m_targetY = m_boardHeight/2;
         m_ballPreviousX = m_ballX = m_boardWidth/2;
         m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54;
         updateDirection();
         checkResult();
         QByteArray result;
         result.append("result ");
         QByteArray res;
         res.setNum(m_resultLeft);
         result.append(res);
         result.append(' ');
         res.setNum(m_resultRight);
         result.append(res);
         result.append(" \n");
         socket->write(result);
         emit resultChanged();
     }
 }

 void PingPong::checkResult()
 {
     if (m_resultRight == 10 && m_role == 2) {
         setMessage("Game over. You win!");
         m_timer->stop();
     }
     else if (m_resultRight == 10 && m_role == 1) {
         setMessage("Game over. You lose!");
         m_timer->stop();
     }
     else if (m_resultLeft == 10 && m_role == 1) {
         setMessage("Game over. You win!");
         m_timer->stop();
     }
     else if (m_resultLeft == 10 && m_role == 2) {
         setMessage("Game over. You lose!");
         m_timer->stop();
     }
 }

 void PingPong::updateDirection()
 {
     m_direction = (m_targetY - m_ballY)/(m_targetX - m_ballX);
 }

 void PingPong::startServer()
 {
     setMessage(QStringLiteral("Starting the server"));
     m_serverInfo = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this);
     connect(m_serverInfo, &QBluetoothServer::newConnection,
             this, &PingPong::clientConnected);
     connect(m_serverInfo, QOverload<QBluetoothServer::Error>::of(&QBluetoothServer::error),
             this, &PingPong::serverError);
     const QBluetoothUuid uuid(serviceUuid);

     m_serverInfo->listen(uuid, QStringLiteral("PingPong server"));
     setMessage(QStringLiteral("Server started, waiting for the client. You are the left player."));
     // m_role is set to 1 if it is a server
     m_role = 1;
     emit roleChanged();
 }

 void PingPong::startClient()
 {
     discoveryAgent = new QBluetoothServiceDiscoveryAgent(QBluetoothAddress());

     connect(discoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered,
             this, &PingPong::addService);
     connect(discoveryAgent, &QBluetoothServiceDiscoveryAgent::finished,
             this, &PingPong::done);
     connect(discoveryAgent, QOverload<QBluetoothServiceDiscoveryAgent::Error>::of(&QBluetoothServiceDiscoveryAgent::error),
             this, &PingPong::serviceScanError);
 #ifdef Q_OS_ANDROID //see QTBUG-61392
     if (QtAndroid::androidSdkVersion() >= 23)
         discoveryAgent->setUuidFilter(QBluetoothUuid(androidUuid));
     else
         discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid));
 #else
     discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid));
 #endif
     discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);

     setMessage(QStringLiteral("Starting server discovery. You are the right player"));
     // m_role is set to 2 if it is a client
     m_role = 2;
     emit roleChanged();
 }

 void PingPong::clientConnected()
 {
     if (!m_serverInfo->hasPendingConnections()) {
         setMessage("FAIL: expected pending server connection");
         return;
     }
     socket = m_serverInfo->nextPendingConnection();
     if (!socket)
         return;
     socket->setParent(this);
     connect(socket, &QBluetoothSocket::readyRead,
             this, &PingPong::readSocket);
     connect(socket, &QBluetoothSocket::disconnected,
             this, &PingPong::clientDisconnected);
     connect(socket, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::error),
             this, &PingPong::socketError);

     setMessage(QStringLiteral("Client connected."));

     QByteArray size;
     size.setNum(m_boardWidth);
     size.append(' ');
     QByteArray size1;
     size1.setNum(m_boardHeight);
     size.append(size1);
     size.append(" \n");
     socket->write(size.constData());

 }

 void PingPong::clientDisconnected()
 {
     setMessage(QStringLiteral("Client disconnected"));
     m_timer->stop();
 }

 void PingPong::socketError(QBluetoothSocket::SocketError error)
 {
     Q_UNUSED(error);
     m_timer->stop();
 }

 void PingPong::serverError(QBluetoothServer::Error error)
 {
     Q_UNUSED(error);
     m_timer->stop();
 }

 void PingPong::done()
 {
     qDebug() << "Service scan done";
     if (!m_serviceFound)
         setMessage("PingPong service not found");
 }

 void PingPong::addService(const QBluetoothServiceInfo &service)
 {
     setMessage("Service found. Setting parameters...");
     socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
     socket->connectToService(service);

     connect(socket, &QBluetoothSocket::readyRead, this, &PingPong::readSocket);
     connect(socket, &QBluetoothSocket::connected, this, &PingPong::serverConnected);
     connect(socket, &QBluetoothSocket::disconnected, this, &PingPong::serverDisconnected);
     m_serviceFound = true;
 }

 void PingPong::serviceScanError(QBluetoothServiceDiscoveryAgent::Error error)
 {
     setMessage(QStringLiteral("Scanning error") + error);
 }

 bool PingPong::showDialog() const
 {
     return m_showDialog;
 }

 QString PingPong::message() const
 {
     return m_message;
 }

 void PingPong::serverConnected()
 {
     setMessage("Server Connected");
     QByteArray size;
     size.setNum(m_boardWidth);
     size.append(' ');
     QByteArray size1;
     size1.setNum(m_boardHeight);
     size.append(size1);
     size.append(" \n");
     socket->write(size.constData());
 }

 void PingPong::serverDisconnected()
 {
     setMessage("Server Disconnected");
     m_timer->stop();
 }

 void PingPong::readSocket()
 {
     if (!socket)
         return;
     const char sep = ' ';
     QByteArray line;
     while (socket->canReadLine()) {
         line = socket->readLine();
         //qDebug() << QString::fromUtf8(line.constData(), line.length());
         if (line.contains("result")) {
             QList<QByteArray> result = line.split(sep);
             if (result.size() > 2) {
                 QByteArray leftSide = result.at(1);
                 QByteArray rightSide = result.at(2);
                 m_resultLeft = leftSide.toInt();
                 m_resultRight = rightSide.toInt();
                 emit resultChanged();
                 checkResult();
             }
         }
     }
     if ((m_proportionX == 0 || m_proportionY == 0)) {
         QList<QByteArray> boardSize = line.split(sep);
         if (boardSize.size() > 1) {
             QByteArray boardWidth = boardSize.at(0);
             QByteArray boardHeight = boardSize.at(1);
             m_proportionX = m_boardWidth/boardWidth.toFloat();
             m_proportionY = m_boardHeight/boardHeight.toFloat();
             setMessage("Screen adjusted. Get ready!");
             QTimer::singleShot(3000, this, SLOT(startGame()));
         }
     }
     else if (m_role == 1) {
         QList<QByteArray> boardSize = line.split(sep);
         if (boardSize.size() > 1) {
             QByteArray rightBlockY = boardSize.at(0);
             m_rightBlockY = m_proportionY * rightBlockY.toFloat();
             emit rightBlockChanged();
         }
     }
     else if (m_role == 2) {
         QList<QByteArray> boardSize = line.split(sep);
         if (boardSize.size() > 2) {
             QByteArray ballX = boardSize.at(0);
             QByteArray ballY = boardSize.at(1);
             QByteArray leftBlockY = boardSize.at(2);
             m_ballX = m_proportionX * ballX.toFloat();
             m_ballY = m_proportionY * ballY.toFloat();
             m_leftBlockY = m_proportionY * leftBlockY.toFloat();
             emit leftBlockChanged();
             emit ballChanged();
         }
     }
 }

 void PingPong::setMessage(const QString &message)
 {
     m_showDialog = true;
     m_message = message;
     emit showDialogChanged();
 }

 int PingPong::role() const
 {
     return m_role;
 }

 int PingPong::leftResult() const
 {
     return m_resultLeft;
 }

 int PingPong::rightResult() const
 {
     return m_resultRight;
 }

 float PingPong::ballX() const
 {
     return m_ballX;
 }

 float PingPong::ballY() const
 {
     return m_ballY;
 }

 float PingPong::leftBlockY() const
 {
     return m_leftBlockY;
 }

 float PingPong::rightBlockY() const
 {
     return m_rightBlockY;
 }