Qt 5.9 | Qt Bluetooth | Bluetooth QML Ping Pong example | pingpong.cpp Example File

Qt 5.9.9 Reference Documentation

# 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;
      }