Cavoke  1.1.0
A Platform for creating and hosting multiplayer turn-based board games
Loading...
Searching...
No Matches
cavokeclientcontroller.cpp
1#include "cavokeclientcontroller.h"
2#include <QOAuthHttpServerReplyHandler>
3
4CavokeClientController::CavokeClientController(QObject *parent)
5 : QObject{parent},
6 model{parent},
7 networkManager{parent},
8 testWindowView{},
9 startView{},
10 joinGameView{},
11 statisticsView{},
12 settingsView{},
13 roomView{},
14 sessionView{},
15 settings{} {
16 // startQml connection
17 connect(&model, SIGNAL(startQmlApplication(CavokeQmlGameModel *)), this,
18 SLOT(startQmlApplication(CavokeQmlGameModel *)));
19
20 // All back buttons
21 connect(&testWindowView, SIGNAL(shownStartView()), this,
22 SLOT(showStartView()));
23 connect(&joinGameView, SIGNAL(shownStartView()), this,
24 SLOT(showStartView()));
25 connect(&createGameView, SIGNAL(shownStartView()), this,
26 SLOT(showStartView()));
27 connect(&gamesListView, SIGNAL(shownStartView()), this,
28 SLOT(showStartView()));
29 connect(&settingsView, SIGNAL(shownStartView()), this,
30 SLOT(showStartView()));
31 connect(&statisticsView, SIGNAL(shownStartView()), this,
32 SLOT(showStartView()));
33 connect(&roomView, SIGNAL(shownStartView()), this, SLOT(showStartView()));
34
35 // Main navigation buttons from startView
36 connect(&startView, SIGNAL(shownTestWindowView()), this,
37 SLOT(showTestWindowView())); // Not displayed
38 connect(&startView, SIGNAL(shownJoinGameView()), this,
39 SLOT(showJoinGameView()));
40 connect(&startView, SIGNAL(shownCreateGameView()), this,
41 SLOT(showCreateGameView()));
42 connect(&startView, SIGNAL(shownGamesListView()), this,
43 SLOT(showGamesListView()));
44 connect(&startView, SIGNAL(shownSettingsView()), this,
45 SLOT(showSettingsView()));
46 connect(&startView, SIGNAL(shownStatisticsView()), this,
47 SLOT(showStatisticsView()));
48
49 // startView Exit button
50 connect(&startView, SIGNAL(clickedExitButton()), this,
51 SLOT(exitApplication()), Qt::QueuedConnection);
52
53 // testWindowView actions
54 connect(&testWindowView, SIGNAL(startGame(QString)), &model,
55 SLOT(loadQmlGame(QString)));
56 connect(&testWindowView, SIGNAL(testHealthConnectionButton()),
57 &networkManager, SLOT(getHealth()));
58
59 // createGameView actions
60 connect(&createGameView, SIGNAL(startedCreateGameRoutine(QString)), this,
61 SLOT(createGameStart(QString)));
62
63 // joinGameView actions
64 connect(&joinGameView, SIGNAL(joinedGame(QString)), this,
65 SLOT(joinGameStart(QString)));
66
67 // gamesListView actions
68 connect(&gamesListView, SIGNAL(currentIndexChanged(int)), &model,
69 SLOT(receivedGameIndexChangeInList(int)));
70 connect(&gamesListView, SIGNAL(requestGameStatistics(QString)),
71 &networkManager, SLOT(getGameStatistics(QString)));
72 connect(&model, SIGNAL(updateSelectedGameInList(GameInfo)), &gamesListView,
73 SLOT(gotNewSelectedGame(GameInfo)));
74 connect(&networkManager, SIGNAL(gotGameStatistics(GameStatistics)),
75 &gamesListView, SLOT(gotNewGameStatistics(GameStatistics)));
76 connect(&gamesListView, SIGNAL(requestedDownloadGame(int)), &model,
77 SLOT(gotIndexToDownload(int)));
78 connect(this, SIGNAL(clearScreens()), &gamesListView, SLOT(displayEmpty()));
79
80 // session/room/game-Info processing actions
81 connect(&networkManager, SIGNAL(gotSessionInfo(SessionInfo)), this,
82 SLOT(gotSessionInfo(SessionInfo)));
83 connect(&networkManager, SIGNAL(gotGameInfo(GameInfo)), this,
84 SLOT(gotCurrentGameInfo(GameInfo)));
85 connect(this, SIGNAL(setGameName(QString)), &sessionView,
86 SLOT(updateGameName(QString)));
87 connect(&networkManager, SIGNAL(gotRoomInfo(RoomInfo)), this,
88 SLOT(gotRoomInfo(RoomInfo)));
89
90 // Download games list from server workflow
91 connect(this, SIGNAL(loadGamesList()), &networkManager,
92 SLOT(getGamesList()));
93 connect(&networkManager, SIGNAL(finalizedGamesList(QJsonArray)), &model,
94 SLOT(updateGamesList(QJsonArray)));
95 connect(&model, SIGNAL(gamesListUpdated(std::vector<GameInfo>)),
96 &gamesListView, SLOT(gotGamesListUpdate(std::vector<GameInfo>)));
97 connect(&model, SIGNAL(gamesListUpdated(std::vector<GameInfo>)),
98 &statisticsView, SLOT(gotGamesListUpdate(std::vector<GameInfo>)));
99 connect(&model, SIGNAL(gamesListUpdated(std::vector<GameInfo>)), &roomView,
100 SLOT(gotGamesListUpdate(std::vector<GameInfo>)));
101
102 // Download and unpack game workflow
103 connect(&model, SIGNAL(downloadGame(QString)), &networkManager,
104 SLOT(getGamesClient(QString)));
105 connect(&networkManager, SIGNAL(downloadedGameFile(QFile *, QString)), this,
106 SLOT(unpackDownloadedQml(QFile *, QString)));
107
108 // roomView actions
109 connect(&roomView, SIGNAL(leftRoom()), this, SLOT(leftRoom()));
110 connect(&roomView, SIGNAL(createdSession(QString)), this,
111 SLOT(createSessionStart(QString)));
112 connect(&roomView, SIGNAL(joinedSession(QString)), this,
113 SLOT(joinSessionStart(QString)));
114 connect(&roomView, SIGNAL(requestedSessionUpdate(QString)), &networkManager,
115 SLOT(getSessionInfo(QString)));
116
117 // sessionView actions
118 connect(&sessionView, SIGNAL(joinedCreatedGame()), this,
119 SLOT(startLoadedQml()));
120 connect(&networkManager, SIGNAL(gotValidationResult(ValidationResult)),
121 &sessionView, SLOT(updateValidationResult(ValidationResult)));
122 connect(&sessionView, SIGNAL(createdGame()), &networkManager,
123 SLOT(startSession()));
124 connect(this, SIGNAL(createdAvailableRolesList(std::vector<Role>)),
125 &sessionView, SLOT(gotRolesListUpdate(std::vector<Role>)));
126 connect(&sessionView, SIGNAL(newRoleChosen(int)), &networkManager,
127 SLOT(changeRoleInSession(int)));
128 connect(&sessionView, SIGNAL(leftSession()), this, SLOT(leftSession()));
129 connect(&sessionView, SIGNAL(shownRoomView()), this, SLOT(showRoomView()));
130
131 // settingsView actions
132 connect(this, SIGNAL(initSettingsValues(QString, QString)), &settingsView,
133 SLOT(initStartValues(QString, QString)));
134 connect(&settingsView, SIGNAL(updatedSettings(QString, QString)), this,
135 SLOT(updateSettings(QString, QString)));
136 connect(&networkManager, SIGNAL(gotDisplayName(QString)), &settingsView,
137 SLOT(updateDisplayName(QString)));
138
139 // statisticsView actions
140 connect(&networkManager, SIGNAL(gotUserStatistics(UserStatistics)),
141 &statisticsView, SLOT(gotUserStatisticsUpdate(UserStatistics)));
142 connect(&statisticsView, SIGNAL(requestedRefresh()), &networkManager,
143 SLOT(getMyUserStatistics()));
144 connect(&networkManager, SIGNAL(gotUserGameStatistics(UserGameStatistics)),
145 &statisticsView,
146 SLOT(gotUserGameStatisticsUpdate(UserGameStatistics)));
147 connect(&statisticsView, SIGNAL(statisticsGameChanged(QString)),
148 &networkManager, SLOT(getMyUserGameStatistics(QString)));
149
150 defaultSettingsInitialization();
151
152 // oauth reply handler
153 auto replyHandler = new QOAuthHttpServerReplyHandler(1337, this);
155 auth.oauth2.setReplyHandler(replyHandler);
156
157 // on new authentication update my user id and display name
158 connect(&auth, SIGNAL(authenticated()), &networkManager, SLOT(getMe()));
159 connect(&auth, SIGNAL(authenticated()), &networkManager,
160 SLOT(getMyUserStatistics()));
161 // initialize auth in a separate thread
162 QTimer::singleShot(0, [&]() { auth.init(); });
163
164 showStartView();
165}
166
167void CavokeClientController::defaultSettingsInitialization() {
168 settings.setValue(PLAYER_NICKNAME,
169 settings.value(PLAYER_NICKNAME, DEFAULT_NICKNAME));
170 settings.setValue(
171 NETWORK_HOST,
172 settings.value(NETWORK_HOST, NetworkManager::DEFAULT_HOST));
173
174 networkManager.changeHost(
175 QUrl::fromUserInput(settings.value(NETWORK_HOST).toString()));
176
177 if (cavoke::auth::AuthenticationManager::getInstance().checkAuthStatus()) {
178 networkManager.getMe();
179 }
180
181 emit initSettingsValues(settings.value(PLAYER_NICKNAME).toString(),
182 settings.value(NETWORK_HOST).toString());
183}
184
185void CavokeClientController::showTestWindowView() {
186 testWindowView.show();
187}
188
189void CavokeClientController::leftSession(bool real_leave) {
190 if (real_leave) {
191 networkManager.leaveSession();
192 }
193 networkManager.stopGamePolling();
194 networkManager.stopSessionPolling();
195 networkManager.stopValidationPolling();
196 qmlDownloadStatus = QMLDownloadStatus::NOT_STARTED;
197 hostGuestStatus = HostGuestStatus::NOT_IN;
198 currentGameInfo = GameInfo();
199 currentSessionInfo = SessionInfo();
200}
201
202void CavokeClientController::leftRoom() {
203 networkManager.stopRoomPolling();
204 networkManager.leaveRoom();
205 roomHostGuestStatus = HostGuestStatus::NOT_IN;
206 currentRoomInfo = RoomInfo();
207}
208
209void CavokeClientController::showStartView() {
210 startView.show();
211}
212
213void CavokeClientController::showJoinGameView() {
214 joinGameView.show();
215}
216
217void CavokeClientController::showGamesListView() {
218 gamesListView.show();
219}
220
221void CavokeClientController::showCreateGameView() {
222 createGameView.show();
223}
224
225void CavokeClientController::showRoomView() {
226 displacement = UserDisplacement::ROOM;
227 roomView.show();
228}
229
230void CavokeClientController::showSessionView() {
231 displacement = UserDisplacement::SESSION;
232 sessionView.show();
233}
234
235void CavokeClientController::showStatisticsView() {
236 statisticsView.requestUpdates();
237 statisticsView.show();
238}
239
240void CavokeClientController::showSettingsView() {
241 settingsView.show();
242}
243
244void CavokeClientController::startQmlApplication(
245 CavokeQmlGameModel *gameModel) {
246 auto *qmlView = new QQuickView();
247
248 connect(qmlView, SIGNAL(closing(QQuickCloseEvent *)), gameModel,
249 SLOT(getClosingFromQml(QQuickCloseEvent *)));
250
251 qmlView->rootContext()->setContextProperty("cavoke", gameModel);
252 qmlView->setSource(gameModel->qmlPath);
253 if (!qmlView->errors().empty()) {
254 qDebug() << "An error(s) occurred while opening the QML app:" << '\n';
255 for (const auto &error : qmlView->errors()) {
256 qDebug() << error;
257 }
258 qmlView->destroy();
259 return;
260 }
261 qmlView->show();
262}
263
264void CavokeClientController::exitApplication() {
265 testWindowView.close();
266 joinGameView.close();
267 gamesListView.close();
268 createGameView.close();
269 roomView.close();
270 sessionView.close();
271 settingsView.close();
272 startView.close();
273}
274
275void CavokeClientController::startQmlByGameId(const QString &gameId) {
276 currentQmlGameModel =
277 new CavokeQmlGameModel(cache_manager::get_cached_app_path(gameId));
278 startQmlApplication(currentQmlGameModel);
279 connect(currentQmlGameModel, SIGNAL(sendMoveToNetwork(QString)),
280 &networkManager, SLOT(sendMove(QString)));
281 connect(&networkManager, SIGNAL(gotGameUpdate(QString)),
282 currentQmlGameModel, SLOT(getUpdateFromNetwork(QString)));
283 connect(currentQmlGameModel, SIGNAL(closingQml()), this, SLOT(stopQml()));
284 networkManager.startGamePolling();
285}
286
287void CavokeClientController::startLoadedQml() {
288 displacement = UserDisplacement::GAME;
289 networkManager.stopSessionPolling();
290 networkManager.stopValidationPolling();
291 networkManager.stopRoomPolling();
292 startQmlByGameId(currentGameInfo.id);
293}
294
295void CavokeClientController::stopQml() {
296 networkManager.stopGamePolling();
297 networkManager.startRoomPolling();
298 leftSession(false); // Without actual `leave` request, due to issue #171
299 showRoomView();
300 currentQmlGameModel->deleteLater();
301}
302
303void CavokeClientController::unpackDownloadedQml(QFile *file,
304 const QString &gameId) {
305 cache_manager::save_zip_to_cache(file, gameId);
306 qDebug() << "UnpackDownloadedQml Finished";
307 file->deleteLater();
308
309 qmlDownloadStatus = QMLDownloadStatus::DOWNLOADED;
310}
311
312void CavokeClientController::createGameStart(const QString &roomName) {
313 qDebug() << "Now we are creating room with name:" << roomName;
314
315 createGameView.close();
316 showRoomView();
317 networkManager.createRoom(roomName);
318}
319
320void CavokeClientController::joinGameStart(const QString &inviteCode) {
321 qDebug() << "Now we are joining room with inviteCode:" << inviteCode;
322
323 joinGameView.close();
324 showRoomView();
325 networkManager.joinRoom(inviteCode);
326}
327
328void CavokeClientController::createSessionStart(const QString &gameId) {
329 qDebug() << "Now we are creating session with gameId:" << gameId;
330
331 roomView.close();
332 showSessionView();
333 networkManager.roomCreateSession(gameId);
334}
335
336void CavokeClientController::joinSessionStart(const QString &sessionId) {
337 qDebug() << "Now we are joining session with sessionId:" << sessionId;
338
339 roomView.close();
340 showSessionView();
341 networkManager.joinSession(sessionId);
342}
343
344void CavokeClientController::gotRoomInfo(const RoomInfo &roomInfo) {
345 qDebug() << "Got room info";
346
347 if (displacement != UserDisplacement::ROOM) {
348 return; // We are not in a room, actually
349 }
350
351 currentRoomInfo = roomInfo;
352
353 if (roomHostGuestStatus == HostGuestStatus::NOT_IN) {
354 networkManager.startRoomPolling();
355 }
356
357 if (currentRoomInfo.isHost &&
358 roomHostGuestStatus != HostGuestStatus::HOST) {
359 becomeRoomHost();
360 } else if (!currentRoomInfo.isHost &&
361 roomHostGuestStatus != HostGuestStatus::GUEST) {
362 becomeRoomGuest();
363 }
364
365 roomView.updateRoomInfo(currentRoomInfo);
366}
367
368void CavokeClientController::gotSessionInfo(const SessionInfo &sessionInfo) {
369 qDebug() << "Now we got session info";
370
371 if (displacement == UserDisplacement::ROOM) {
372 roomView.updateSessionInfo(sessionInfo);
373 return;
374 }
375
376 if (displacement != UserDisplacement::SESSION) {
377 return; // We are not in a session, actually
378 }
379
380 currentSessionInfo = sessionInfo;
381
382 if (hostGuestStatus == HostGuestStatus::NOT_IN) {
383 networkManager.startSessionPolling();
384 }
385
386 if (currentSessionInfo.isHost && hostGuestStatus != HostGuestStatus::HOST) {
387 becomeHost();
388 } else if (!currentSessionInfo.isHost &&
389 hostGuestStatus != HostGuestStatus::GUEST) {
390 becomeGuest();
391 }
392
393 if (qmlDownloadStatus == QMLDownloadStatus::NOT_STARTED) {
394 networkManager.getGamesClient(currentSessionInfo.game_id);
395 qmlDownloadStatus = QMLDownloadStatus::DOWNLOADING;
396 sessionView.updateStatus(SessionView::CreatingGameStatus::DOWNLOAD);
397 }
398
399 sessionView.updateSessionInfo(currentSessionInfo);
400
401 if (currentGameInfo.players_num == 0) {
402 networkManager.getGamesConfig(currentSessionInfo.game_id);
403 } else if (qmlDownloadStatus == QMLDownloadStatus::DOWNLOADED) {
404 sessionView.updateStatus(SessionView::CreatingGameStatus::DONE);
405 collectListOfAvailableRoles();
406 }
407}
408
409void CavokeClientController::gotCurrentGameInfo(const GameInfo &gameInfo) {
410 currentGameInfo = gameInfo;
411 emit setGameName(currentGameInfo.display_name);
412}
413
414void CavokeClientController::updateSettings(const QString &displayName,
415 const QString &host) {
416 settings.setValue(PLAYER_NICKNAME, displayName);
417 settings.setValue(NETWORK_HOST, host);
418 emit clearScreens();
419 networkManager.changeHost(QUrl::fromUserInput(host));
420 networkManager.changeName(displayName);
421}
422void CavokeClientController::collectListOfAvailableRoles() {
423 std::vector<bool> isFree(currentGameInfo.players_num, true);
424 int ourRole = -1;
425 QString userId = networkManager.getUserId();
426 for (const auto &player : currentSessionInfo.players) {
427 isFree[player.player_id] = false;
428 if (player.user.user_id == userId) {
429 ourRole = player.player_id;
430 }
431 }
432 if (ourRole == -1) {
433 return;
434 // I guess it is the bug when we have already left the session but
435 // still
436 // somehow made a request to get info about session
437 }
438 std::vector<Role> availableRoles;
439 availableRoles.emplace_back(currentGameInfo.role_names[ourRole],
440 ourRole); // Now first.
441 for (int i = 0; i < currentGameInfo.players_num; ++i) {
442 if (isFree[i]) {
443 availableRoles.emplace_back(currentGameInfo.role_names[i], i);
444 }
445 }
446 emit createdAvailableRolesList(availableRoles);
447}
448
449void CavokeClientController::becomeHost() {
450 hostGuestStatus = HostGuestStatus::HOST;
451 networkManager.startValidationPolling();
452}
453
454void CavokeClientController::becomeGuest() {
455 hostGuestStatus = HostGuestStatus::GUEST;
456 networkManager.stopValidationPolling();
457}
458
459void CavokeClientController::becomeRoomHost() {
460 roomHostGuestStatus = HostGuestStatus::HOST;
461}
462
463void CavokeClientController::becomeRoomGuest() {
464 roomHostGuestStatus = HostGuestStatus::GUEST;
465}
static AuthenticationManager & getInstance()
Singleton wrapper.