diff --git a/common/pb/serverinfo_game.proto b/common/pb/serverinfo_game.proto index 6f0e6ed3..15444a05 100644 --- a/common/pb/serverinfo_game.proto +++ b/common/pb/serverinfo_game.proto @@ -16,4 +16,5 @@ message ServerInfo_Game { optional bool spectators_need_password = 13; optional uint32 spectators_count = 14; optional uint32 start_time = 15; + optional sint32 server_id = 16 [default = -1]; } diff --git a/common/server.cpp b/common/server.cpp index 6e4f1bdf..79cc633e 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -32,10 +32,11 @@ #include Server::Server(QObject *parent) - : QObject(parent), serverMutex(QMutex::Recursive), nextGameId(0), nextReplayId(0) + : QObject(parent), nextGameId(0), nextReplayId(0) { qRegisterMetaType("ServerInfo_Game"); qRegisterMetaType("ServerInfo_Room"); + qRegisterMetaType("ServerInfo_User"); } Server::~Server() @@ -44,22 +45,26 @@ Server::~Server() void Server::prepareDestroy() { - QMutexLocker locker(&serverMutex); - - while (!clients.isEmpty()) - delete clients.takeFirst(); - + roomsLock.lockForWrite(); QMapIterator roomIterator(rooms); while (roomIterator.hasNext()) delete roomIterator.next().value(); + rooms.clear(); + roomsLock.unlock(); + + clientsLock.lockForWrite(); + while (!clients.isEmpty()) + delete clients.takeFirst(); + clientsLock.unlock(); } AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reasonStr) { - QMutexLocker locker(&serverMutex); if (name.size() > 35) name = name.left(35); + QWriteLocker locker(&clientsLock); + AuthenticationResult authState = checkUserPassword(session, name, password, reasonStr); if ((authState == NotLoggedIn) || (authState == UserIsBanned)) return authState; @@ -113,13 +118,13 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString void Server::addClient(Server_ProtocolHandler *client) { - QMutexLocker locker(&serverMutex); + QWriteLocker locker(&clientsLock); clients << client; } void Server::removeClient(Server_ProtocolHandler *client) { - QMutexLocker locker(&serverMutex); + QWriteLocker locker(&clientsLock); clients.removeAt(clients.indexOf(client)); ServerInfo_User *data = client->getUserInfo(); if (data) { @@ -142,34 +147,90 @@ void Server::removeClient(Server_ProtocolHandler *client) qDebug() << "Server::removeClient:" << clients.size() << "clients; " << users.size() << "users left"; } -void Server::externalUserJoined(ServerInfo_User userInfo) +void Server::externalUserJoined(const ServerInfo_User &userInfo) { // This function is always called from the main thread via signal/slot. - QMutexLocker locker(&serverMutex); + QWriteLocker locker(&clientsLock); externalUsers.insert(QString::fromStdString(userInfo.name()), new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo))); Event_UserJoined event; event.mutable_user_info()->CopyFrom(userInfo); + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); for (int i = 0; i < clients.size(); ++i) if (clients[i]->getAcceptsUserListChanges()) clients[i]->sendProtocolItem(*se); + delete se; } -void Server::externalUserLeft(QString userName) +void Server::externalUserLeft(const QString &userName) { // This function is always called from the main thread via signal/slot. - QMutexLocker locker(&serverMutex); + QWriteLocker locker(&clientsLock); delete externalUsers.take(userName); Event_UserLeft event; event.set_name(userName.toStdString()); + SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); for (int i = 0; i < clients.size(); ++i) if (clients[i]->getAcceptsUserListChanges()) clients[i]->sendProtocolItem(*se); + delete se; +} + +void Server::externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo) +{ + // This function is always called from the main thread via signal/slot. + QReadLocker locker(&roomsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalRoomUserJoined: room id=" << roomId << "not found"; + return; + } + room->addExternalUser(userInfo); +} + +void Server::externalRoomUserLeft(int roomId, const QString &userName) +{ + // This function is always called from the main thread via signal/slot. + QReadLocker locker(&roomsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalRoomUserLeft: room id=" << roomId << "not found"; + return; + } + room->removeExternalUser(userName); +} + +void Server::externalRoomSay(int roomId, const QString &userName, const QString &message) +{ + // This function is always called from the main thread via signal/slot. + QReadLocker locker(&roomsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalRoomSay: room id=" << roomId << "not found"; + return; + } + room->say(userName, message, false); +} + +void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &gameInfo) +{ + // This function is always called from the main thread via signal/slot. + QReadLocker locker(&roomsLock); + + Server_Room *room = rooms.value(roomId); + if (!room) { + qDebug() << "externalRoomGameListChanged: room id=" << roomId << "not found"; + return; + } + room->updateExternalGameList(gameInfo); } void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl) @@ -179,11 +240,11 @@ void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event); - serverMutex.lock(); + clientsLock.lockForRead(); for (int i = 0; i < clients.size(); ++i) if (clients[i]->getAcceptsRoomListChanges()) clients[i]->sendProtocolItem(*se); - serverMutex.unlock(); + clientsLock.unlock(); if (sendToIsl) sendIslMessage(*se); @@ -193,25 +254,26 @@ void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl void Server::addRoom(Server_Room *newRoom) { - QMutexLocker locker(&serverMutex); + QWriteLocker locker(&roomsLock); + qDebug() << "Adding room: ID=" << newRoom->getId() << "name=" << newRoom->getName(); rooms.insert(newRoom->getId(), newRoom); connect(newRoom, SIGNAL(roomInfoChanged(ServerInfo_Room)), this, SLOT(broadcastRoomUpdate(const ServerInfo_Room &)), Qt::QueuedConnection); } int Server::getUsersCount() const { - QMutexLocker locker(&serverMutex); + QReadLocker locker(&clientsLock); return users.size(); } int Server::getGamesCount() const { int result = 0; - QMutexLocker locker(&serverMutex); + QReadLocker locker(&roomsLock); QMapIterator roomIterator(rooms); while (roomIterator.hasNext()) { Server_Room *room = roomIterator.next().value(); - QMutexLocker roomLocker(&room->roomMutex); + QMutexLocker roomLocker(&room->gamesMutex); result += room->getGames().size(); } return result; diff --git a/common/server.h b/common/server.h index ef53d077..41467625 100644 --- a/common/server.h +++ b/common/server.h @@ -5,8 +5,11 @@ #include #include #include +#include +#include #include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_room.pb.h" +#include "pb/serverinfo_game.pb.h" class Server_Game; class Server_Room; @@ -29,7 +32,7 @@ signals: private slots: void broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl = false); public: - mutable QMutex serverMutex; + mutable QReadWriteLock clientsLock, roomsLock; Server(QObject *parent = 0); ~Server(); AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason); @@ -67,9 +70,12 @@ public: void removeExternalUser(const QString &userName); const QMap &getExternalUsers() const { return externalUsers; } protected slots: - void externalUserJoined(ServerInfo_User userInfo); - void externalUserLeft(QString userName); - void externalRoomUpdated(ServerInfo_Room roomInfo); + void externalUserJoined(const ServerInfo_User &userInfo); + void externalUserLeft(const QString &userName); + void externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo); + void externalRoomUserLeft(int roomId, const QString &userName); + void externalRoomSay(int roomId, const QString &userName, const QString &message); + void externalRoomGameListChanged(int roomId, const ServerInfo_Game &gameInfo); protected: void prepareDestroy(); QList clients; @@ -95,4 +101,8 @@ protected: virtual void doSendIslMessage(const IslMessage &msg, int serverId) { } }; +Q_DECLARE_METATYPE(ServerInfo_User) +Q_DECLARE_METATYPE(ServerInfo_Room) +Q_DECLARE_METATYPE(ServerInfo_Game) + #endif diff --git a/common/server_game.cpp b/common/server_game.cpp index 32d5c0ba..4199ae5c 100644 --- a/common/server_game.cpp +++ b/common/server_game.cpp @@ -65,7 +65,7 @@ Server_Game::Server_Game(Server_ProtocolHandler *_creator, int _gameId, const QS Server_Game::~Server_Game() { - room->roomMutex.lock(); + room->gamesMutex.lock(); gameMutex.lock(); sendGameEventContainer(prepareGameEvent(Event_GameClosed(), -1)); @@ -80,7 +80,7 @@ Server_Game::~Server_Game() creatorInfo = 0; gameMutex.unlock(); - room->roomMutex.unlock(); + room->gamesMutex.unlock(); currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); replayList.append(currentReplay); @@ -371,7 +371,6 @@ Server_Player *Server_Game::addPlayer(Server_ProtocolHandler *handler, bool spec void Server_Game::removePlayer(Server_Player *player) { - QMutexLocker roomLocker(&room->roomMutex); QMutexLocker locker(&gameMutex); players.remove(player->getPlayerId()); @@ -461,7 +460,6 @@ void Server_Game::unattachCards(GameEventStorage &ges, Server_Player *player) bool Server_Game::kickPlayer(int playerId) { - QMutexLocker roomLocker(&room->roomMutex); QMutexLocker locker(&gameMutex); Server_Player *playerToKick = players.value(playerId); diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 0bb787c5..baede47b 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -89,7 +89,7 @@ #include Server_ProtocolHandler::Server_ProtocolHandler(Server *_server, QObject *parent) - : QObject(parent), Server_AbstractUserInterface(_server), authState(NotLoggedIn), acceptsUserListChanges(false), acceptsRoomListChanges(false), sessionId(-1), timeRunning(0), lastDataReceived(0), gameListMutex(QMutex::Recursive) + : QObject(parent), Server_AbstractUserInterface(_server), authState(NotLoggedIn), acceptsUserListChanges(false), acceptsRoomListChanges(false), sessionId(-1), timeRunning(0), lastDataReceived(0) { connect(server, SIGNAL(pingClockTimeout()), this, SLOT(pingClockTimeout())); } @@ -103,7 +103,6 @@ Server_ProtocolHandler::~Server_ProtocolHandler() // finalization. void Server_ProtocolHandler::prepareDestroy() { - QMutexLocker locker(&server->serverMutex); qDebug("Server_ProtocolHandler::prepareDestroy"); server->removeClient(this); @@ -113,20 +112,40 @@ void Server_ProtocolHandler::prepareDestroy() roomIterator.next().value()->removeClient(this); gameListMutex.lock(); - QMapIterator > gameIterator(games); + QMap > tempGames(games); + gameListMutex.unlock(); + + server->roomsLock.lockForRead(); + QMapIterator > gameIterator(tempGames); while (gameIterator.hasNext()) { gameIterator.next(); - Server_Game *g = gameIterator.value().first; - Server_Player *p = gameIterator.value().second; + Server_Room *r = server->getRooms().value(gameIterator.value().first); + if (!r) + continue; + r->gamesMutex.lock(); + Server_Game *g = r->getGames().value(gameIterator.key()); + if (!g) { + r->gamesMutex.unlock(); + continue; + } g->gameMutex.lock(); + Server_Player *p = g->getPlayer(gameIterator.value().second); + if (!p) { + g->gameMutex.unlock(); + r->gamesMutex.unlock(); + continue; + } + if ((authState == UnknownUser) || p->getSpectator()) g->removePlayer(p); else p->setProtocolHandler(0); + g->gameMutex.unlock(); + r->gamesMutex.unlock(); } - gameListMutex.unlock(); + server->roomsLock.unlock(); } void Server_ProtocolHandler::playerRemovedFromGame(Server_Game *game) @@ -214,12 +233,11 @@ Response::ResponseCode Server_ProtocolHandler::processRoomCommandContainer(const if (authState == NotLoggedIn) return Response::RespLoginNeeded; + QReadLocker locker(&server->roomsLock); Server_Room *room = rooms.value(cont.room_id(), 0); if (!room) return Response::RespNotInRoom; - QMutexLocker locker(&room->roomMutex); - Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.room_command_size() - 1; i >= 0; --i) { Response::ResponseCode resp = Response::RespInvalidCommand; @@ -245,16 +263,34 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const gameListMutex.lock(); if (!games.contains(cont.game_id())) { - qDebug() << "invalid game"; + gameListMutex.unlock(); return Response::RespNotInRoom; } - QPair gamePair = games.value(cont.game_id()); - Server_Game *game = gamePair.first; - Server_Player *player = gamePair.second; - - QMutexLocker locker(&game->gameMutex); + const QPair roomIdAndPlayerId = games.value(cont.game_id()); gameListMutex.unlock(); - + + server->roomsLock.lockForRead(); + Server_Room *room = server->getRooms().value(roomIdAndPlayerId.first); + if (!room) { + server->roomsLock.unlock(); + return Response::RespNotInRoom; + } + room->gamesMutex.lock(); + Server_Game *game = room->getGames().value(cont.game_id()); + if (!game) { + room->gamesMutex.unlock(); + server->roomsLock.unlock(); + return Response::RespNotInRoom; + } + game->gameMutex.lock(); + Server_Player *player = game->getPlayer(roomIdAndPlayerId.second); + if (!player) { + game->gameMutex.unlock(); + room->gamesMutex.unlock(); + server->roomsLock.unlock(); + return Response::RespNotInRoom; + } + GameEventStorage ges; Response::ResponseCode finalResponseCode = Response::RespOk; for (int i = cont.game_command_size() - 1; i >= 0; --i) { @@ -298,6 +334,11 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const finalResponseCode = resp; } ges.sendToGame(game); + + game->gameMutex.unlock(); + room->gamesMutex.unlock(); + server->roomsLock.unlock(); + return finalResponseCode; } @@ -400,13 +441,6 @@ void Server_ProtocolHandler::pingClockTimeout() ++timeRunning; } -QPair Server_ProtocolHandler::getGame(int gameId) const -{ - if (games.contains(gameId)) - return games.value(gameId); - return QPair(0, 0); -} - Response::ResponseCode Server_ProtocolHandler::cmdPing(const Command_Ping & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespOk; @@ -449,13 +483,13 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd re->add_ignore_list()->CopyFrom(ignoreIterator.next().value()); } - server->serverMutex.lock(); + server->roomsLock.lockForRead(); QMapIterator roomIterator(server->getRooms()); - QMutexLocker gameListLocker(&gameListMutex); + gameListMutex.lock(); while (roomIterator.hasNext()) { Server_Room *room = roomIterator.next().value(); - room->roomMutex.lock(); + room->gamesMutex.lock(); QMapIterator gameIterator(room->getGames()); while (gameIterator.hasNext()) { Server_Game *game = gameIterator.next().value(); @@ -464,7 +498,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd for (int j = 0; j < gamePlayers.size(); ++j) if (gamePlayers[j]->getUserInfo()->name() == userInfo->name()) { gamePlayers[j]->setProtocolHandler(this); - games.insert(game->getGameId(), QPair(game, gamePlayers[j])); + games.insert(game->getGameId(), QPair(room->getId(), gamePlayers[j]->getPlayerId())); Event_GameJoined event1; event1.set_game_id(game->getGameId()); @@ -478,7 +512,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd rc.enqueuePostResponseItem(ServerMessage::SESSION_EVENT, prepareSessionEvent(event1)); Event_GameStateChanged event2; - QListIterator gameStateIterator(game->getGameState(gamePlayers[j])); + QListIterator gameStateIterator(game->getGameState(gamePlayers[j], gamePlayers[j]->getSpectator() && game->getSpectatorsSeeEverything(), true)); while (gameStateIterator.hasNext()) event2.add_player_list()->CopyFrom(gameStateIterator.next()); event2.set_seconds_elapsed(game->getSecondsElapsed()); @@ -490,9 +524,10 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd break; } } - room->roomMutex.unlock(); + room->gamesMutex.unlock(); } - server->serverMutex.unlock(); + gameListMutex.unlock(); + server->roomsLock.unlock(); rc.setResponseExtension(re); return Response::RespOk; @@ -503,7 +538,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message if (authState == NotLoggedIn) return Response::RespLoginNeeded; - QMutexLocker locker(&server->serverMutex); + QReadLocker locker(&server->clientsLock); QString receiver = QString::fromStdString(cmd.user_name()); Server_AbstractUserInterface *userInterface = server->getUsers().value(receiver); @@ -532,22 +567,24 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_G if (authState == NotLoggedIn) return Response::RespLoginNeeded; - server->serverMutex.lock(); + server->clientsLock.lockForRead(); if (!server->getUsers().contains(QString::fromStdString(cmd.user_name()))) return Response::RespNameNotFound; + server->clientsLock.unlock(); Response_GetGamesOfUser *re = new Response_GetGamesOfUser; + server->roomsLock.lockForRead(); QMapIterator roomIterator(server->getRooms()); while (roomIterator.hasNext()) { Server_Room *room = roomIterator.next().value(); - room->roomMutex.lock(); + room->gamesMutex.lock(); re->add_room_list()->CopyFrom(room->getInfo(false, true)); QListIterator gameIterator(room->getGamesOfUser(QString::fromStdString(cmd.user_name()))); while (gameIterator.hasNext()) re->add_game_list()->CopyFrom(gameIterator.next()); - room->roomMutex.unlock(); + room->gamesMutex.unlock(); } - server->serverMutex.unlock(); + server->roomsLock.unlock(); rc.setResponseExtension(re); return Response::RespOk; @@ -564,7 +601,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetU re->mutable_user_info()->CopyFrom(*userInfo); else { - QMutexLocker locker(&server->serverMutex); + QReadLocker locker(&server->clientsLock); ServerInfo_User_Container *infoSource; if (server->getUsers().contains(userName)) @@ -604,12 +641,11 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinRoom(const Command_JoinRoo if (rooms.contains(cmd.room_id())) return Response::RespContextError; + QReadLocker serverLocker(&server->roomsLock); Server_Room *r = server->getRooms().value(cmd.room_id(), 0); if (!r) return Response::RespNameNotFound; - QMutexLocker serverLocker(&server->serverMutex); - QMutexLocker roomLocker(&r->roomMutex); r->addClient(this); rooms.insert(r->getId(), r); @@ -630,14 +666,14 @@ Response::ResponseCode Server_ProtocolHandler::cmdListUsers(const Command_ListUs return Response::RespLoginNeeded; Response_ListUsers *re = new Response_ListUsers; - server->serverMutex.lock(); + server->clientsLock.lockForRead(); QMapIterator userIterator = server->getUsers(); while (userIterator.hasNext()) re->add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false)); QMapIterator extIterator = server->getExternalUsers(); while (extIterator.hasNext()) re->add_user_list()->CopyFrom(extIterator.next().value()->copyUserInfo(false)); - server->serverMutex.unlock(); + server->clientsLock.unlock(); acceptsUserListChanges = true; @@ -675,7 +711,7 @@ Response::ResponseCode Server_ProtocolHandler::cmdRoomSay(const Command_RoomSay } msg.replace(QChar('\n'), QChar(' ')); - room->say(this, msg); + room->say(QString::fromStdString(userInfo->name()), msg); return Response::RespOk; } @@ -683,7 +719,9 @@ Response::ResponseCode Server_ProtocolHandler::cmdCreateGame(const Command_Creat { if (authState == NotLoggedIn) return Response::RespLoginNeeded; - + + QMutexLocker roomLocker(&room->gamesMutex); + if (server->getMaxGamesPerUser() > 0) if (room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= server->getMaxGamesPerUser()) return Response::RespContextError; @@ -699,13 +737,14 @@ Response::ResponseCode Server_ProtocolHandler::cmdCreateGame(const Command_Creat Server_Game *game = new Server_Game(this, server->getNextGameId(), description, QString::fromStdString(cmd.password()), cmd.max_players(), gameTypes, cmd.only_buddies(), cmd.only_registered(), cmd.spectators_allowed(), cmd.spectators_need_password(), cmd.spectators_can_talk(), cmd.spectators_see_everything(), room); game->moveToThread(room->thread()); - QMutexLocker gameListLocker(&gameListMutex); game->gameMutex.lock(); room->addGame(game); Server_Player *creator = game->getPlayers().values().first(); - games.insert(game->getGameId(), QPair(game, creator)); + gameListMutex.lock(); + games.insert(game->getGameId(), QPair(room->getId(), creator->getPlayerId())); + gameListMutex.unlock(); Event_GameJoined event1; event1.set_game_id(game->getGameId()); @@ -738,21 +777,25 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinGame(const Command_JoinGam if (authState == NotLoggedIn) return Response::RespLoginNeeded; - QMutexLocker gameListLocker(&gameListMutex); + QMutexLocker locker(&gameListMutex); if (games.contains(cmd.game_id())) return Response::RespContextError; + room->gamesMutex.lock(); Server_Game *g = room->getGames().value(cmd.game_id()); - if (!g) + if (!g) { + room->gamesMutex.unlock(); return Response::RespNameNotFound; + } - QMutexLocker locker(&g->gameMutex); + g->gameMutex.lock(); Response::ResponseCode result = g->checkJoin(userInfo, QString::fromStdString(cmd.password()), cmd.spectator(), cmd.override_restrictions()); if (result == Response::RespOk) { Server_Player *player = g->addPlayer(this, cmd.spectator()); - games.insert(cmd.game_id(), QPair(g, player)); + + games.insert(cmd.game_id(), QPair(room->getId(), player->getPlayerId())); Event_GameJoined event1; event1.set_game_id(g->getGameId()); @@ -775,6 +818,10 @@ Response::ResponseCode Server_ProtocolHandler::cmdJoinGame(const Command_JoinGam event2.set_active_phase(g->getActivePhase()); rc.enqueuePostResponseItem(ServerMessage::GAME_EVENT_CONTAINER, g->prepareGameEvent(event2, -1)); } + + g->gameMutex.unlock(); + room->gamesMutex.unlock(); + return result; } diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 0ea586a5..f8dd5c08 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -88,11 +88,9 @@ class Command_ShutdownServer; class Server_ProtocolHandler : public QObject, public Server_AbstractUserInterface { Q_OBJECT protected: - QMap > games; + QMap > games; // gameId -> (roomId, playerId) QMap rooms; - QPair getGame(int gameId) const; - AuthenticationResult authState; bool acceptsUserListChanges; bool acceptsRoomListChanges; @@ -100,6 +98,8 @@ protected: void prepareDestroy(); int sessionId; private: + QMutex gameListMutex; + QList messageSizeOverTime, messageCountOverTime; int timeRunning, lastDataReceived; QTimer *pingClock; @@ -175,8 +175,6 @@ private slots: signals: void logDebugMessage(const QString &message, Server_ProtocolHandler *session); public: - QMutex gameListMutex; - Server_ProtocolHandler(Server *_server, QObject *parent = 0); ~Server_ProtocolHandler(); void playerRemovedFromGame(Server_Game *game); diff --git a/common/server_room.cpp b/common/server_room.cpp index 1d5c0578..dbb52725 100644 --- a/common/server_room.cpp +++ b/common/server_room.cpp @@ -10,22 +10,25 @@ #include Server_Room::Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent) - : QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), roomMutex(QMutex::Recursive) + : QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), gamesMutex(QMutex::Recursive) { connect(this, SIGNAL(gameListChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)), Qt::QueuedConnection); } Server_Room::~Server_Room() { - QMutexLocker locker(&roomMutex); qDebug("Server_Room destructor"); + gamesMutex.lock(); const QList gameList = games.values(); for (int i = 0; i < gameList.size(); ++i) delete gameList[i]; games.clear(); + gamesMutex.unlock(); + usersLock.lockForWrite(); userList.clear(); + usersLock.unlock(); } Server *Server_Room::getServer() const @@ -35,12 +38,8 @@ Server *Server_Room::getServer() const ServerInfo_Room Server_Room::getInfo(bool complete, bool showGameTypes, bool updating, bool includeExternalData) const { - QMutexLocker locker(&roomMutex); - ServerInfo_Room result; result.set_room_id(id); - result.set_game_count(games.size() + externalGames.size()); - result.set_player_count(userList.size() + externalUsers.size()); if (!updating) { result.set_name(name.toStdString()); @@ -48,6 +47,8 @@ ServerInfo_Room Server_Room::getInfo(bool complete, bool showGameTypes, bool upd result.set_auto_join(autoJoin); } + gamesMutex.lock(); + result.set_game_count(games.size() + externalGames.size()); if (complete) { QMapIterator gameIterator(games); while (gameIterator.hasNext()) @@ -57,7 +58,12 @@ ServerInfo_Room Server_Room::getInfo(bool complete, bool showGameTypes, bool upd while (externalGameIterator.hasNext()) result.add_game_list()->CopyFrom(externalGameIterator.next().value()); } - + } + gamesMutex.unlock(); + + usersLock.lockForRead(); + result.set_player_count(userList.size() + externalUsers.size()); + if (complete) { for (int i = 0; i < userList.size(); ++i) result.add_user_list()->CopyFrom(userList[i]->copyUserInfo(false)); if (includeExternalData) { @@ -66,6 +72,8 @@ ServerInfo_Room Server_Room::getInfo(bool complete, bool showGameTypes, bool upd result.add_user_list()->CopyFrom(externalUserIterator.next().value().copyUserInfo(false)); } } + usersLock.unlock(); + if (complete || showGameTypes) for (int i = 0; i < gameTypes.size(); ++i) { ServerInfo_GameType *gameTypeInfo = result.add_gametype_list(); @@ -90,18 +98,18 @@ void Server_Room::addClient(Server_ProtocolHandler *client) event.mutable_user_info()->CopyFrom(client->copyUserInfo(false)); sendRoomEvent(prepareRoomEvent(event)); - roomMutex.lock(); + usersLock.lockForWrite(); userList.append(client); - roomMutex.unlock(); + usersLock.unlock(); emit roomInfoChanged(getInfo(false, false, true)); } void Server_Room::removeClient(Server_ProtocolHandler *client) { - roomMutex.lock(); + usersLock.lockForWrite(); userList.removeAt(userList.indexOf(client)); - roomMutex.unlock(); + usersLock.unlock(); Event_LeaveRoom event; event.set_name(client->getUserInfo()->name()); @@ -112,23 +120,25 @@ void Server_Room::removeClient(Server_ProtocolHandler *client) void Server_Room::addExternalUser(const ServerInfo_User &userInfo) { + // This function is always called from the Server thread with server->roomsMutex locked. ServerInfo_User_Container userInfoContainer(userInfo); Event_JoinRoom event; event.mutable_user_info()->CopyFrom(userInfoContainer.copyUserInfo(false)); sendRoomEvent(prepareRoomEvent(event), false); - roomMutex.lock(); + usersLock.lockForWrite(); externalUsers.insert(QString::fromStdString(userInfo.name()), userInfoContainer); - roomMutex.unlock(); + usersLock.unlock(); emit roomInfoChanged(getInfo(false, false, true)); } void Server_Room::removeExternalUser(const QString &name) { - roomMutex.lock(); + // This function is always called from the Server thread with server->roomsMutex locked. + usersLock.lockForWrite(); externalUsers.remove(name); - roomMutex.unlock(); + usersLock.unlock(); Event_LeaveRoom event; event.set_name(name.toStdString()); @@ -137,20 +147,34 @@ void Server_Room::removeExternalUser(const QString &name) emit roomInfoChanged(getInfo(false, false, true)); } -void Server_Room::say(Server_ProtocolHandler *client, const QString &s) +void Server_Room::updateExternalGameList(const ServerInfo_Game &gameInfo) +{ + // This function is always called from the Server thread with server->roomsMutex locked. + gamesMutex.lock(); + if (!gameInfo.has_player_count() && externalGames.contains(gameInfo.game_id())) + externalGames.remove(gameInfo.game_id()); + else + externalGames.insert(gameInfo.game_id(), gameInfo); + gamesMutex.unlock(); + + broadcastGameListUpdate(gameInfo, false); + emit roomInfoChanged(getInfo(false, false, true)); +} + +void Server_Room::say(const QString &userName, const QString &s, bool sendToIsl) { Event_RoomSay event; - event.set_name(client->getUserInfo()->name()); + event.set_name(userName.toStdString()); event.set_message(s.toStdString()); - sendRoomEvent(prepareRoomEvent(event)); + sendRoomEvent(prepareRoomEvent(event), sendToIsl); } void Server_Room::sendRoomEvent(RoomEvent *event, bool sendToIsl) { - roomMutex.lock(); + usersLock.lockForRead(); for (int i = 0; i < userList.size(); ++i) userList[i]->sendProtocolItem(*event); - roomMutex.unlock(); + usersLock.unlock(); if (sendToIsl) static_cast(parent())->sendIslMessage(*event); @@ -158,16 +182,16 @@ void Server_Room::sendRoomEvent(RoomEvent *event, bool sendToIsl) delete event; } -void Server_Room::broadcastGameListUpdate(ServerInfo_Game gameInfo) +void Server_Room::broadcastGameListUpdate(ServerInfo_Game gameInfo, bool sendToIsl) { Event_ListGames event; event.add_game_list()->CopyFrom(gameInfo); - sendRoomEvent(prepareRoomEvent(event)); + sendRoomEvent(prepareRoomEvent(event), sendToIsl); } void Server_Room::addGame(Server_Game *game) { - // Lock roomMutex and gameMutex before calling this + // Lock gamesMutex and gameMutex before calling this connect(game, SIGNAL(gameInfoChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game))); games.insert(game->getGameId(), game); @@ -177,7 +201,7 @@ void Server_Room::addGame(Server_Game *game) void Server_Room::removeGame(Server_Game *game) { - // No need to lock roomMutex or gameMutex. This method is only + // No need to lock gamesMutex or gameMutex. This method is only // called from ~Server_Game, which locks both mutexes anyway beforehand. disconnect(game, 0, this, 0); @@ -189,7 +213,7 @@ void Server_Room::removeGame(Server_Game *game) int Server_Room::getGamesCreatedByUser(const QString &userName) const { - QMutexLocker locker(&roomMutex); + QMutexLocker locker(&gamesMutex); QMapIterator gamesIterator(games); int result = 0; @@ -201,6 +225,8 @@ int Server_Room::getGamesCreatedByUser(const QString &userName) const QList Server_Room::getGamesOfUser(const QString &userName) const { + QMutexLocker locker(&gamesMutex); + QList result; QMapIterator gamesIterator(games); while (gamesIterator.hasNext()) { diff --git a/common/server_room.h b/common/server_room.h index 47e307cb..0def72ca 100644 --- a/common/server_room.h +++ b/common/server_room.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "pb/serverinfo_room.pb.h" #include "serverinfo_user_container.h" @@ -35,9 +35,10 @@ private: QList userList; QMap externalUsers; private slots: - void broadcastGameListUpdate(ServerInfo_Game gameInfo); + void broadcastGameListUpdate(ServerInfo_Game gameInfo, bool sendToIsl = true); public: - mutable QMutex roomMutex; + mutable QReadWriteLock usersLock; + mutable QMutex gamesMutex; Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent); ~Server_Room(); int getId() const { return id; } @@ -57,8 +58,10 @@ public: void addExternalUser(const ServerInfo_User &userInfo); void removeExternalUser(const QString &name); + const QMap &getExternalUsers() const { return externalUsers; } + void updateExternalGameList(const ServerInfo_Game &gameInfo); - void say(Server_ProtocolHandler *client, const QString &s); + void say(const QString &userName, const QString &s, bool sendToIsl = true); void addGame(Server_Game *game); void removeGame(Server_Game *game); @@ -67,7 +70,4 @@ public: RoomEvent *prepareRoomEvent(const ::google::protobuf::Message &roomEvent); }; -Q_DECLARE_METATYPE(ServerInfo_Game) -Q_DECLARE_METATYPE(ServerInfo_Room) - #endif diff --git a/servatrice/src/isl_interface.cpp b/servatrice/src/isl_interface.cpp index d62d9f58..f4e03f6e 100644 --- a/servatrice/src/isl_interface.cpp +++ b/servatrice/src/isl_interface.cpp @@ -11,6 +11,10 @@ #include "pb/event_user_message.pb.h" #include "pb/event_user_joined.pb.h" #include "pb/event_user_left.pb.h" +#include "pb/event_join_room.pb.h" +#include "pb/event_leave_room.pb.h" +#include "pb/event_room_say.pb.h" +#include "pb/event_list_games.pb.h" #include void IslInterface::sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey) @@ -19,7 +23,7 @@ void IslInterface::sharedCtor(const QSslCertificate &cert, const QSslKey &privat socket->setLocalCertificate(cert); socket->setPrivateKey(privateKey); - connect(socket, SIGNAL(readyRead()), this, SLOT(readClient())); + connect(socket, SIGNAL(readyRead()), this, SLOT(readClient()), Qt::QueuedConnection); connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError))); connect(this, SIGNAL(outputBufferChanged()), this, SLOT(flushOutputBuffer()), Qt::QueuedConnection); } @@ -42,18 +46,31 @@ IslInterface::~IslInterface() flushOutputBuffer(); - QStringList usersToDelete; - server->serverMutex.lock(); - QMapIterator extUsers = server->getExternalUsers(); + // As these signals are connected with Qt::QueuedConnection implicitly, + // we don't need to worry about them modifying the lists while we're iterating. + server->clientsLock.lockForRead(); + QMapIterator extUsers(server->getExternalUsers()); while (extUsers.hasNext()) { extUsers.next(); if (extUsers.value()->getUserInfo()->server_id() == serverId) - usersToDelete.append(extUsers.key()); + emit externalUserLeft(extUsers.key()); } - server->serverMutex.unlock(); + server->clientsLock.unlock(); - for (int i = 0; i < usersToDelete.size(); ++i) - emit externalUserLeft(usersToDelete[i]); + server->roomsLock.lockForRead(); + QMapIterator roomIterator(server->getRooms()); + while (roomIterator.hasNext()) { + Server_Room *room = roomIterator.next().value(); + room->usersLock.lockForRead(); + QMapIterator roomUsers(room->getExternalUsers()); + while (roomUsers.hasNext()) { + roomUsers.next(); + if (roomUsers.value().getUserInfo()->server_id() == serverId) + emit externalRoomUserLeft(room->getId(), roomUsers.key()); + } + room->usersLock.unlock(); + } + server->roomsLock.unlock(); } void IslInterface::initServer() @@ -98,15 +115,18 @@ void IslInterface::initServer() Event_ServerCompleteList event; event.set_server_id(server->getServerId()); - server->serverMutex.lock(); + server->clientsLock.lockForRead(); QMapIterator userIterator(server->getUsers()); while (userIterator.hasNext()) event.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(true, true)); - + server->clientsLock.unlock(); + + server->roomsLock.lockForRead(); QMapIterator roomIterator(server->getRooms()); while (roomIterator.hasNext()) { Server_Room *room = roomIterator.next().value(); - room->roomMutex.lock(); + room->usersLock.lockForRead(); + room->gamesMutex.lock(); event.add_room_list()->CopyFrom(room->getInfo(true, true, false, false)); } @@ -126,9 +146,12 @@ void IslInterface::initServer() server->islLock.unlock(); roomIterator.toFront(); - while (roomIterator.hasNext()) - roomIterator.next().value()->roomMutex.unlock(); - server->serverMutex.unlock(); + while (roomIterator.hasNext()) { + roomIterator.next(); + roomIterator.value()->gamesMutex.unlock(); + roomIterator.value()->usersLock.unlock(); + } + server->roomsLock.unlock(); } void IslInterface::initClient() @@ -161,7 +184,7 @@ void IslInterface::initClient() deleteLater(); return; } - + server->addIslInterface(serverId, this); server->islLock.unlock(); } @@ -242,11 +265,24 @@ void IslInterface::sessionEvent_ServerCompleteList(const Event_ServerCompleteLis temp.set_server_id(serverId); emit externalUserJoined(temp); } + for (int i = 0; i < event.room_list_size(); ++i) { + const ServerInfo_Room &room = event.room_list(i); + for (int j = 0; j < room.user_list_size(); ++j) { + ServerInfo_User userInfo(room.user_list(j)); + userInfo.set_server_id(serverId); + emit externalRoomUserJoined(room.room_id(), userInfo); + } + for (int j = 0; j < room.game_list_size(); ++j) { + ServerInfo_Game gameInfo(room.game_list(j)); + gameInfo.set_server_id(serverId); + emit externalRoomGameListChanged(room.room_id(), gameInfo); + } + } } void IslInterface::sessionEvent_UserMessage(const SessionEvent &sessionEvent, const Event_UserMessage &event) { - QMutexLocker locker(&server->serverMutex); + QReadLocker locker(&server->clientsLock); Server_ProtocolHandler *userInterface = server->getUsers().value(QString::fromStdString(event.receiver_name())); if (userInterface) @@ -255,7 +291,9 @@ void IslInterface::sessionEvent_UserMessage(const SessionEvent &sessionEvent, co void IslInterface::sessionEvent_UserJoined(const Event_UserJoined &event) { - emit externalUserJoined(event.user_info()); + ServerInfo_User userInfo(event.user_info()); + userInfo.set_server_id(serverId); + emit externalUserJoined(userInfo); } void IslInterface::sessionEvent_UserLeft(const Event_UserLeft &event) @@ -263,6 +301,32 @@ void IslInterface::sessionEvent_UserLeft(const Event_UserLeft &event) emit externalUserLeft(QString::fromStdString(event.name())); } +void IslInterface::roomEvent_UserJoined(int roomId, const Event_JoinRoom &event) +{ + ServerInfo_User userInfo(event.user_info()); + userInfo.set_server_id(serverId); + emit externalRoomUserJoined(roomId, userInfo); +} + +void IslInterface::roomEvent_UserLeft(int roomId, const Event_LeaveRoom &event) +{ + emit externalRoomUserLeft(roomId, QString::fromStdString(event.name())); +} + +void IslInterface::roomEvent_Say(int roomId, const Event_RoomSay &event) +{ + emit externalRoomSay(roomId, QString::fromStdString(event.name()), QString::fromStdString(event.message())); +} + +void IslInterface::roomEvent_ListGames(int roomId, const Event_ListGames &event) +{ + for (int i = 0; i < event.game_list_size(); ++i) { + ServerInfo_Game gameInfo(event.game_list(i)); + gameInfo.set_server_id(serverId); + emit externalRoomGameListChanged(roomId, gameInfo); + } +} + void IslInterface::processSessionEvent(const SessionEvent &event) { switch (getPbExtension(event)) { @@ -274,6 +338,17 @@ void IslInterface::processSessionEvent(const SessionEvent &event) } } +void IslInterface::processRoomEvent(const RoomEvent &event) +{ + switch (getPbExtension(event)) { + case RoomEvent::JOIN_ROOM: roomEvent_UserJoined(event.room_id(), event.GetExtension(Event_JoinRoom::ext)); break; + case RoomEvent::LEAVE_ROOM: roomEvent_UserLeft(event.room_id(), event.GetExtension(Event_LeaveRoom::ext)); break; + case RoomEvent::ROOM_SAY: roomEvent_Say(event.room_id(), event.GetExtension(Event_RoomSay::ext)); break; + case RoomEvent::LIST_GAMES: roomEvent_ListGames(event.room_id(), event.GetExtension(Event_ListGames::ext)); break; + default: ; + } +} + void IslInterface::processMessage(const IslMessage &item) { qDebug() << QString::fromStdString(item.DebugString()); @@ -290,6 +365,7 @@ void IslInterface::processMessage(const IslMessage &item) break; } case IslMessage::ROOM_EVENT: { + processRoomEvent(item.room_event()); break; break; } default: ; diff --git a/servatrice/src/isl_interface.h b/servatrice/src/isl_interface.h index 47508565..6469f596 100644 --- a/servatrice/src/isl_interface.h +++ b/servatrice/src/isl_interface.h @@ -5,6 +5,8 @@ #include #include #include "pb/serverinfo_user.pb.h" +#include "pb/serverinfo_room.pb.h" +#include "pb/serverinfo_game.pb.h" class Servatrice; class QSslSocket; @@ -15,6 +17,10 @@ class Event_ServerCompleteList; class Event_UserMessage; class Event_UserJoined; class Event_UserLeft; +class Event_JoinRoom; +class Event_LeaveRoom; +class Event_RoomSay; +class Event_ListGames; class IslInterface : public QObject { Q_OBJECT @@ -26,7 +32,11 @@ signals: void outputBufferChanged(); void externalUserJoined(ServerInfo_User userInfo); - void externalUserLeft(QString name); + void externalUserLeft(QString userName); + void externalRoomUserJoined(int roomId, ServerInfo_User userInfo); + void externalRoomUserLeft(int roomId, QString userName); + void externalRoomSay(int roomId, QString userName, QString message); + void externalRoomGameListChanged(int roomId, ServerInfo_Game gameInfo); private: int serverId; int socketDescriptor; @@ -47,7 +57,13 @@ private: void sessionEvent_UserJoined(const Event_UserJoined &event); void sessionEvent_UserLeft(const Event_UserLeft &event); + void roomEvent_UserJoined(int roomId, const Event_JoinRoom &event); + void roomEvent_UserLeft(int roomId, const Event_LeaveRoom &event); + void roomEvent_Say(int roomId, const Event_RoomSay &event); + void roomEvent_ListGames(int roomId, const Event_ListGames &event); + void processSessionEvent(const SessionEvent &event); + void processRoomEvent(const RoomEvent &event); void processMessage(const IslMessage &item); void sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey); diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index 026aae1b..81986366 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -35,7 +35,7 @@ RNG_Abstract *rng; ServerLogger *logger; -ServerLoggerThread *loggerThread; +QThread *loggerThread; void testRNG() { @@ -100,7 +100,11 @@ void sigSegvHandler(int sig) logger->logMessage("CRASH: SIGSEGV"); else if (sig == SIGABRT) logger->logMessage("CRASH: SIGABRT"); + + logger->deleteLater(); + loggerThread->wait(); delete loggerThread; + raise(sig); } #endif @@ -121,10 +125,13 @@ int main(int argc, char *argv[]) QSettings *settings = new QSettings("servatrice.ini", QSettings::IniFormat); - loggerThread = new ServerLoggerThread(settings->value("server/logfile").toString()); + loggerThread = new QThread; + logger = new ServerLogger; + logger->moveToThread(loggerThread); + QObject::connect(logger, SIGNAL(destroyed()), loggerThread, SLOT(quit())); + loggerThread->start(); - loggerThread->waitForInit(); - logger = loggerThread->getLogger(); + QMetaObject::invokeMethod(logger, "startLog", Qt::BlockingQueuedConnection, Q_ARG(QString, settings->value("server/logfile").toString())); qInstallMsgHandler(myMessageOutput2); #ifdef Q_OS_UNIX @@ -166,6 +173,9 @@ int main(int argc, char *argv[]) delete rng; delete settings; + + logger->deleteLater(); + loggerThread->wait(); delete loggerThread; return retval; diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index adcfb65a..1abcbcf3 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -87,30 +87,55 @@ Servatrice::Servatrice(QSettings *_settings, QObject *parent) updateServerList(); clearSessionTables(); - int size = settings->beginReadArray("rooms"); - for (int i = 0; i < size; ++i) { - settings->setArrayIndex(i); - - QStringList gameTypes; - int size2 = settings->beginReadArray("game_types"); - for (int j = 0; j < size2; ++j) { - settings->setArrayIndex(j); - gameTypes.append(settings->value("name").toString()); + const QString roomMethod = settings->value("rooms/method").toString(); + if (roomMethod == "sql") { + QSqlQuery query; + query.prepare("select id, name, descr, auto_join, join_message from " + dbPrefix + "_rooms order by id asc"); + execSqlQuery(query); + while (query.next()) { + QSqlQuery query2; + query2.prepare("select name from " + dbPrefix + "_rooms_gametypes where id_room = :id_room"); + query2.bindValue(":id_room", query.value(0).toInt()); + execSqlQuery(query2); + QStringList gameTypes; + while (query2.next()) + gameTypes.append(query2.value(0).toString()); + + addRoom(new Server_Room(query.value(0).toInt(), + query.value(1).toString(), + query.value(2).toString(), + query.value(3).toInt(), + query.value(4).toString(), + gameTypes, + this + )); + } + } else { + int size = settings->beginReadArray("rooms/roomlist"); + for (int i = 0; i < size; ++i) { + settings->setArrayIndex(i); + + QStringList gameTypes; + int size2 = settings->beginReadArray("game_types"); + for (int j = 0; j < size2; ++j) { + settings->setArrayIndex(j); + gameTypes.append(settings->value("name").toString()); + } + settings->endArray(); + + Server_Room *newRoom = new Server_Room( + i, + settings->value("name").toString(), + settings->value("description").toString(), + settings->value("autojoin").toBool(), + settings->value("joinmessage").toString(), + gameTypes, + this + ); + addRoom(newRoom); } settings->endArray(); - - Server_Room *newRoom = new Server_Room( - i, - settings->value("name").toString(), - settings->value("description").toString(), - settings->value("autojoin").toBool(), - settings->value("joinmessage").toString(), - gameTypes, - this - ); - addRoom(newRoom); } - settings->endArray(); updateLoginMessage(); @@ -499,8 +524,8 @@ ServerInfo_User Servatrice::getUserData(const QString &name, bool withId) int Servatrice::getUsersWithAddress(const QHostAddress &address) const { - QMutexLocker locker(&serverMutex); int result = 0; + QReadLocker locker(&clientsLock); for (int i = 0; i < clients.size(); ++i) if (static_cast(clients[i])->getPeerAddress() == address) ++result; @@ -509,8 +534,8 @@ int Servatrice::getUsersWithAddress(const QHostAddress &address) const QList Servatrice::getUsersWithAddressAsList(const QHostAddress &address) const { - QMutexLocker locker(&serverMutex); QList result; + QReadLocker locker(&clientsLock); for (int i = 0; i < clients.size(); ++i) if (static_cast(clients[i])->getPeerAddress() == address) result.append(static_cast(clients[i])); @@ -519,6 +544,8 @@ QList Servatrice::getUsersWithAddressAsList(const QHost void Servatrice::clearSessionTables() { + qDebug() << "Clearing previous sessions..."; + lockSessionTables(); QSqlQuery query; query.prepare("update " + dbPrefix + "_sessions set end_time=now() where end_time is null and id_server = :id_server"); @@ -740,13 +767,13 @@ void Servatrice::storeGameInformation(int secondsElapsed, const QSet &a SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent); allUsersIterator.toFront(); - serverMutex.lock(); + clientsLock.lockForRead(); while (allUsersIterator.hasNext()) { Server_ProtocolHandler *userHandler = users.value(allUsersIterator.next()); if (userHandler) userHandler->sendProtocolItem(*sessionEvent); } - serverMutex.unlock(); + clientsLock.unlock(); delete sessionEvent; QMutexLocker locker(&dbMutex); @@ -790,8 +817,6 @@ void Servatrice::storeGameInformation(int secondsElapsed, const QSet &a void Servatrice::scheduleShutdown(const QString &reason, int minutes) { - QMutexLocker locker(&serverMutex); - shutdownReason = reason; shutdownMinutes = minutes + 1; if (minutes > 0) { @@ -818,8 +843,6 @@ void Servatrice::incRxBytes(quint64 num) void Servatrice::shutdownTimeout() { - QMutexLocker locker(&serverMutex); - --shutdownMinutes; SessionEvent *se; @@ -833,9 +856,11 @@ void Servatrice::shutdownTimeout() event.set_reason(Event_ConnectionClosed::SERVER_SHUTDOWN); se = Server_ProtocolHandler::prepareSessionEvent(event); } - + + clientsLock.lockForRead(); for (int i = 0; i < clients.size(); ++i) clients[i]->sendProtocolItem(*se); + clientsLock.unlock(); delete se; if (!shutdownMinutes) @@ -856,7 +881,10 @@ void Servatrice::addIslInterface(int serverId, IslInterface *interface) islInterfaces.insert(serverId, interface); connect(interface, SIGNAL(externalUserJoined(ServerInfo_User)), this, SLOT(externalUserJoined(ServerInfo_User))); connect(interface, SIGNAL(externalUserLeft(QString)), this, SLOT(externalUserLeft(QString))); - connect(interface, SIGNAL(externalRoomUpdated(ServerInfo_Room)), this, SLOT(externalRoomUpdated(const ServerInfo_Room &))); + connect(interface, SIGNAL(externalRoomUserJoined(int, ServerInfo_User)), this, SLOT(externalRoomUserJoined(int, ServerInfo_User))); + connect(interface, SIGNAL(externalRoomUserLeft(int, QString)), this, SLOT(externalRoomUserLeft(int, QString))); + connect(interface, SIGNAL(externalRoomSay(int, QString, QString)), this, SLOT(externalRoomSay(int, QString, QString))); + connect(interface, SIGNAL(externalRoomGameListChanged(int, ServerInfo_Game)), this, SLOT(externalRoomGameListChanged(int, ServerInfo_Game))); } void Servatrice::removeIslInterface(int serverId) diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 95710cd1..87e466f5 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -26,7 +26,6 @@ #include #include #include -#include #include "server.h" class QSqlDatabase; @@ -167,6 +166,4 @@ private: QMap islInterfaces; }; -Q_DECLARE_METATYPE(ServerInfo_User) - #endif diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index e77eca54..fb8816e6 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -8,8 +8,17 @@ # include #endif -ServerLogger::ServerLogger(const QString &logFileName, QObject *parent) +ServerLogger::ServerLogger(QObject *parent) : QObject(parent), flushRunning(false) +{ +} + +ServerLogger::~ServerLogger() +{ + flushBuffer(); +} + +void ServerLogger::startLog(const QString &logFileName) { if (!logFileName.isEmpty()) { logFile = new QFile("server.log", this); @@ -26,11 +35,6 @@ ServerLogger::ServerLogger(const QString &logFileName, QObject *parent) connect(this, SIGNAL(sigFlushBuffer()), this, SLOT(flushBuffer()), Qt::QueuedConnection); } -ServerLogger::~ServerLogger() -{ - flushBuffer(); -} - void ServerLogger::logMessage(QString message, void *caller) { if (!logFile) @@ -98,34 +102,3 @@ void ServerLogger::handleSigHup() QFile *ServerLogger::logFile; int ServerLogger::sigHupFD[2]; - -ServerLoggerThread::ServerLoggerThread(const QString &_fileName, QObject *parent) - : QThread(parent), fileName(_fileName) -{ -} - -ServerLoggerThread::~ServerLoggerThread() -{ - quit(); - wait(); -} - -void ServerLoggerThread::run() -{ - logger = new ServerLogger(fileName); - - usleep(100); - initWaitCondition.wakeAll(); - - exec(); - - delete logger; -} - -void ServerLoggerThread::waitForInit() -{ - QMutex mutex; - mutex.lock(); - initWaitCondition.wait(&mutex); - mutex.unlock(); -} diff --git a/servatrice/src/server_logger.h b/servatrice/src/server_logger.h index 038e6002..8a17936b 100644 --- a/servatrice/src/server_logger.h +++ b/servatrice/src/server_logger.h @@ -14,10 +14,11 @@ class Server_ProtocolHandler; class ServerLogger : public QObject { Q_OBJECT public: - ServerLogger(const QString &logFileName, QObject *parent = 0); + ServerLogger(QObject *parent = 0); ~ServerLogger(); static void hupSignalHandler(int unused); public slots: + void startLog(const QString &logFileName); void logMessage(QString message, void *caller = 0); private slots: void handleSigHup(); @@ -33,19 +34,4 @@ private: QMutex bufferMutex; }; -class ServerLoggerThread : public QThread { - Q_OBJECT -private: - QString fileName; - ServerLogger *logger; - QWaitCondition initWaitCondition; -protected: - void run(); -public: - ServerLoggerThread(const QString &_fileName, QObject *parent = 0); - ~ServerLoggerThread(); - ServerLogger *getLogger() const { return logger; } - void waitForInit(); -}; - #endif diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 64d6aaa8..28776461 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -642,6 +642,6 @@ Response::ResponseCode ServerSocketInterface::cmdUpdateServerMessage(const Comma Response::ResponseCode ServerSocketInterface::cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer & /*rc*/) { - servatrice->scheduleShutdown(QString::fromStdString(cmd.reason()), cmd.minutes()); + QMetaObject::invokeMethod(server, "scheduleShutdown", Q_ARG(QString, QString::fromStdString(cmd.reason())), Q_ARG(int, cmd.minutes())); return Response::RespOk; }