diff --git a/cockatrice/src/dlg_creategame.cpp b/cockatrice/src/dlg_creategame.cpp index 2c16889d..654cf97e 100644 --- a/cockatrice/src/dlg_creategame.cpp +++ b/cockatrice/src/dlg_creategame.cpp @@ -83,11 +83,13 @@ void DlgCreateGame::sharedCtor() spectatorsNeedPasswordCheckBox = new QCheckBox(tr("Spectators &need a password to watch")); spectatorsCanTalkCheckBox = new QCheckBox(tr("Spectators can &chat")); spectatorsSeeEverythingCheckBox = new QCheckBox(tr("Spectators can see &hands")); + createGameAsSpectatorCheckBox = new QCheckBox(tr("Create game as spectator")); QVBoxLayout *spectatorsLayout = new QVBoxLayout; spectatorsLayout->addWidget(spectatorsAllowedCheckBox); spectatorsLayout->addWidget(spectatorsNeedPasswordCheckBox); spectatorsLayout->addWidget(spectatorsCanTalkCheckBox); spectatorsLayout->addWidget(spectatorsSeeEverythingCheckBox); + spectatorsLayout->addWidget(createGameAsSpectatorCheckBox); spectatorsGroupBox = new QGroupBox(tr("Spectators")); spectatorsGroupBox->setLayout(spectatorsLayout); @@ -129,6 +131,7 @@ DlgCreateGame::DlgCreateGame(TabRoom *_room, const QMap &_gameType spectatorsNeedPasswordCheckBox->setChecked(SettingsCache::instance().getSpectatorsNeedPassword()); spectatorsCanTalkCheckBox->setChecked(SettingsCache::instance().getSpectatorsCanTalk()); spectatorsSeeEverythingCheckBox->setChecked(SettingsCache::instance().getSpectatorsCanSeeEverything()); + createGameAsSpectatorCheckBox->setChecked(SettingsCache::instance().getCreateGameAsSpectator()); if (!rememberGameSettings->isChecked()) { actReset(); @@ -159,6 +162,7 @@ DlgCreateGame::DlgCreateGame(const ServerInfo_Game &gameInfo, const QMapsetEnabled(false); spectatorsCanTalkCheckBox->setEnabled(false); spectatorsSeeEverythingCheckBox->setEnabled(false); + createGameAsSpectatorCheckBox->setEnabled(false); descriptionEdit->setText(QString::fromStdString(gameInfo.description())); maxPlayersEdit->setValue(gameInfo.max_players()); @@ -200,6 +204,7 @@ void DlgCreateGame::actReset() spectatorsNeedPasswordCheckBox->setChecked(false); spectatorsCanTalkCheckBox->setChecked(false); spectatorsSeeEverythingCheckBox->setChecked(false); + createGameAsSpectatorCheckBox->setChecked(false); QMapIterator gameTypeCheckBoxIterator(gameTypeCheckBoxes); while (gameTypeCheckBoxIterator.hasNext()) { @@ -226,6 +231,7 @@ void DlgCreateGame::actOK() cmd.set_spectators_can_talk(spectatorsCanTalkCheckBox->isChecked()); cmd.set_spectators_see_everything(spectatorsSeeEverythingCheckBox->isChecked()); cmd.set_join_as_judge(QApplication::keyboardModifiers() & Qt::ShiftModifier); + cmd.set_join_as_spectator(createGameAsSpectatorCheckBox->isChecked()); QString gameTypes = QString(); QMapIterator gameTypeCheckBoxIterator(gameTypeCheckBoxes); @@ -247,6 +253,7 @@ void DlgCreateGame::actOK() SettingsCache::instance().setSpectatorsNeedPassword(spectatorsNeedPasswordCheckBox->isChecked()); SettingsCache::instance().setSpectatorsCanTalk(spectatorsCanTalkCheckBox->isChecked()); SettingsCache::instance().setSpectatorsCanSeeEverything(spectatorsSeeEverythingCheckBox->isChecked()); + SettingsCache::instance().setCreateGameAsSpectator(createGameAsSpectatorCheckBox->isChecked()); SettingsCache::instance().setGameTypes(gameTypes); } PendingCommand *pend = room->prepareRoomCommand(cmd); diff --git a/cockatrice/src/dlg_creategame.h b/cockatrice/src/dlg_creategame.h index 08b9c8df..90e3ba2b 100644 --- a/cockatrice/src/dlg_creategame.h +++ b/cockatrice/src/dlg_creategame.h @@ -39,7 +39,7 @@ private: QSpinBox *maxPlayersEdit; QCheckBox *onlyBuddiesCheckBox, *onlyRegisteredCheckBox; QCheckBox *spectatorsAllowedCheckBox, *spectatorsNeedPasswordCheckBox, *spectatorsCanTalkCheckBox, - *spectatorsSeeEverythingCheckBox; + *spectatorsSeeEverythingCheckBox, *createGameAsSpectatorCheckBox; QDialogButtonBox *buttonBox; QPushButton *clearButton; QCheckBox *rememberGameSettings; diff --git a/cockatrice/src/gamesmodel.cpp b/cockatrice/src/gamesmodel.cpp index 1b7aefea..092bf18b 100644 --- a/cockatrice/src/gamesmodel.cpp +++ b/cockatrice/src/gamesmodel.cpp @@ -278,8 +278,6 @@ void GamesModel::updateGameList(const ServerInfo_Game &game) return; } } - if (game.player_count() <= 0) - return; beginInsertRows(QModelIndex(), gameList.size(), gameList.size()); gameList.append(game); endInsertRows(); diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 4906ae46..32ed3427 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -284,6 +284,7 @@ SettingsCache::SettingsCache() spectatorsNeedPassword = settings->value("game/spectatorsneedpassword", false).toBool(); spectatorsCanTalk = settings->value("game/spectatorscantalk", false).toBool(); spectatorsCanSeeEverything = settings->value("game/spectatorscanseeeverything", false).toBool(); + createGameAsSpectator = settings->value("game/creategameasspectator", false).toBool(); rememberGameSettings = settings->value("game/remembergamesettings", true).toBool(); clientID = settings->value("personal/clientid", CLIENT_INFO_NOT_SET).toString(); clientVersion = settings->value("personal/clientversion", CLIENT_INFO_NOT_SET).toString(); @@ -940,6 +941,12 @@ void SettingsCache::setSpectatorsCanSeeEverything(const bool _spectatorsCanSeeEv settings->setValue("game/spectatorscanseeeverything", spectatorsCanSeeEverything); } +void SettingsCache::setCreateGameAsSpectator(const bool _createGameAsSpectator) +{ + createGameAsSpectator = _createGameAsSpectator; + settings->setValue("game/creategameasspectator", createGameAsSpectator); +} + void SettingsCache::setRememberGameSettings(const bool _rememberGameSettings) { rememberGameSettings = _rememberGameSettings; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index cbb8c36b..d42cde18 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -124,6 +124,7 @@ private: bool spectatorsNeedPassword; bool spectatorsCanTalk; bool spectatorsCanSeeEverything; + bool createGameAsSpectator; int keepalive; void translateLegacySettings(); QString getSafeConfigPath(QString configEntry, QString defaultPath) const; @@ -394,6 +395,10 @@ public: { return spectatorsCanSeeEverything; } + bool getCreateGameAsSpectator() const + { + return createGameAsSpectator; + } bool getRememberGameSettings() const { return rememberGameSettings; @@ -525,6 +530,7 @@ public slots: void setSpectatorsNeedPassword(const bool _spectatorsNeedPassword); void setSpectatorsCanTalk(const bool _spectatorsCanTalk); void setSpectatorsCanSeeEverything(const bool _spectatorsCanSeeEverything); + void setCreateGameAsSpectator(const bool _createGameAsSpectator); void setRememberGameSettings(const bool _rememberGameSettings); void setNotifyAboutUpdate(int _notifyaboutupdate); void setNotifyAboutNewVersion(int _notifyaboutnewversion); diff --git a/cockatrice/src/user_context_menu.cpp b/cockatrice/src/user_context_menu.cpp index e32b0192..76172111 100644 --- a/cockatrice/src/user_context_menu.cpp +++ b/cockatrice/src/user_context_menu.cpp @@ -66,7 +66,7 @@ void UserContextMenu::retranslateUi() aBanHistory->setText(tr("View user's &ban history")); aPromoteToMod->setText(tr("&Promote user to moderator")); aDemoteFromMod->setText(tr("Dem&ote user from moderator")); - aPromoteToJudge->setText(tr("Promote user to &juge")); + aPromoteToJudge->setText(tr("Promote user to &judge")); aDemoteFromJudge->setText(tr("Demote user from judge")); } diff --git a/common/pb/room_commands.proto b/common/pb/room_commands.proto index d1dfde9f..57e27dff 100644 --- a/common/pb/room_commands.proto +++ b/common/pb/room_commands.proto @@ -37,6 +37,7 @@ message Command_CreateGame { optional bool spectators_see_everything = 9; repeated uint32 game_type_ids = 10; optional bool join_as_judge = 11; + optional bool join_as_spectator = 12; } message Command_JoinGame { diff --git a/common/server_game.cpp b/common/server_game.cpp index 63db0503..d8f4ad0a 100644 --- a/common/server_game.cpp +++ b/common/server_game.cpp @@ -93,9 +93,9 @@ Server_Game::~Server_Game() gameClosed = true; sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1)); - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - playerIterator.next().value()->prepareDestroy(); + for (Server_Player *player : players.values()) { + player->prepareDestroy(); + } players.clear(); room->removeGame(this); @@ -131,9 +131,9 @@ void Server_Game::storeGameInformation() for (int i = gameInfo.game_types_size() - 1; i >= 0; --i) gameTypes.append(allGameTypes[gameInfo.game_types(i)]); - QSetIterator playerIterator(allPlayersEver); - while (playerIterator.hasNext()) - replayMatchInfo->add_player_names(playerIterator.next().toStdString()); + for (auto playerName : allPlayersEver) { + replayMatchInfo->add_player_names(playerName.toStdString()); + } for (int i = 0; i < replayList.size(); ++i) { ServerInfo_Replay *replayInfo = replayMatchInfo->add_replay_list(); @@ -142,23 +142,21 @@ void Server_Game::storeGameInformation() replayInfo->set_duration(replayList[i]->duration_seconds()); } - QSet allUsersInGame = allPlayersEver + allSpectatorsEver; - QSetIterator allUsersIterator(allUsersInGame); - SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent); Server *server = room->getServer(); server->clientsLock.lockForRead(); - while (allUsersIterator.hasNext()) { - Server_AbstractUserInterface *userHandler = server->findUser(allUsersIterator.next()); + for (auto userName : allPlayersEver + allSpectatorsEver) { + Server_AbstractUserInterface *userHandler = server->findUser(userName); if (userHandler && server->getStoreReplaysEnabled()) userHandler->sendProtocolItem(*sessionEvent); } server->clientsLock.unlock(); delete sessionEvent; - if (server->getStoreReplaysEnabled()) + if (server->getStoreReplaysEnabled()) { server->getDatabaseInterface()->storeGameInformation(room->getName(), gameTypes, gameInfo, allPlayersEver, allSpectatorsEver, replayList); + } } void Server_Game::pingClockTimeout() @@ -175,20 +173,22 @@ void Server_Game::pingClockTimeout() if (player == nullptr) continue; - if (!player->getSpectator()) + if (!player->getSpectator()) { ++playerCount; + } int oldPingTime = player->getPingTime(); int newPingTime; - player->playerMutex.lock(); - if (player->getUserInterface()) { - newPingTime = player->getUserInterface()->getLastCommandTime(); - } else { - newPingTime = -1; + { + QMutexLocker playerMutexLocker(&player->playerMutex); + if (player->getUserInterface()) { + newPingTime = player->getUserInterface()->getLastCommandTime(); + } else { + newPingTime = -1; + } } - player->playerMutex.unlock(); - if ((newPingTime != -1) && !player->getSpectator()) { + if ((newPingTime != -1) && (!player->getSpectator() || player->getPlayerId() == hostId)) { allPlayersInactive = false; } @@ -217,11 +217,11 @@ int Server_Game::getPlayerCount() const { QMutexLocker locker(&gameMutex); - QMapIterator playerIterator(players); int result = 0; - while (playerIterator.hasNext()) - if (!playerIterator.next().value()->getSpectator()) + for (Server_Player *player : players.values()) { + if (!player->getSpectator()) ++result; + } return result; } @@ -229,11 +229,11 @@ int Server_Game::getSpectatorCount() const { QMutexLocker locker(&gameMutex); - QMapIterator playerIterator(players); int result = 0; - while (playerIterator.hasNext()) - if (playerIterator.next().value()->getSpectator()) + for (Server_Player *player : players.values()) { + if (player->getSpectator()) ++result; + } return result; } @@ -250,9 +250,9 @@ void Server_Game::createGameStateChangedEvent(Event_GameStateChanged *event, } else event->set_game_started(false); - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - playerIterator.next().value()->getInfo(event->add_player_list(), playerWhosAsking, omniscient, withUserInfo); + for (Server_Player *otherPlayer : players.values()) { + otherPlayer->getInfo(event->add_player_list(), playerWhosAsking, omniscient, withUserInfo); + } } void Server_Game::sendGameStateToPlayers() @@ -277,9 +277,7 @@ void Server_Game::sendGameStateToPlayers() createGameStateChangedEvent(&spectatorEvent, 0, false, false); // send game state info to clients according to their role in the game - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *player = playerIterator.next().value(); + for (Server_Player *player : players.values()) { GameEventContainer *gec; if (player->getSpectator()) gec = prepareGameEvent(spectatorEvent, -1); @@ -301,23 +299,17 @@ void Server_Game::doStartGameIfReady() if (getPlayerCount() < maxPlayers) return; - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - if (!p->getReadyStart() && !p->getSpectator()) + for (Server_Player *player : players.values()) { + if (!player->getReadyStart() && !player->getSpectator()) return; } - playerIterator.toFront(); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - if (!p->getSpectator()) - p->setupZones(); + for (Server_Player *player : players.values()) { + if (!player->getSpectator()) + player->setupZones(); } gameStarted = true; - playerIterator.toFront(); - while (playerIterator.hasNext()) { - Server_Player *player = playerIterator.next().value(); + for (Server_Player *player : players.values()) { player->setConceded(false); player->setReadyStart(false); } @@ -367,11 +359,9 @@ void Server_Game::stopGameIfFinished() { QMutexLocker locker(&gameMutex); - QMapIterator playerIterator(players); int playing = 0; - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - if (!p->getConceded() && !p->getSpectator()) + for (Server_Player *player : players.values()) { + if (!player->getConceded() && !player->getSpectator()) ++playing; } if (playing > 1) @@ -379,11 +369,9 @@ void Server_Game::stopGameIfFinished() gameStarted = false; - playerIterator.toFront(); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - p->clearZones(); - p->setConceded(false); + for (Server_Player *player : players.values()) { + player->clearZones(); + player->setConceded(false); } sendGameStateToPlayers(); @@ -404,11 +392,9 @@ Response::ResponseCode Server_Game::checkJoin(ServerInfo_User *user, bool asJudge) { Server_DatabaseInterface *databaseInterface = room->getServer()->getDatabaseInterface(); - { - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - if (playerIterator.next().value()->getUserInfo()->name() == user->name()) - return Response::RespContextError; + for (Server_Player *player : players.values()) { + if (player->getUserInfo()->name() == user->name()) + return Response::RespContextError; } if (asJudge && !(user->user_level() & ServerInfo_User::IsJudge)) { @@ -441,10 +427,10 @@ bool Server_Game::containsUser(const QString &userName) const { QMutexLocker locker(&gameMutex); - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - if (playerIterator.next().value()->getUserInfo()->name() == userName.toStdString()) + for (Server_Player *player : players.values()) { + if (player->getUserInfo()->name() == userName.toStdString()) return true; + } return false; } @@ -466,14 +452,18 @@ void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface, sendGameEventContainer(prepareGameEvent(joinEvent, -1)); const QString playerName = QString::fromStdString(newPlayer->getUserInfo()->name()); - if (spectator) - allSpectatorsEver.insert(playerName); - else - allPlayersEver.insert(playerName); players.insert(newPlayer->getPlayerId(), newPlayer); - if (newPlayer->getUserInfo()->name() == creatorInfo->name()) { - hostId = newPlayer->getPlayerId(); - sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); + if (spectator) { + allSpectatorsEver.insert(playerName); + } else { + allPlayersEver.insert(playerName); + + // if the original creator of the game joins, give them host status back + // FIXME: transferring host to spectators has side effects + if (newPlayer->getUserInfo()->name() == creatorInfo->name()) { + hostId = newPlayer->getPlayerId(); + sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); + } } if (broadcastUpdate) { @@ -513,26 +503,24 @@ void Server_Game::removePlayer(Server_Player *player, Event_Leave::LeaveReason r bool spectator = player->getSpectator(); player->prepareDestroy(); - if (!getPlayerCount()) { - gameClosed = true; - deleteLater(); - return; - } else if (!spectator) { - if (playerHost) { - int newHostId = -1; - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - if (!p->getSpectator()) { - newHostId = p->getPlayerId(); - break; - } - } - if (newHostId != -1) { - hostId = newHostId; - sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); + if (playerHost) { + int newHostId = -1; + for (Server_Player *otherPlayer : players.values()) { + if (!otherPlayer->getSpectator()) { + newHostId = otherPlayer->getPlayerId(); + break; } } + if (newHostId != -1) { + hostId = newHostId; + sendGameEventContainer(prepareGameEvent(Event_GameHostChanged(), hostId)); + } else { + gameClosed = true; + deleteLater(); + return; + } + } + if (!spectator) { stopGameIfFinished(); if (gameStarted && playerActive) nextTurn(); @@ -553,10 +541,8 @@ void Server_Game::removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_Play // Remove all arrows of other players pointing to the player being removed or to one of his cards. // Also remove all arrows starting at one of his cards. This is necessary since players can create // arrows that start at another person's cards. - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); - QList arrows = p->getArrows().values(); + for (Server_Player *otherPlayer : players.values()) { + QList arrows = otherPlayer->getArrows().values(); QList toDelete; for (int i = 0; i < arrows.size(); ++i) { Server_Arrow *a = arrows[i]; @@ -574,9 +560,9 @@ void Server_Game::removeArrowsRelatedToPlayer(GameEventStorage &ges, Server_Play for (int i = 0; i < toDelete.size(); ++i) { Event_DeleteArrow event; event.set_arrow_id(toDelete[i]->getId()); - ges.enqueueGameEvent(event, p->getPlayerId()); + ges.enqueueGameEvent(event, otherPlayer->getPlayerId()); - p->deleteArrow(toDelete[i]->getId()); + otherPlayer->deleteArrow(toDelete[i]->getId()); } } } @@ -586,15 +572,13 @@ void Server_Game::unattachCards(GameEventStorage &ges, Server_Player *player) QMutexLocker locker(&gameMutex); QMapIterator zoneIterator(player->getZones()); - while (zoneIterator.hasNext()) { - Server_CardZone *zone = zoneIterator.next().value(); - for (int i = 0; i < zone->getCards().size(); ++i) { - Server_Card *card = zone->getCards().at(i); - + for (Server_CardZone *zone : player->getZones()) { + for (Server_Card *card : zone->getCards()) { // Make a copy of the list because the original one gets modified during the loop QList attachedCards = card->getAttachedCards(); - for (int i = 0; i < attachedCards.size(); ++i) - attachedCards[i]->getZone()->getPlayer()->unattachCard(ges, attachedCards[i]); + for (Server_Card *attachedCard : attachedCards) { + attachedCard->getZone()->getPlayer()->unattachCard(ges, attachedCard); + } } } } @@ -633,9 +617,7 @@ void Server_Game::setActivePhase(int _activePhase) { QMutexLocker locker(&gameMutex); - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *player = playerIterator.next().value(); + for (Server_Player *player : players.values()) { QList toDelete = player->getArrows().values(); for (int i = 0; i < toDelete.size(); ++i) { Server_Arrow *a = toDelete[i]; @@ -705,10 +687,9 @@ void Server_Game::createGameJoinedEvent(Server_Player *player, ResponseContainer event2.set_active_player_id(activePlayer); event2.set_active_phase(activePhase); - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) - playerIterator.next().value()->getInfo(event2.add_player_list(), player, - player->getSpectator() && spectatorsSeeEverything, true); + for (Server_Player *player : players.values()) { + player->getInfo(event2.add_player_list(), player, player->getSpectator() && spectatorsSeeEverything, true); + } rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, prepareGameEvent(event2, -1)); } @@ -720,14 +701,12 @@ void Server_Game::sendGameEventContainer(GameEventContainer *cont, QMutexLocker locker(&gameMutex); cont->set_game_id(gameId); - QMapIterator playerIterator(players); - while (playerIterator.hasNext()) { - Server_Player *p = playerIterator.next().value(); + for (Server_Player *player : players.values()) { const bool playerPrivate = - (p->getPlayerId() == privatePlayerId) || (p->getSpectator() && spectatorsSeeEverything); + (player->getPlayerId() == privatePlayerId) || (player->getSpectator() && spectatorsSeeEverything); if ((recipients.testFlag(GameEventStorageItem::SendToPrivate) && playerPrivate) || (recipients.testFlag(GameEventStorageItem::SendToOthers) && !playerPrivate)) - p->sendGameEvent(*cont); + player->sendGameEvent(*cont); } if (recipients.testFlag(GameEventStorageItem::SendToPrivate)) { cont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); @@ -760,11 +739,12 @@ void Server_Game::getInfo(ServerInfo_Game &result) const result.set_room_id(room->getId()); result.set_game_id(gameId); - if (gameClosed) + if (gameClosed) { result.set_closed(true); - else { - for (int i = 0; i < gameTypes.size(); ++i) - result.add_game_types(gameTypes[i]); + } else { + for (auto type : gameTypes) { + result.add_game_types(type); + } result.set_max_players(getMaxPlayers()); result.set_description(getDescription().toStdString()); diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 9f8072ea..d723bd63 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -766,22 +766,28 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room if (gameId == -1) return Response::RespInternalError; - if (server->getMaxGamesPerUser() > 0) - if (room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= server->getMaxGamesPerUser()) - return Response::RespContextError; - - if (cmd.join_as_judge() && !server->permitCreateGameAsJudge() && - !(userInfo->user_level() & ServerInfo_User::IsJudge)) { + auto level = userInfo->user_level(); + bool isJudge = level & ServerInfo_User::IsJudge; + int maxGames = server->getMaxGamesPerUser(); + bool asJudge = cmd.join_as_judge(); + bool asSpectator = cmd.join_as_spectator(); + // allow judges to open games as spectator without limit to facilitate bots etc, -1 means no limit + if (!(isJudge && asJudge && asSpectator) && maxGames >= 0 && + room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= maxGames) { return Response::RespContextError; } - QList gameTypes; - for (int i = cmd.game_type_ids_size() - 1; i >= 0; --i) - gameTypes.append(cmd.game_type_ids(i)); + // if a non judge user tries to create a game as judge while not permitted, instead create a normal game + if (asJudge && !(server->permitCreateGameAsJudge() || isJudge)) { + asJudge = false; + } - QString description = QString::fromStdString(cmd.description()); - if (description.size() > 60) - description = description.left(60); + QList gameTypes; + for (int i = cmd.game_type_ids_size() - 1; i >= 0; --i) { // FIXME: why are these iterated in reverse? + gameTypes.append(cmd.game_type_ids(i)); + } + + QString description = QString::fromStdString(cmd.description()).left(60); // When server doesn't permit registered users to exist, do not honor only-reg setting bool onlyRegisteredUsers = cmd.only_registered() && (server->permitUnregisteredUsers()); @@ -790,7 +796,7 @@ Server_ProtocolHandler::cmdCreateGame(const Command_CreateGame &cmd, Server_Room cmd.only_buddies(), onlyRegisteredUsers, cmd.spectators_allowed(), cmd.spectators_need_password(), cmd.spectators_can_talk(), cmd.spectators_see_everything(), room); - game->addPlayer(this, rc, false, cmd.join_as_judge(), false); + game->addPlayer(this, rc, asSpectator, asJudge, false); room->addGame(game); return Response::RespOk; diff --git a/common/server_room.cpp b/common/server_room.cpp index 369b5f78..9569a15e 100644 --- a/common/server_room.cpp +++ b/common/server_room.cpp @@ -243,8 +243,8 @@ Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGam // server->roomsMutex is always locked. QReadLocker roomGamesLocker(&gamesLock); - Server_Game *g = games.value(cmd.game_id()); - if (!g) { + Server_Game *game = games.value(cmd.game_id()); + if (!game) { if (externalGames.contains(cmd.game_id())) { CommandContainer cont; cont.set_cmd_id(rc.getCmdId()); @@ -256,16 +256,18 @@ Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGam userInterface->getUserInfo()->session_id(), id); return Response::RespNothing; - } else + } else { return Response::RespNameNotFound; + } } - QMutexLocker gameLocker(&g->gameMutex); + QMutexLocker gameLocker(&game->gameMutex); - Response::ResponseCode result = g->checkJoin(userInterface->getUserInfo(), QString::fromStdString(cmd.password()), - cmd.spectator(), cmd.override_restrictions(), cmd.join_as_judge()); + Response::ResponseCode result = + game->checkJoin(userInterface->getUserInfo(), QString::fromStdString(cmd.password()), cmd.spectator(), + cmd.override_restrictions(), cmd.join_as_judge()); if (result == Response::RespOk) - g->addPlayer(userInterface, rc, cmd.spectator(), cmd.join_as_judge()); + game->addPlayer(userInterface, rc, cmd.spectator(), cmd.join_as_judge()); return result; } diff --git a/dbconverter/src/mocks.cpp b/dbconverter/src/mocks.cpp index f107d5d4..d1994af3 100644 --- a/dbconverter/src/mocks.cpp +++ b/dbconverter/src/mocks.cpp @@ -242,6 +242,9 @@ void SettingsCache::setSpectatorsCanTalk(const bool /* _spectatorsCanTalk */) void SettingsCache::setSpectatorsCanSeeEverything(const bool /* _spectatorsCanSeeEverything */) { } +void SettingsCache::setCreateGameAsSpectator(const bool /* _createGameAsSpectator */) +{ +} void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */) { } diff --git a/servatrice/servatrice.ini.example b/servatrice/servatrice.ini.example index 9da88779..c1b2b45d 100644 --- a/servatrice/servatrice.ini.example +++ b/servatrice/servatrice.ini.example @@ -344,7 +344,7 @@ max_message_size_per_interval=1000 ; Maximum number of messages in an interval before new messages gets dropped; default is 10 max_message_count_per_interval=10 -; Maximum number of games a single user can create; default is 5 +; Maximum number of games a single user can create; default is 5; set to -1 to disable; 0 disallows game creation max_games_per_user=5 ; Servatrice can avoid users from flooding games with large number of game commands in an interval of time. diff --git a/tests/carddatabase/mocks.cpp b/tests/carddatabase/mocks.cpp index 2343cc3e..b9a530f5 100644 --- a/tests/carddatabase/mocks.cpp +++ b/tests/carddatabase/mocks.cpp @@ -246,6 +246,9 @@ void SettingsCache::setSpectatorsCanTalk(const bool /* _spectatorsCanTalk */) void SettingsCache::setSpectatorsCanSeeEverything(const bool /* _spectatorsCanSeeEverything */) { } +void SettingsCache::setCreateGameAsSpectator(const bool /* _createGameAsSpectator */) +{ +} void SettingsCache::setRememberGameSettings(const bool /* _rememberGameSettings */) { }