| /*************************************************************************** |
| ** |
| ** 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(); |
| //! [Start the game] |
| if (m_role == 1) |
| updateDirection(); |
| |
| m_timer->start(50); |
| //! [Start the game] |
| } |
| |
| void PingPong::update() |
| { |
| QByteArray size; |
| // Server is only updating the coordinates |
| //! [Updating 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()); |
| } |
| //! [Updating coordinates] |
| } |
| |
| |
| |
| 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; |
| //! [Checking the boundaries] |
| 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(); |
| } |
| //! [Checking the boundaries] |
| 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")); |
| //! [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")); |
| //! [Starting the 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() |
| { |
| //! [Searching for the service] |
| 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); |
| |
| //! [Searching for the service] |
| 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() |
| { |
| //! [Initiating server socket] |
| 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); |
| |
| //! [Initiating server socket] |
| 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..."); |
| //! [Connecting the socket] |
| 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); |
| //! [Connecting the socket] |
| 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; |
| } |