void Server_AbstractUserInterface::joinPersistentGames(ResponseContainer &rc) { QList<PlayerReference> gamesToJoin = server->getPersistentPlayerReferences(QString::fromStdString(userInfo->name())); server->roomsLock.lockForRead(); for (int i = 0; i < gamesToJoin.size(); ++i) { const PlayerReference &pr = gamesToJoin.at(i); Server_Room *room = server->getRooms().value(pr.getRoomId()); if (!room) continue; QReadLocker roomGamesLocker(&room->gamesLock); Server_Game *game = room->getGames().value(pr.getGameId()); if (!game) continue; QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(pr.getPlayerId()); player->setUserInterface(this); playerAddedToGame(game->getGameId(), room->getId(), player->getPlayerId()); game->createGameJoinedEvent(player, rc, true); } server->roomsLock.unlock(); }
void Server::externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId) { // This function is always called from the main thread via signal/slot. try { ResponseContainer responseContainer(cont.cmd_id()); Response::ResponseCode finalResponseCode = Response::RespOk; QReadLocker roomsLocker(&roomsLock); Server_Room *room = rooms.value(cont.room_id()); if (!room) { qDebug() << "externalGameCommandContainerReceived: room id=" << cont.room_id() << "not found"; throw Response::RespNotInRoom; } QReadLocker roomGamesLocker(&room->gamesLock); Server_Game *game = room->getGames().value(cont.game_id()); if (!game) { qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found"; throw Response::RespNotInRoom; } QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(playerId); if (!player) { qDebug() << "externalGameCommandContainerReceived: player id=" << playerId << "not found"; throw Response::RespNotInRoom; } GameEventStorage ges; for (int i = cont.game_command_size() - 1; i >= 0; --i) { const GameCommand &sc = cont.game_command(i); qDebug() << "[ISL]" << QString::fromStdString(sc.ShortDebugString()); Response::ResponseCode resp = player->processGameCommand(sc, responseContainer, ges); if (resp != Response::RespOk) finalResponseCode = resp; } ges.sendToGame(game); if (finalResponseCode != Response::RespNothing) { player->playerMutex.lock(); player->getUserInterface()->sendResponseContainer(responseContainer, finalResponseCode); player->playerMutex.unlock(); } } catch (Response::ResponseCode code) { Response response; response.set_cmd_id(cont.cmd_id()); response.set_response_code(code); sendIsl_Response(response, serverId, sessionId); } }
int Server::getGamesCount() const { int result = 0; QReadLocker locker(&roomsLock); QMapIterator<int, Server_Room *> roomIterator(rooms); while (roomIterator.hasNext()) { Server_Room *room = roomIterator.next().value(); QReadLocker roomLocker(&room->gamesLock); result += room->getGames().size(); } return result; }
Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { if (authState == NotLoggedIn) return Response::RespLoginNeeded; QMap<int, QPair<int, int> > gameMap = getGames(); if (!gameMap.contains(cont.game_id())) return Response::RespNotInRoom; const QPair<int, int> roomIdAndPlayerId = gameMap.value(cont.game_id()); QReadLocker roomsLocker(&server->roomsLock); Server_Room *room = server->getRooms().value(roomIdAndPlayerId.first); if (!room) return Response::RespNotInRoom; QReadLocker roomGamesLocker(&room->gamesLock); Server_Game *game = room->getGames().value(cont.game_id()); if (!game) { if (room->getExternalGames().contains(cont.game_id())) { server->sendIsl_GameCommand(cont, room->getExternalGames().value(cont.game_id()).server_id(), userInfo->session_id(), roomIdAndPlayerId.first, roomIdAndPlayerId.second ); return Response::RespNothing; } return Response::RespNotInRoom; } QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(roomIdAndPlayerId.second); if (!player) return Response::RespNotInRoom; GameEventStorage ges; Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.game_command_size() - 1; i >= 0; --i) { const GameCommand &sc = cont.game_command(i); logDebugMessage(QString("game %1 player %2: ").arg(cont.game_id()).arg(roomIdAndPlayerId.second) + QString::fromStdString(sc.ShortDebugString())); Response::ResponseCode resp = player->processGameCommand(sc, rc, ges); if (resp != Response::RespOk) finalResponseCode = resp; } ges.sendToGame(game); return finalResponseCode; }
// This function must only be called from the thread this object lives in. // The thread must not hold any server locks when calling this (e.g. clientsLock, roomsLock). void Server_ProtocolHandler::prepareDestroy() { if (deleted) return; deleted = true; QMapIterator<int, Server_Room *> roomIterator(rooms); while (roomIterator.hasNext()) roomIterator.next().value()->removeClient(this); QMap<int, QPair<int, int> > tempGames(getGames()); server->roomsLock.lockForRead(); QMapIterator<int, QPair<int, int> > gameIterator(tempGames); while (gameIterator.hasNext()) { gameIterator.next(); Server_Room *r = server->getRooms().value(gameIterator.value().first); if (!r) continue; r->gamesLock.lockForRead(); Server_Game *g = r->getGames().value(gameIterator.key()); if (!g) { r->gamesLock.unlock(); continue; } g->gameMutex.lock(); Server_Player *p = g->getPlayers().value(gameIterator.value().second); if (!p) { g->gameMutex.unlock(); r->gamesLock.unlock(); continue; } p->disconnectClient(); g->gameMutex.unlock(); r->gamesLock.unlock(); } server->roomsLock.unlock(); server->removeClient(this); deleteLater(); }
void Server::externalUserLeft(const QString &userName) { // This function is always called from the main thread via signal/slot. clientsLock.lockForWrite(); Server_AbstractUserInterface *user = externalUsers.take(userName); externalUsersBySessionId.remove(user->getUserInfo()->session_id()); clientsLock.unlock(); QMap<int, QPair<int, int> > userGames(user->getGames()); QMapIterator<int, QPair<int, int> > userGamesIterator(userGames); roomsLock.lockForRead(); while (userGamesIterator.hasNext()) { userGamesIterator.next(); Server_Room *room = rooms.value(userGamesIterator.value().first); if (!room) continue; QReadLocker roomGamesLocker(&room->gamesLock); Server_Game *game = room->getGames().value(userGamesIterator.key()); if (!game) continue; QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(userGamesIterator.value().second); if (!player) continue; player->disconnectClient(); } roomsLock.unlock(); delete user; Event_UserLeft event; event.set_name(userName.toStdString()); SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); clientsLock.lockForRead(); for (int i = 0; i < clients.size(); ++i) if (clients[i]->getAcceptsUserListChanges()) clients[i]->sendProtocolItem(*se); clientsLock.unlock(); delete se; }
Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const CommandContainer &cont, ResponseContainer &rc) { static QList<GameCommand::GameCommandType> antifloodCommandsWhiteList = QList<GameCommand::GameCommandType>() // draw/undo card draw (example: drawing 10 cards one by one from the deck) << GameCommand::DRAW_CARDS << GameCommand::UNDO_DRAW // create, delete arrows (example: targeting with 10 cards during an attack) << GameCommand::CREATE_ARROW << GameCommand::DELETE_ARROW // set card attributes (example: tapping 10 cards at once) << GameCommand::SET_CARD_ATTR // increment / decrement counter (example: -10 life points one by one) << GameCommand::INC_COUNTER // mulling lots of hands in a row << GameCommand::MULLIGAN // allows a user to sideboard without receiving flooding message << GameCommand::MOVE_CARD; if (authState == NotLoggedIn) return Response::RespLoginNeeded; QMap<int, QPair<int, int> > gameMap = getGames(); if (!gameMap.contains(cont.game_id())) return Response::RespNotInRoom; const QPair<int, int> roomIdAndPlayerId = gameMap.value(cont.game_id()); QReadLocker roomsLocker(&server->roomsLock); Server_Room *room = server->getRooms().value(roomIdAndPlayerId.first); if (!room) return Response::RespNotInRoom; QReadLocker roomGamesLocker(&room->gamesLock); Server_Game *game = room->getGames().value(cont.game_id()); if (!game) { if (room->getExternalGames().contains(cont.game_id())) { server->sendIsl_GameCommand(cont, room->getExternalGames().value(cont.game_id()).server_id(), userInfo->session_id(), roomIdAndPlayerId.first, roomIdAndPlayerId.second ); return Response::RespNothing; } return Response::RespNotInRoom; } QMutexLocker gameLocker(&game->gameMutex); Server_Player *player = game->getPlayers().value(roomIdAndPlayerId.second); if (!player) return Response::RespNotInRoom; int commandCountingInterval = server->getCommandCountingInterval(); int maxCommandCountPerInterval = server->getMaxCommandCountPerInterval(); GameEventStorage ges; Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.game_command_size() - 1; i >= 0; --i) { const GameCommand &sc = cont.game_command(i); logDebugMessage(QString("game %1 player %2: ").arg(cont.game_id()).arg(roomIdAndPlayerId.second) + QString::fromStdString(sc.ShortDebugString())); if (commandCountingInterval > 0) { int totalCount = 0; if (commandCountOverTime.isEmpty()) commandCountOverTime.prepend(0); if(!antifloodCommandsWhiteList.contains((GameCommand::GameCommandType) getPbExtension(sc))) ++commandCountOverTime[0]; for (int i = 0; i < commandCountOverTime.size(); ++i) totalCount += commandCountOverTime[i]; if (totalCount > maxCommandCountPerInterval) return Response::RespChatFlood; } Response::ResponseCode resp = player->processGameCommand(sc, rc, ges); if (resp != Response::RespOk) finalResponseCode = resp; } ges.sendToGame(game); return finalResponseCode; }