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 | |
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(sender: m_timer, signal: &QTimer::timeout, receiver: this, slot: &PingPong::update); |
63 | } |
64 | |
65 | PingPong::~PingPong() |
66 | { |
67 | m_timer->stop(); |
68 | delete socket; |
69 | delete discoveryAgent; |
70 | } |
71 | |
72 | void PingPong::startGame() |
73 | { |
74 | m_showDialog = false; |
75 | emit showDialogChanged(); |
76 | //! [Start the game] |
77 | if (m_role == 1) |
78 | updateDirection(); |
79 | |
80 | m_timer->start(msec: 50); |
81 | //! [Start the game] |
82 | } |
83 | |
84 | void PingPong::update() |
85 | { |
86 | QByteArray size; |
87 | // Server is only updating the coordinates |
88 | //! [Updating coordinates] |
89 | if (m_role == 1) { |
90 | checkBoundaries(); |
91 | m_ballPreviousX = m_ballX; |
92 | m_ballPreviousY = m_ballY; |
93 | m_ballY = m_direction*(m_ballX+interval) - m_direction*m_ballX + m_ballY; |
94 | m_ballX = m_ballX + interval; |
95 | |
96 | size.setNum(n: m_ballX); |
97 | size.append(c: ' '); |
98 | QByteArray size1; |
99 | size1.setNum(n: m_ballY); |
100 | size.append(a: size1); |
101 | size.append(c: ' '); |
102 | size1.setNum(n: m_leftBlockY); |
103 | size.append(a: size1); |
104 | size.append(s: " \n" ); |
105 | socket->write(data: size.constData()); |
106 | emit ballChanged(); |
107 | } |
108 | else if (m_role == 2) { |
109 | size.setNum(n: m_rightBlockY); |
110 | size.append(s: " \n" ); |
111 | socket->write(data: size.constData()); |
112 | } |
113 | //! [Updating coordinates] |
114 | } |
115 | |
116 | |
117 | |
118 | void PingPong::setSize(const float &x, const float &y) |
119 | { |
120 | m_boardWidth = x; |
121 | m_boardHeight = y; |
122 | m_targetX = m_boardWidth; |
123 | m_targetY = m_boardHeight/2; |
124 | m_ballPreviousX = m_ballX = m_boardWidth/2; |
125 | m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54; |
126 | emit ballChanged(); |
127 | } |
128 | |
129 | void PingPong::updateBall(const float &bX, const float &bY) |
130 | { |
131 | m_ballX = bX; |
132 | m_ballY = bY; |
133 | } |
134 | |
135 | void PingPong::updateLeftBlock(const float &lY) |
136 | { |
137 | m_leftBlockY = lY; |
138 | } |
139 | |
140 | void PingPong::updateRightBlock(const float &rY) |
141 | { |
142 | m_rightBlockY = rY; |
143 | } |
144 | |
145 | void PingPong::checkBoundaries() |
146 | { |
147 | float ballWidth = m_boardWidth/54; |
148 | float blockSize = m_boardWidth/27; |
149 | float blockHeight = m_boardHeight/5; |
150 | //! [Checking the boundaries] |
151 | if (((m_ballX + ballWidth) > (m_boardWidth - blockSize)) && ((m_ballY + ballWidth) < (m_rightBlockY + blockHeight)) |
152 | && (m_ballY > m_rightBlockY)) { |
153 | m_targetY = 2 * m_ballY - m_ballPreviousY; |
154 | m_targetX = m_ballPreviousX; |
155 | interval = -5; |
156 | updateDirection(); |
157 | } |
158 | else if ((m_ballX < blockSize) && ((m_ballY + ballWidth) < (m_leftBlockY + blockHeight)) |
159 | && (m_ballY > m_leftBlockY)) { |
160 | m_targetY = 2 * m_ballY - m_ballPreviousY; |
161 | m_targetX = m_ballPreviousX; |
162 | interval = 5; |
163 | updateDirection(); |
164 | } |
165 | else if (m_ballY < 0 || (m_ballY + ballWidth > m_boardHeight)) { |
166 | m_targetY = m_ballPreviousY; |
167 | m_targetX = m_ballX + interval; |
168 | updateDirection(); |
169 | } |
170 | //! [Checking the boundaries] |
171 | else if ((m_ballX + ballWidth) > m_boardWidth) { |
172 | m_resultLeft++; |
173 | m_targetX = m_boardWidth; |
174 | m_targetY = m_boardHeight/2; |
175 | m_ballPreviousX = m_ballX = m_boardWidth/2; |
176 | m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54; |
177 | |
178 | updateDirection(); |
179 | checkResult(); |
180 | QByteArray result; |
181 | result.append(s: "result " ); |
182 | QByteArray res; |
183 | res.setNum(n: m_resultLeft); |
184 | result.append(a: res); |
185 | result.append(c: ' '); |
186 | res.setNum(n: m_resultRight); |
187 | result.append(a: res); |
188 | result.append(s: " \n" ); |
189 | socket->write(data: result); |
190 | qDebug() << result; |
191 | emit resultChanged(); |
192 | } |
193 | else if (m_ballX < 0) { |
194 | m_resultRight++; |
195 | m_targetX = 0; |
196 | m_targetY = m_boardHeight/2; |
197 | m_ballPreviousX = m_ballX = m_boardWidth/2; |
198 | m_ballPreviousY = m_ballY = m_boardHeight - m_boardWidth/54; |
199 | updateDirection(); |
200 | checkResult(); |
201 | QByteArray result; |
202 | result.append(s: "result " ); |
203 | QByteArray res; |
204 | res.setNum(n: m_resultLeft); |
205 | result.append(a: res); |
206 | result.append(c: ' '); |
207 | res.setNum(n: m_resultRight); |
208 | result.append(a: res); |
209 | result.append(s: " \n" ); |
210 | socket->write(data: result); |
211 | emit resultChanged(); |
212 | } |
213 | } |
214 | |
215 | void PingPong::checkResult() |
216 | { |
217 | if (m_resultRight == 10 && m_role == 2) { |
218 | setMessage("Game over. You win!" ); |
219 | m_timer->stop(); |
220 | } |
221 | else if (m_resultRight == 10 && m_role == 1) { |
222 | setMessage("Game over. You lose!" ); |
223 | m_timer->stop(); |
224 | } |
225 | else if (m_resultLeft == 10 && m_role == 1) { |
226 | setMessage("Game over. You win!" ); |
227 | m_timer->stop(); |
228 | } |
229 | else if (m_resultLeft == 10 && m_role == 2) { |
230 | setMessage("Game over. You lose!" ); |
231 | m_timer->stop(); |
232 | } |
233 | } |
234 | |
235 | void PingPong::updateDirection() |
236 | { |
237 | m_direction = (m_targetY - m_ballY)/(m_targetX - m_ballX); |
238 | } |
239 | |
240 | void PingPong::startServer() |
241 | { |
242 | setMessage(QStringLiteral("Starting the server" )); |
243 | //! [Starting the server] |
244 | m_serverInfo = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); |
245 | connect(sender: m_serverInfo, signal: &QBluetoothServer::newConnection, |
246 | receiver: this, slot: &PingPong::clientConnected); |
247 | connect(sender: m_serverInfo, signal: QOverload<QBluetoothServer::Error>::of(ptr: &QBluetoothServer::error), |
248 | receiver: this, slot: &PingPong::serverError); |
249 | const QBluetoothUuid uuid(serviceUuid); |
250 | |
251 | m_serviceInfo = m_serverInfo->listen(uuid, QStringLiteral("PingPong server" )); |
252 | //! [Starting the server] |
253 | setMessage(QStringLiteral("Server started, waiting for the client. You are the left player." )); |
254 | // m_role is set to 1 if it is a server |
255 | m_role = 1; |
256 | emit roleChanged(); |
257 | } |
258 | |
259 | void PingPong::startClient() |
260 | { |
261 | //! [Searching for the service] |
262 | discoveryAgent = new QBluetoothServiceDiscoveryAgent(QBluetoothAddress()); |
263 | |
264 | connect(sender: discoveryAgent, signal: &QBluetoothServiceDiscoveryAgent::serviceDiscovered, |
265 | receiver: this, slot: &PingPong::addService); |
266 | connect(sender: discoveryAgent, signal: &QBluetoothServiceDiscoveryAgent::finished, |
267 | receiver: this, slot: &PingPong::done); |
268 | connect(sender: discoveryAgent, signal: QOverload<QBluetoothServiceDiscoveryAgent::Error>::of(ptr: &QBluetoothServiceDiscoveryAgent::error), |
269 | receiver: this, slot: &PingPong::serviceScanError); |
270 | #ifdef Q_OS_ANDROID //see QTBUG-61392 |
271 | if (QtAndroid::androidSdkVersion() >= 23) |
272 | discoveryAgent->setUuidFilter(QBluetoothUuid(androidUuid)); |
273 | else |
274 | discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid)); |
275 | #else |
276 | discoveryAgent->setUuidFilter(QBluetoothUuid(serviceUuid)); |
277 | #endif |
278 | discoveryAgent->start(mode: QBluetoothServiceDiscoveryAgent::FullDiscovery); |
279 | |
280 | //! [Searching for the service] |
281 | setMessage(QStringLiteral("Starting server discovery. You are the right player" )); |
282 | // m_role is set to 2 if it is a client |
283 | m_role = 2; |
284 | emit roleChanged(); |
285 | } |
286 | |
287 | void PingPong::clientConnected() |
288 | { |
289 | //! [Initiating server socket] |
290 | if (!m_serverInfo->hasPendingConnections()) { |
291 | setMessage("FAIL: expected pending server connection" ); |
292 | return; |
293 | } |
294 | socket = m_serverInfo->nextPendingConnection(); |
295 | if (!socket) |
296 | return; |
297 | socket->setParent(this); |
298 | connect(sender: socket, signal: &QBluetoothSocket::readyRead, |
299 | receiver: this, slot: &PingPong::readSocket); |
300 | connect(sender: socket, signal: &QBluetoothSocket::disconnected, |
301 | receiver: this, slot: &PingPong::clientDisconnected); |
302 | connect(sender: socket, signal: QOverload<QBluetoothSocket::SocketError>::of(ptr: &QBluetoothSocket::error), |
303 | receiver: this, slot: &PingPong::socketError); |
304 | |
305 | //! [Initiating server socket] |
306 | setMessage(QStringLiteral("Client connected." )); |
307 | |
308 | QByteArray size; |
309 | size.setNum(n: m_boardWidth); |
310 | size.append(c: ' '); |
311 | QByteArray size1; |
312 | size1.setNum(n: m_boardHeight); |
313 | size.append(a: size1); |
314 | size.append(s: " \n" ); |
315 | socket->write(data: size.constData()); |
316 | |
317 | } |
318 | |
319 | void PingPong::clientDisconnected() |
320 | { |
321 | setMessage(QStringLiteral("Client disconnected" )); |
322 | m_timer->stop(); |
323 | } |
324 | |
325 | void PingPong::socketError(QBluetoothSocket::SocketError error) |
326 | { |
327 | Q_UNUSED(error); |
328 | m_timer->stop(); |
329 | } |
330 | |
331 | void PingPong::serverError(QBluetoothServer::Error error) |
332 | { |
333 | Q_UNUSED(error); |
334 | m_timer->stop(); |
335 | } |
336 | |
337 | void PingPong::done() |
338 | { |
339 | qDebug() << "Service scan done" ; |
340 | if (!m_serviceFound) |
341 | setMessage("PingPong service not found" ); |
342 | } |
343 | |
344 | void PingPong::addService(const QBluetoothServiceInfo &service) |
345 | { |
346 | if (m_serviceFound) |
347 | return; |
348 | m_serviceFound = true; |
349 | |
350 | setMessage("Service found. Stopping discovery and creating connection..." ); |
351 | discoveryAgent->stop(); |
352 | //! [Connecting the socket] |
353 | socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol); |
354 | socket->connectToService(service); |
355 | |
356 | connect(sender: socket, signal: &QBluetoothSocket::readyRead, receiver: this, slot: &PingPong::readSocket); |
357 | connect(sender: socket, signal: &QBluetoothSocket::connected, receiver: this, slot: &PingPong::serverConnected); |
358 | connect(sender: socket, signal: &QBluetoothSocket::disconnected, receiver: this, slot: &PingPong::serverDisconnected); |
359 | //! [Connecting the socket] |
360 | } |
361 | |
362 | void PingPong::serviceScanError(QBluetoothServiceDiscoveryAgent::Error error) |
363 | { |
364 | setMessage(QStringLiteral("Scanning error" ) + error); |
365 | } |
366 | |
367 | bool PingPong::showDialog() const |
368 | { |
369 | return m_showDialog; |
370 | } |
371 | |
372 | QString PingPong::message() const |
373 | { |
374 | return m_message; |
375 | } |
376 | |
377 | void PingPong::serverConnected() |
378 | { |
379 | setMessage("Server Connected" ); |
380 | QByteArray size; |
381 | size.setNum(n: m_boardWidth); |
382 | size.append(c: ' '); |
383 | QByteArray size1; |
384 | size1.setNum(n: m_boardHeight); |
385 | size.append(a: size1); |
386 | size.append(s: " \n" ); |
387 | socket->write(data: size.constData()); |
388 | } |
389 | |
390 | void PingPong::serverDisconnected() |
391 | { |
392 | setMessage("Server Disconnected" ); |
393 | m_timer->stop(); |
394 | } |
395 | |
396 | void PingPong::readSocket() |
397 | { |
398 | if (!socket) |
399 | return; |
400 | const char sep = ' '; |
401 | QByteArray line; |
402 | while (socket->canReadLine()) { |
403 | line = socket->readLine(); |
404 | //qDebug() << QString::fromUtf8(line.constData(), line.length()); |
405 | if (line.contains(c: "result" )) { |
406 | QList<QByteArray> result = line.split(sep); |
407 | if (result.size() > 2) { |
408 | QByteArray leftSide = result.at(i: 1); |
409 | QByteArray rightSide = result.at(i: 2); |
410 | m_resultLeft = leftSide.toInt(); |
411 | m_resultRight = rightSide.toInt(); |
412 | emit resultChanged(); |
413 | checkResult(); |
414 | } |
415 | } |
416 | } |
417 | if ((m_proportionX == 0 || m_proportionY == 0)) { |
418 | QList<QByteArray> boardSize = line.split(sep); |
419 | if (boardSize.size() > 1) { |
420 | QByteArray boardWidth = boardSize.at(i: 0); |
421 | QByteArray boardHeight = boardSize.at(i: 1); |
422 | m_proportionX = m_boardWidth/boardWidth.toFloat(); |
423 | m_proportionY = m_boardHeight/boardHeight.toFloat(); |
424 | setMessage("Screen adjusted. Get ready!" ); |
425 | QTimer::singleShot(msec: 3000, receiver: this, SLOT(startGame())); |
426 | } |
427 | } |
428 | else if (m_role == 1) { |
429 | QList<QByteArray> boardSize = line.split(sep); |
430 | if (boardSize.size() > 1) { |
431 | QByteArray rightBlockY = boardSize.at(i: 0); |
432 | m_rightBlockY = m_proportionY * rightBlockY.toFloat(); |
433 | emit rightBlockChanged(); |
434 | } |
435 | } |
436 | else if (m_role == 2) { |
437 | QList<QByteArray> boardSize = line.split(sep); |
438 | if (boardSize.size() > 2) { |
439 | QByteArray ballX = boardSize.at(i: 0); |
440 | QByteArray ballY = boardSize.at(i: 1); |
441 | QByteArray leftBlockY = boardSize.at(i: 2); |
442 | m_ballX = m_proportionX * ballX.toFloat(); |
443 | m_ballY = m_proportionY * ballY.toFloat(); |
444 | m_leftBlockY = m_proportionY * leftBlockY.toFloat(); |
445 | emit leftBlockChanged(); |
446 | emit ballChanged(); |
447 | } |
448 | } |
449 | } |
450 | |
451 | void PingPong::setMessage(const QString &message) |
452 | { |
453 | m_showDialog = true; |
454 | m_message = message; |
455 | emit showDialogChanged(); |
456 | } |
457 | |
458 | int PingPong::role() const |
459 | { |
460 | return m_role; |
461 | } |
462 | |
463 | int PingPong::leftResult() const |
464 | { |
465 | return m_resultLeft; |
466 | } |
467 | |
468 | int PingPong::rightResult() const |
469 | { |
470 | return m_resultRight; |
471 | } |
472 | |
473 | float PingPong::ballX() const |
474 | { |
475 | return m_ballX; |
476 | } |
477 | |
478 | float PingPong::ballY() const |
479 | { |
480 | return m_ballY; |
481 | } |
482 | |
483 | |
484 | float PingPong::leftBlockY() const |
485 | { |
486 | return m_leftBlockY; |
487 | } |
488 | |
489 | float PingPong::rightBlockY() const |
490 | { |
491 | return m_rightBlockY; |
492 | } |
493 | |