Merge branch 'master' of git://github.com/mbruker/Cockatrice

This commit is contained in:
Daenyth 2013-02-27 12:36:14 -05:00
commit 347d30a84b
44 changed files with 673 additions and 371 deletions

View file

@ -36,7 +36,7 @@
#include <QDebug>
Server::Server(bool _threaded, QObject *parent)
: QObject(parent), threaded(_threaded), clientsLock(QReadWriteLock::Recursive), nextLocalGameId(0)
: QObject(parent), threaded(_threaded), nextLocalGameId(0)
{
qRegisterMetaType<ServerInfo_Game>("ServerInfo_Game");
qRegisterMetaType<ServerInfo_Room>("ServerInfo_Room");
@ -79,10 +79,9 @@ void Server::prepareDestroy()
clientsLock.unlock();
} while (!done);
} else {
clientsLock.lockForWrite();
// no locking is needed in unthreaded mode
while (!clients.isEmpty())
clients.first()->prepareDestroy();
clientsLock.unlock();
}
roomsLock.lockForWrite();
@ -141,7 +140,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
}
users.insert(name, session);
qDebug() << "Server::loginUser: name=" << name;
qDebug() << "Server::loginUser:" << session << "name=" << name;
data.set_session_id(databaseInterface->startSession(name, session->getAddress()));
databaseInterface->unlockSessionTables();
@ -159,7 +158,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
clients[i]->sendProtocolItem(*se);
delete se;
event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true));
event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true));
locker.unlock();
se = Server_ProtocolHandler::prepareSessionEvent(event);
@ -187,6 +186,17 @@ QList<PlayerReference> Server::getPersistentPlayerReferences(const QString &user
return persistentPlayers.values(userName);
}
Server_AbstractUserInterface *Server::findUser(const QString &userName) const
{
// Call this only with clientsLock set.
Server_AbstractUserInterface *userHandler = users.value(userName);
if (userHandler)
return userHandler;
else
return externalUsers.value(userName);
}
void Server::addClient(Server_ProtocolHandler *client)
{
QWriteLocker locker(&clientsLock);
@ -218,13 +228,13 @@ void Server::removeClient(Server_ProtocolHandler *client)
qDebug() << "closed session id:" << sessionId;
}
}
qDebug() << "Server::removeClient:" << clients.size() << "clients; " << users.size() << "users left";
qDebug() << "Server::removeClient: removed" << (void *) client << ";" << clients.size() << "clients; " << users.size() << "users left";
}
void Server::externalUserJoined(const ServerInfo_User &userInfo)
{
// This function is always called from the main thread via signal/slot.
QWriteLocker locker(&clientsLock);
clientsLock.lockForWrite();
Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo));
externalUsers.insert(QString::fromStdString(userInfo.name()), newUser);
@ -263,7 +273,7 @@ void Server::externalUserLeft(const QString &userName)
if (!room)
continue;
QMutexLocker roomGamesLocker(&room->gamesMutex);
QReadLocker roomGamesLocker(&room->gamesLock);
Server_Game *game = room->getGames().value(userGamesIterator.key());
if (!game)
continue;
@ -389,7 +399,7 @@ void Server::externalGameCommandContainerReceived(const CommandContainer &cont,
throw Response::RespNotInRoom;
}
QMutexLocker roomGamesLocker(&room->gamesMutex);
QReadLocker roomGamesLocker(&room->gamesLock);
Server_Game *game = room->getGames().value(cont.game_id());
if (!game) {
qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found";
@ -499,7 +509,7 @@ int Server::getGamesCount() const
QMapIterator<int, Server_Room *> roomIterator(rooms);
while (roomIterator.hasNext()) {
Server_Room *room = roomIterator.next().value();
QMutexLocker roomLocker(&room->gamesMutex);
QReadLocker roomLocker(&room->gamesLock);
result += room->getGames().size();
}
return result;

View file

@ -42,9 +42,11 @@ public:
mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock
Server(bool _threaded, QObject *parent = 0);
~Server();
void setThreaded(bool _threaded) { threaded = _threaded; }
AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft);
const QMap<int, Server_Room *> &getRooms() { return rooms; }
Server_AbstractUserInterface *findUser(const QString &userName) const;
const QMap<QString, Server_ProtocolHandler *> &getUsers() const { return users; }
const QMap<qint64, Server_ProtocolHandler *> &getUsersBySessionId() const { return usersBySessionId; }
void addClient(Server_ProtocolHandler *player);
@ -62,7 +64,6 @@ public:
Server_DatabaseInterface *getDatabaseInterface() const;
int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; }
virtual void storeGameInformation(int secondsElapsed, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replays) { }
void sendIsl_Response(const Response &item, int serverId = -1, qint64 sessionId = -1);
void sendIsl_SessionEvent(const SessionEvent &item, int serverId = -1, qint64 sessionId = -1);

View file

@ -78,7 +78,7 @@ void Server_AbstractUserInterface::joinPersistentGames(ResponseContainer &rc)
Server_Room *room = server->getRooms().value(pr.getRoomId());
if (!room)
continue;
QMutexLocker roomGamesLocker(&room->gamesMutex);
QReadLocker roomGamesLocker(&room->gamesLock);
Server_Game *game = room->getGames().value(pr.getGameId());
if (!game)

View file

@ -51,28 +51,78 @@ void Server_CardZone::shuffle()
playersWithWritePermission.clear();
}
void Server_CardZone::removeCardFromCoordMap(Server_Card *card, int oldX, int oldY)
{
if (oldX < 0)
return;
const int baseX = (oldX / 3) * 3;
QMap<int, Server_Card *> &coordMap = coordinateMap[oldY];
if (coordMap.contains(baseX) && coordMap.contains(baseX + 1) && coordMap.contains(baseX + 2))
// If the removal of this card has opened up a previously full pile...
freePilesMap[oldY].insert(coordMap.value(baseX)->getName(), baseX);
coordMap.remove(oldX);
if (!(coordMap.contains(baseX) && coordMap.value(baseX)->getName() == card->getName()) && !(coordMap.contains(baseX + 1) && coordMap.value(baseX + 1)->getName() == card->getName()) && !(coordMap.contains(baseX + 2) && coordMap.value(baseX + 2)->getName() == card->getName()))
// If this card was the last one with this name...
freePilesMap[oldY].remove(card->getName(), baseX);
if (!coordMap.contains(baseX) && !coordMap.contains(baseX + 1) && !coordMap.contains(baseX + 2)) {
// If the removal of this card has freed a whole pile, i.e. it was the last card in it...
if (baseX < freeSpaceMap[oldY])
freeSpaceMap[oldY] = baseX;
}
}
void Server_CardZone::insertCardIntoCoordMap(Server_Card *card, int x, int y)
{
if (x < 0)
return;
coordinateMap[y].insert(x, card);
if (!(x % 3)) {
if (!freePilesMap[y].contains(card->getName(), x) && card->getAttachedCards().isEmpty())
freePilesMap[y].insert(card->getName(), x);
if (freeSpaceMap[y] == x) {
int nextFreeX = x;
do {
nextFreeX += 3;
} while (coordinateMap[y].contains(nextFreeX) || coordinateMap[y].contains(nextFreeX + 1) || coordinateMap[y].contains(nextFreeX + 2));
freeSpaceMap[y] = nextFreeX;
}
} else if (!((x - 2) % 3)) {
const int baseX = (x / 3) * 3;
freePilesMap[y].remove(coordinateMap[y].value(baseX)->getName(), baseX);
}
}
int Server_CardZone::removeCard(Server_Card *card)
{
int index = cards.indexOf(card);
cards.removeAt(index);
if (has_coords)
removeCardFromCoordMap(card, card->getX(), card->getY());
card->setZone(0);
return index;
}
Server_Card *Server_CardZone::getCard(int id, int *position)
Server_Card *Server_CardZone::getCard(int id, int *position, bool remove)
{
if (type != ServerInfo_Zone::HiddenZone) {
QListIterator<Server_Card *> CardIterator(cards);
int i = 0;
while (CardIterator.hasNext()) {
Server_Card *tmp = CardIterator.next();
for (int i = 0; i < cards.size(); ++i) {
Server_Card *tmp = cards[i];
if (tmp->getId() == id) {
if (position)
*position = i;
if (remove) {
cards.removeAt(i);
tmp->setZone(0);
}
return tmp;
}
i++;
}
return NULL;
} else {
@ -81,30 +131,29 @@ Server_Card *Server_CardZone::getCard(int id, int *position)
Server_Card *tmp = cards[id];
if (position)
*position = id;
if (remove) {
cards.removeAt(id);
tmp->setZone(0);
}
return tmp;
}
}
int Server_CardZone::getFreeGridColumn(int x, int y, const QString &cardName) const
{
QMap<int, Server_Card *> coordMap;
for (int i = 0; i < cards.size(); ++i)
if (cards[i]->getY() == y)
coordMap.insert(cards[i]->getX(), cards[i]);
int resultX = 0;
const QMap<int, Server_Card *> &coordMap = coordinateMap.value(y);
if (x == -1) {
for (int i = 0; i < cards.size(); ++i)
if ((cards[i]->getName() == cardName) && !(cards[i]->getX() % 3) && (cards[i]->getY() == y)) {
if (!cards[i]->getAttachedCards().isEmpty())
continue;
if (!coordMap.value(cards[i]->getX() + 1))
return cards[i]->getX() + 1;
if (!coordMap.value(cards[i]->getX() + 2))
return cards[i]->getX() + 2;
}
} else if (x == -2) {
} else {
if (freePilesMap[y].contains(cardName)) {
x = (freePilesMap[y].value(cardName) / 3) * 3;
if (!coordMap.contains(x))
return x;
else if (!coordMap.contains(x + 1))
return x + 1;
else
return x + 2;
}
} else if (x >= 0) {
int resultX = 0;
x = (x / 3) * 3;
if (!coordMap.contains(x))
resultX = x;
@ -119,13 +168,14 @@ int Server_CardZone::getFreeGridColumn(int x, int y, const QString &cardName) co
resultX = x;
x = -1;
}
if (x < 0)
while (coordMap.contains(resultX))
resultX += 3;
return resultX;
}
if (x < 0)
while (coordMap.value(resultX))
resultX += 3;
return resultX;
return freeSpaceMap[y];
}
bool Server_CardZone::isColumnStacked(int x, int y) const
@ -133,12 +183,7 @@ bool Server_CardZone::isColumnStacked(int x, int y) const
if (!has_coords)
return false;
QMap<int, Server_Card *> coordMap;
for (int i = 0; i < cards.size(); ++i)
if (cards[i]->getY() == y)
coordMap.insert(cards[i]->getX(), cards[i]);
return coordMap.contains((x / 3) * 3 + 1);
return coordinateMap[y].contains((x / 3) * 3 + 1);
}
bool Server_CardZone::isColumnEmpty(int x, int y) const
@ -146,63 +191,68 @@ bool Server_CardZone::isColumnEmpty(int x, int y) const
if (!has_coords)
return true;
QMap<int, Server_Card *> coordMap;
for (int i = 0; i < cards.size(); ++i)
if (cards[i]->getY() == y)
coordMap.insert(cards[i]->getX(), cards[i]);
return !coordMap.contains((x / 3) * 3);
return !coordinateMap[y].contains((x / 3) * 3);
}
void Server_CardZone::moveCard(GameEventStorage &ges, QMap<int, Server_Card *> &coordMap, Server_Card *card, int x, int y)
void Server_CardZone::moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y)
{
coordMap.remove(card->getY() * 10000 + card->getX());
CardToMove *cardToMove = new CardToMove;
cardToMove->set_card_id(card->getId());
player->moveCard(ges, this, QList<const CardToMove *>() << cardToMove, this, x, y, false, false);
delete cardToMove;
coordMap.insert(y * 10000 + x, card);
}
void Server_CardZone::fixFreeSpaces(GameEventStorage &ges)
{
QMap<int, Server_Card *> coordMap;
QSet<int> placesToLook;
for (int i = 0; i < cards.size(); ++i) {
coordMap.insert(cards[i]->getY() * 10000 + cards[i]->getX(), cards[i]);
placesToLook.insert(cards[i]->getY() * 10000 + (cards[i]->getX() / 3) * 3);
}
if (!has_coords)
return;
QSetIterator<int> placeIterator(placesToLook);
QSet<QPair<int, int> > placesToLook;
for (int i = 0; i < cards.size(); ++i)
placesToLook.insert(QPair<int, int>((cards[i]->getX() / 3) * 3, cards[i]->getY()));
QSetIterator<QPair<int, int> > placeIterator(placesToLook);
while (placeIterator.hasNext()) {
int foo = placeIterator.next();
int y = foo / 10000;
int baseX = foo - y * 10000;
const QPair<int, int> &foo = placeIterator.next();
int baseX = foo.first;
int y = foo.second;
if (!coordMap.contains(y * 10000 + baseX)) {
if (coordMap.contains(y * 10000 + baseX + 1))
moveCard(ges, coordMap, coordMap.value(y * 10000 + baseX + 1), baseX, y);
else if (coordMap.contains(y * 10000 + baseX + 2)) {
moveCard(ges, coordMap, coordMap.value(y * 10000 + baseX + 2), baseX, y);
if (!coordinateMap[y].contains(baseX)) {
if (coordinateMap[y].contains(baseX + 1))
moveCardInRow(ges, coordinateMap[y].value(baseX + 1), baseX, y);
else if (coordinateMap[y].contains(baseX + 2)) {
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX, y);
continue;
} else
continue;
}
if (!coordMap.contains(y * 10000 + baseX + 1) && coordMap.contains(y * 10000 + baseX + 2))
moveCard(ges, coordMap, coordMap.value(y * 10000 + baseX + 2), baseX + 1, y);
if (!coordinateMap[y].contains(baseX + 1) && coordinateMap[y].contains(baseX + 2))
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX + 1, y);
}
}
void Server_CardZone::updateCardCoordinates(Server_Card *card, int oldX, int oldY)
{
if (!has_coords)
return;
if (oldX != -1)
removeCardFromCoordMap(card, oldX, oldY);
insertCardIntoCoordMap(card, card->getX(), card->getY());
}
void Server_CardZone::insertCard(Server_Card *card, int x, int y)
{
if (hasCoords()) {
card->setCoords(x, y);
cards.append(card);
insertCardIntoCoordMap(card, x, y);
} else {
card->setCoords(0, 0);
cards.insert(x, card);
if (x == -1)
cards.append(card);
else
cards.insert(x, card);
}
card->setZone(this);
}
@ -212,6 +262,9 @@ void Server_CardZone::clear()
for (int i = 0; i < cards.size(); i++)
delete cards.at(i);
cards.clear();
coordinateMap.clear();
freePilesMap.clear();
freeSpaceMap.clear();
playersWithWritePermission.clear();
}

View file

@ -40,12 +40,19 @@ private:
int cardsBeingLookedAt;
QSet<int> playersWithWritePermission;
bool alwaysRevealTopCard;
QList<Server_Card *> cards;
QMap<int, QMap<int, Server_Card *> > coordinateMap; // y -> (x -> card)
QMap<int, QMultiMap<QString, int> > freePilesMap; // y -> (cardName -> x)
QMap<int, int> freeSpaceMap; // y -> x
void removeCardFromCoordMap(Server_Card *card, int oldX, int oldY);
void insertCardIntoCoordMap(Server_Card *card, int x, int y);
public:
Server_CardZone(Server_Player *_player, const QString &_name, bool _has_coords, ServerInfo_Zone::ZoneType _type);
~Server_CardZone();
const QList<Server_Card *> &getCards() const { return cards; }
int removeCard(Server_Card *card);
Server_Card *getCard(int id, int *position = NULL);
Server_Card *getCard(int id, int *position = NULL, bool remove = false);
int getCardsBeingLookedAt() const { return cardsBeingLookedAt; }
void setCardsBeingLookedAt(int _cardsBeingLookedAt) { cardsBeingLookedAt = _cardsBeingLookedAt; }
@ -59,9 +66,9 @@ public:
bool isColumnEmpty(int x, int y) const;
bool isColumnStacked(int x, int y) const;
void fixFreeSpaces(GameEventStorage &ges);
void moveCard(GameEventStorage &ges, QMap<int, Server_Card *> &coordMap, Server_Card *card, int x, int y);
QList<Server_Card *> cards;
void moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y);
void insertCard(Server_Card *card, int x, int y);
void updateCardCoordinates(Server_Card *card, int oldX, int oldY);
void shuffle();
void clear();
void addWritePermission(int playerId);

View file

@ -19,7 +19,7 @@ public:
virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; }
virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0;
virtual void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replayList) { }
virtual DeckList *getDeckFromDatabase(int deckId, const QString &userName) { return 0; }
virtual DeckList *getDeckFromDatabase(int deckId, int userId) { return 0; }
virtual qint64 startSession(const QString &userName, const QString &address) { return 0; }
public slots:

View file

@ -42,6 +42,7 @@
#include "pb/event_set_active_phase.pb.h"
#include "pb/serverinfo_playerping.pb.h"
#include "pb/game_replay.pb.h"
#include "pb/event_replay_added.pb.h"
#include <google/protobuf/descriptor.h>
#include <QTimer>
#include <QDebug>
@ -90,7 +91,7 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, const
Server_Game::~Server_Game()
{
room->gamesMutex.lock();
room->gamesLock.lockForWrite();
gameMutex.lock();
gameClosed = true;
@ -106,11 +107,11 @@ Server_Game::~Server_Game()
creatorInfo = 0;
gameMutex.unlock();
room->gamesMutex.unlock();
room->gamesLock.unlock();
currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame);
replayList.append(currentReplay);
room->getServer()->storeGameInformation(secondsElapsed, allPlayersEver, allSpectatorsEver, replayList);
storeGameInformation();
for (int i = 0; i < replayList.size(); ++i)
delete replayList[i];
@ -118,6 +119,51 @@ Server_Game::~Server_Game()
qDebug() << "Server_Game destructor: gameId=" << gameId;
}
void Server_Game::storeGameInformation()
{
const ServerInfo_Game &gameInfo = replayList.first()->game_info();
Event_ReplayAdded replayEvent;
ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info();
replayMatchInfo->set_game_id(gameInfo.game_id());
replayMatchInfo->set_room_name(room->getName().toStdString());
replayMatchInfo->set_time_started(QDateTime::currentDateTime().addSecs(-secondsElapsed).toTime_t());
replayMatchInfo->set_length(secondsElapsed);
replayMatchInfo->set_game_name(gameInfo.description());
const QStringList &allGameTypes = room->getGameTypes();
QStringList gameTypes;
for (int i = gameInfo.game_types_size() - 1; i >= 0; --i)
gameTypes.append(allGameTypes[gameInfo.game_types(i)]);
QSetIterator<QString> playerIterator(allPlayersEver);
while (playerIterator.hasNext())
replayMatchInfo->add_player_names(playerIterator.next().toStdString());
for (int i = 0; i < replayList.size(); ++i) {
ServerInfo_Replay *replayInfo = replayMatchInfo->add_replay_list();
replayInfo->set_replay_id(replayList[i]->replay_id());
replayInfo->set_replay_name(gameInfo.description());
replayInfo->set_duration(replayList[i]->duration_seconds());
}
QSet<QString> allUsersInGame = allPlayersEver + allSpectatorsEver;
QSetIterator<QString> 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());
if (userHandler)
userHandler->sendProtocolItem(*sessionEvent);
}
server->clientsLock.unlock();
delete sessionEvent;
server->getDatabaseInterface()->storeGameInformation(room->getName(), gameTypes, gameInfo, allPlayersEver, allSpectatorsEver, replayList);
}
void Server_Game::pingClockTimeout()
{
QMutexLocker locker(&gameMutex);
@ -359,7 +405,7 @@ Response::ResponseCode Server_Game::checkJoin(ServerInfo_User *user, const QStri
return Response::RespWrongPassword;
if (!(user->user_level() & ServerInfo_User::IsRegistered) && onlyRegistered)
return Response::RespUserLevelTooLow;
if (onlyBuddies)
if (onlyBuddies && (user->name() != creatorInfo->name()))
if (!databaseInterface->isInBuddyList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name())))
return Response::RespOnlyBuddies;
if (databaseInterface->isInIgnoreList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name())))
@ -390,7 +436,7 @@ void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface, Respons
{
QMutexLocker locker(&gameMutex);
Server_Player *newPlayer = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true), spectator, userInterface);
Server_Player *newPlayer = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true, true), spectator, userInterface);
newPlayer->moveToThread(thread());
Event_Join joinEvent;
@ -516,8 +562,8 @@ void Server_Game::unattachCards(GameEventStorage &ges, Server_Player *player)
QMapIterator<QString, Server_CardZone *> zoneIterator(player->getZones());
while (zoneIterator.hasNext()) {
Server_CardZone *zone = zoneIterator.next().value();
for (int i = 0; i < zone->cards.size(); ++i) {
Server_Card *card = zone->cards.at(i);
for (int i = 0; i < zone->getCards().size(); ++i) {
Server_Card *card = zone->getCards().at(i);
// Make a copy of the list because the original one gets modified during the loop
QList<Server_Card *> attachedCards = card->getAttachedCards();

View file

@ -73,6 +73,7 @@ private:
void createGameStateChangedEvent(Event_GameStateChanged *event, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo);
void sendGameStateToPlayers();
void storeGameInformation();
signals:
void sigStartGameIfReady();
void gameInfoChanged(ServerInfo_Game gameInfo);

View file

@ -84,7 +84,7 @@
#include <QDebug>
Server_Player::Server_Player(Server_Game *_game, int _playerId, const ServerInfo_User &_userInfo, bool _spectator, Server_AbstractUserInterface *_userInterface)
: game(_game), userInterface(_userInterface), userInfo(new ServerInfo_User(_userInfo)), deck(0), pingTime(0), playerId(_playerId), spectator(_spectator), nextCardId(0), readyStart(false), conceded(false), sideboardLocked(true)
: ServerInfo_User_Container(_userInfo), game(_game), userInterface(_userInterface), deck(0), pingTime(0), playerId(_playerId), spectator(_spectator), nextCardId(0), readyStart(false), conceded(false), sideboardLocked(true)
{
}
@ -101,9 +101,6 @@ void Server_Player::prepareDestroy()
userInterface->playerRemovedFromGame(game);
playerMutex.unlock();
delete userInfo;
userInfo = 0;
clearZones();
deleteLater();
@ -185,7 +182,7 @@ void Server_Player::setupZones()
if (!currentCard)
continue;
for (int k = 0; k < currentCard->getNumber(); ++k)
z->cards.append(new Server_Card(currentCard->getName(), nextCardId++, 0, 0, z));
z->insertCard(new Server_Card(currentCard->getName(), nextCardId++, 0, 0, z), -1, 0);
}
}
@ -209,11 +206,10 @@ void Server_Player::setupZones()
else
continue;
for (int j = 0; j < start->cards.size(); ++j)
if (start->cards[j]->getName() == QString::fromStdString(m.card_name())) {
Server_Card *card = start->cards[j];
start->cards.removeAt(j);
target->cards.append(card);
for (int j = 0; j < start->getCards().size(); ++j)
if (start->getCards()[j]->getName() == QString::fromStdString(m.card_name())) {
Server_Card *card = start->getCard(j, NULL, true);
target->insertCard(card, -1, 0);
break;
}
}
@ -245,7 +241,7 @@ void Server_Player::getProperties(ServerInfo_PlayerProperties &result, bool with
{
result.set_player_id(playerId);
if (withUserInfo)
result.mutable_user_info()->CopyFrom(*userInfo);
copyUserInfo(*(result.mutable_user_info()), true);
result.set_spectator(spectator);
if (!spectator) {
result.set_conceded(conceded);
@ -286,16 +282,16 @@ Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int numbe
{
Server_CardZone *deckZone = zones.value("deck");
Server_CardZone *handZone = zones.value("hand");
if (deckZone->cards.size() < number)
number = deckZone->cards.size();
if (deckZone->getCards().size() < number)
number = deckZone->getCards().size();
Event_DrawCards eventOthers;
eventOthers.set_number(number);
Event_DrawCards eventPrivate(eventOthers);
for (int i = 0; i < number; ++i) {
Server_Card *card = deckZone->cards.takeFirst();
handZone->cards.append(card);
Server_Card *card = deckZone->getCard(0, NULL, true);
handZone->insertCard(card, -1, 0);
lastDrawList.append(card->getId());
ServerInfo_Card *cardInfo = eventPrivate.add_cards();
@ -306,11 +302,11 @@ Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int numbe
ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
if (deckZone->getAlwaysRevealTopCard() && !deckZone->cards.isEmpty()) {
if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(deckZone->getName().toStdString());
revealEvent.set_card_id(0);
deckZone->cards.first()->getInfo(revealEvent.add_cards());
deckZone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
}
@ -346,7 +342,7 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_Car
return Response::RespContextError;
if (!targetzone->hasCoords() && (x == -1))
x = targetzone->cards.size();
x = targetzone->getCards().size();
QList<QPair<Server_Card *, int> > cardsToMove;
QMap<Server_Card *, const CardToMove *> cardProperties;
@ -357,15 +353,21 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_Car
continue;
cardIdsToMove.insert(_cards[i]->card_id());
// Consistency checks. In case the command contains illegal moves, try to resolve the legal ones still.
int position;
Server_Card *card = startzone->getCard(_cards[i]->card_id(), &position);
if (!card)
return Response::RespNameNotFound;
if (card->getParentCard())
continue;
if (!card->getAttachedCards().isEmpty() && !targetzone->isColumnEmpty(x, y))
return Response::RespContextError;
continue;
cardsToMove.append(QPair<Server_Card *, int>(card, position));
cardProperties.insert(card, _cards[i]);
}
// In case all moves were filtered out, abort.
if (cardsToMove.isEmpty())
return Response::RespContextError;
MoveCardCompareFunctor cmp(startzone == targetzone ? -1 : x);
qSort(cardsToMove.begin(), cardsToMove.end(), cmp);
@ -525,19 +527,19 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_Car
if (!ptString.isEmpty() && !faceDown)
setCardAttrHelper(ges, targetzone->getName(), card->getId(), AttrPT, ptString);
}
if (startzone->getAlwaysRevealTopCard() && !startzone->cards.isEmpty() && (originalPosition == 0)) {
if (startzone->getAlwaysRevealTopCard() && !startzone->getCards().isEmpty() && (originalPosition == 0)) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(startzone->getName().toStdString());
revealEvent.set_card_id(0);
startzone->cards.first()->getInfo(revealEvent.add_cards());
startzone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
}
if (targetzone->getAlwaysRevealTopCard() && !targetzone->cards.isEmpty() && (newX == 0)) {
if (targetzone->getAlwaysRevealTopCard() && !targetzone->getCards().isEmpty() && (newX == 0)) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(targetzone->getName().toStdString());
revealEvent.set_card_id(0);
targetzone->cards.first()->getInfo(revealEvent.add_cards());
targetzone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
}
@ -556,7 +558,7 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_Car
void Server_Player::unattachCard(GameEventStorage &ges, Server_Card *card)
{
Server_CardZone *zone = card->getZone();
Server_Card *parentCard = card->getParentCard();
card->setParentCard(0);
Event_AttachCard event;
@ -568,6 +570,9 @@ void Server_Player::unattachCard(GameEventStorage &ges, Server_Card *card)
cardToMove->set_card_id(card->getId());
moveCard(ges, zone, QList<const CardToMove *>() << cardToMove, zone, -1, card->getY(), card->getFaceDown());
delete cardToMove;
if (parentCard->getZone())
parentCard->getZone()->updateCardCoordinates(parentCard, parentCard->getX(), parentCard->getY());
}
Response::ResponseCode Server_Player::setCardAttrHelper(GameEventStorage &ges, const QString &zoneName, int cardId, CardAttribute attribute, const QString &attrValue)
@ -580,7 +585,7 @@ Response::ResponseCode Server_Player::setCardAttrHelper(GameEventStorage &ges, c
QString result;
if (cardId == -1) {
QListIterator<Server_Card *> CardIterator(zone->cards);
QListIterator<Server_Card *> CardIterator(zone->getCards());
while (CardIterator.hasNext()) {
result = CardIterator.next()->setAttribute(attribute, attrValue, true);
if (result.isNull())
@ -631,7 +636,7 @@ Response::ResponseCode Server_Player::cmdDeckSelect(const Command_DeckSelect &cm
DeckList *newDeck;
if (cmd.has_deck_id()) {
try {
newDeck = game->getRoom()->getServer()->getDatabaseInterface()->getDeckFromDatabase(cmd.deck_id(), QString::fromStdString(userInfo->name()));
newDeck = game->getRoom()->getServer()->getDatabaseInterface()->getDeckFromDatabase(cmd.deck_id(), userInfo->id());
} catch(Response::ResponseCode r) {
return r;
}
@ -782,11 +787,11 @@ Response::ResponseCode Server_Player::cmdShuffle(const Command_Shuffle & /*cmd*/
event.set_zone_name("deck");
ges.enqueueGameEvent(event, playerId);
if (deckZone->getAlwaysRevealTopCard() && !deckZone->cards.isEmpty()) {
if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(deckZone->getName().toStdString());
revealEvent.set_card_id(0);
deckZone->cards.first()->getInfo(revealEvent.add_cards());
deckZone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
}
@ -805,12 +810,12 @@ Response::ResponseCode Server_Player::cmdMulligan(const Command_Mulligan & /*cmd
return Response::RespContextError;
Server_CardZone *hand = zones.value("hand");
int number = (hand->cards.size() <= 1) ? initialCards : hand->cards.size() - 1;
int number = (hand->getCards().size() <= 1) ? initialCards : hand->getCards().size() - 1;
Server_CardZone *deck = zones.value("deck");
while (!hand->cards.isEmpty()) {
while (!hand->getCards().isEmpty()) {
CardToMove *cardToMove = new CardToMove;
cardToMove->set_card_id(hand->cards.first()->getId());
cardToMove->set_card_id(hand->getCards().first()->getId());
moveCard(ges, hand, QList<const CardToMove *>() << cardToMove, deck, 0, 0, false);
delete cardToMove;
}
@ -991,9 +996,11 @@ Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cm
return Response::RespContextError;
if (cmd.has_target_card_id())
targetCard = targetzone->getCard(cmd.target_card_id());
if (targetCard)
if (targetCard) {
if (targetCard->getParentCard())
return Response::RespContextError;
} else
return Response::RespNameNotFound;
}
if (!startzone->hasCoords())
return Response::RespContextError;
@ -1025,6 +1032,11 @@ Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cm
for (int i = 0; i < attachedList.size(); ++i)
attachedList[i]->getZone()->getPlayer()->unattachCard(ges, attachedList[i]);
card->setParentCard(targetCard);
const int oldX = card->getX();
card->setCoords(-1, card->getY());
startzone->updateCardCoordinates(card, oldX, card->getY());
if (targetzone->isColumnStacked(targetCard->getX(), targetCard->getY())) {
CardToMove *cardToMove = new CardToMove;
cardToMove->set_card_id(targetCard->getId());
@ -1032,9 +1044,6 @@ Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cm
delete cardToMove;
}
card->setParentCard(targetCard);
card->setCoords(-1, card->getY());
Event_AttachCard event;
event.set_start_zone(startzone->getName().toStdString());
event.set_card_id(card->getId());
@ -1407,16 +1416,17 @@ Response::ResponseCode Server_Player::cmdDumpZone(const Command_DumpZone &cmd, R
return Response::RespContextError;
int numberCards = cmd.number_cards();
const QList<Server_Card *> &cards = zone->getCards();
Response_DumpZone *re = new Response_DumpZone;
ServerInfo_Zone *zoneInfo = re->mutable_zone_info();
zoneInfo->set_name(zone->getName().toStdString());
zoneInfo->set_type(zone->getType());
zoneInfo->set_with_coords(zone->hasCoords());
zoneInfo->set_card_count(numberCards < zone->cards.size() ? zone->cards.size() : numberCards);
zoneInfo->set_card_count(numberCards < cards.size() ? cards.size() : numberCards);
for (int i = 0; (i < zone->cards.size()) && (i < numberCards || numberCards == -1); ++i) {
Server_Card *card = zone->cards[i];
for (int i = 0; (i < cards.size()) && (i < numberCards || numberCards == -1); ++i) {
Server_Card *card = cards[i];
QString displayedName = card->getFaceDown() ? QString() : card->getName();
ServerInfo_Card *cardInfo = zoneInfo->add_card_list();
cardInfo->set_name(displayedName.toStdString());
@ -1510,11 +1520,11 @@ Response::ResponseCode Server_Player::cmdRevealCards(const Command_RevealCards &
QList<Server_Card *> cardsToReveal;
if (!cmd.has_card_id())
cardsToReveal = zone->cards;
cardsToReveal = zone->getCards();
else if (cmd.card_id() == -2) {
if (zone->cards.isEmpty())
if (zone->getCards().isEmpty())
return Response::RespContextError;
cardsToReveal.append(zone->cards.at(rng->getNumber(0, zone->cards.size() - 1)));
cardsToReveal.append(zone->getCards().at(rng->getNumber(0, zone->getCards().size() - 1)));
} else {
Server_Card *card = zone->getCard(cmd.card_id());
if (!card)
@ -1600,11 +1610,11 @@ Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_Chan
ges.enqueueGameEvent(event, playerId);
if (!zone->cards.isEmpty() && cmd.always_reveal_top_card()) {
if (!zone->getCards().isEmpty() && cmd.always_reveal_top_card()) {
Event_RevealCards revealEvent;
revealEvent.set_zone_name(zone->getName().toStdString());
revealEvent.set_card_id(0);
zone->cards.first()->getInfo(revealEvent.add_cards());
zone->getCards().first()->getInfo(revealEvent.add_cards());
ges.enqueueGameEvent(revealEvent, playerId);
}

View file

@ -2,6 +2,7 @@
#define PLAYER_H
#include "server_arrowtarget.h"
#include "serverinfo_user_container.h"
#include <QString>
#include <QList>
#include <QMap>
@ -60,13 +61,12 @@ class Command_DeckSelect;
class Command_SetSideboardLock;
class Command_ChangeZoneProperties;
class Server_Player : public Server_ArrowTarget {
class Server_Player : public Server_ArrowTarget, public ServerInfo_User_Container {
Q_OBJECT
private:
class MoveCardCompareFunctor;
Server_Game *game;
Server_AbstractUserInterface *userInterface;
ServerInfo_User *userInfo;
DeckList *deck;
QMap<QString, Server_CardZone *> zones;
QMap<int, Server_Counter *> counters;
@ -96,7 +96,6 @@ public:
bool getSpectator() const { return spectator; }
bool getConceded() const { return conceded; }
void setConceded(bool _conceded) { conceded = _conceded; }
ServerInfo_User *getUserInfo() const { return userInfo; }
DeckList *getDeck() const { return deck; }
Server_Game *getGame() const { return game; }
const QMap<QString, Server_CardZone *> &getZones() const { return zones; }

View file

@ -38,6 +38,8 @@ Server_ProtocolHandler::~Server_ProtocolHandler()
{
}
// 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)
@ -58,24 +60,24 @@ void Server_ProtocolHandler::prepareDestroy()
Server_Room *r = server->getRooms().value(gameIterator.value().first);
if (!r)
continue;
r->gamesMutex.lock();
r->gamesLock.lockForRead();
Server_Game *g = r->getGames().value(gameIterator.key());
if (!g) {
r->gamesMutex.unlock();
r->gamesLock.unlock();
continue;
}
g->gameMutex.lock();
Server_Player *p = g->getPlayers().value(gameIterator.value().second);
if (!p) {
g->gameMutex.unlock();
r->gamesMutex.unlock();
r->gamesLock.unlock();
continue;
}
p->disconnectClient();
g->gameMutex.unlock();
r->gamesMutex.unlock();
r->gamesLock.unlock();
}
server->roomsLock.unlock();
@ -195,7 +197,7 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const
if (!room)
return Response::RespNotInRoom;
QMutexLocker roomGamesLocker(&room->gamesMutex);
QReadLocker roomGamesLocker(&room->gamesLock);
Server_Game *game = room->getGames().value(cont.game_id());
if (!game) {
if (room->getExternalGames().contains(cont.game_id())) {
@ -275,6 +277,10 @@ Response::ResponseCode Server_ProtocolHandler::processAdminCommandContainer(cons
void Server_ProtocolHandler::processCommandContainer(const CommandContainer &cont)
{
// Command processing must be disabled after prepareDestroy() has been called.
if (deleted)
return;
lastDataReceived = timeRunning;
ResponseContainer responseContainer(cont.has_cmd_id() ? cont.cmd_id() : -1);
@ -374,12 +380,9 @@ Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message
QReadLocker locker(&server->clientsLock);
QString receiver = QString::fromStdString(cmd.user_name());
Server_AbstractUserInterface *userInterface = server->getUsers().value(receiver);
if (!userInterface) {
userInterface = server->getExternalUsers().value(receiver);
if (!userInterface)
return Response::RespNameNotFound;
}
Server_AbstractUserInterface *userInterface = server->findUser(receiver);
if (!userInterface)
return Response::RespNameNotFound;
if (databaseInterface->isInIgnoreList(receiver, QString::fromStdString(userInfo->name())))
return Response::RespInIgnoreList;
@ -400,22 +403,20 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_G
if (authState == NotLoggedIn)
return Response::RespLoginNeeded;
server->clientsLock.lockForRead();
if (!server->getUsers().contains(QString::fromStdString(cmd.user_name())))
return Response::RespNameNotFound;
server->clientsLock.unlock();
// We don't need to check whether the user is logged in; persistent games should also work.
// The client needs to deal with an empty result list.
Response_GetGamesOfUser *re = new Response_GetGamesOfUser;
server->roomsLock.lockForRead();
QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
while (roomIterator.hasNext()) {
Server_Room *room = roomIterator.next().value();
room->gamesMutex.lock();
room->gamesLock.lockForRead();
room->getInfo(*re->add_room_list(), false, true);
QListIterator<ServerInfo_Game> gameIterator(room->getGamesOfUser(QString::fromStdString(cmd.user_name())));
while (gameIterator.hasNext())
re->add_game_list()->CopyFrom(gameIterator.next());
room->gamesMutex.unlock();
room->gamesLock.unlock();
}
server->roomsLock.unlock();
@ -436,15 +437,11 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetU
QReadLocker locker(&server->clientsLock);
ServerInfo_User_Container *infoSource;
if (server->getUsers().contains(userName))
infoSource = server->getUsers().value(userName);
else if (server->getExternalUsers().contains(userName))
infoSource = server->getExternalUsers().value(userName);
else
ServerInfo_User_Container *infoSource = server->findUser(userName);
if (!infoSource)
return Response::RespNameNotFound;
re->mutable_user_info()->CopyFrom(infoSource->copyUserInfo(true, userInfo->user_level() & ServerInfo_User::IsModerator));
re->mutable_user_info()->CopyFrom(infoSource->copyUserInfo(true, false, userInfo->user_level() & ServerInfo_User::IsModerator));
}
rc.setResponseExtension(re);
@ -556,8 +553,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdCreateGame(const Command_Creat
if (gameId == -1)
return Response::RespInternalError;
QMutexLocker roomLocker(&room->gamesMutex);
if (server->getMaxGamesPerUser() > 0)
if (room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= server->getMaxGamesPerUser())
return Response::RespContextError;

View file

@ -13,7 +13,7 @@
#include <google/protobuf/descriptor.h>
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), gamesMutex(QMutex::Recursive)
: QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), gamesLock(QReadWriteLock::Recursive)
{
connect(this, SIGNAL(gameListChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)), Qt::QueuedConnection);
}
@ -22,15 +22,15 @@ Server_Room::~Server_Room()
{
qDebug("Server_Room destructor");
gamesMutex.lock();
gamesLock.lockForWrite();
const QList<Server_Game *> gameList = games.values();
for (int i = 0; i < gameList.size(); ++i)
delete gameList[i];
games.clear();
gamesMutex.unlock();
gamesLock.unlock();
usersLock.lockForWrite();
userList.clear();
users.clear();
usersLock.unlock();
}
@ -39,17 +39,15 @@ Server *Server_Room::getServer() const
return static_cast<Server *>(parent());
}
const ServerInfo_Room &Server_Room::getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes, bool updating, bool includeExternalData) const
const ServerInfo_Room &Server_Room::getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes, bool includeExternalData) const
{
result.set_room_id(id);
if (!updating) {
result.set_name(name.toStdString());
result.set_description(description.toStdString());
result.set_auto_join(autoJoin);
}
result.set_name(name.toStdString());
result.set_description(description.toStdString());
result.set_auto_join(autoJoin);
gamesMutex.lock();
gamesLock.lockForRead();
result.set_game_count(games.size() + externalGames.size());
if (complete) {
QMapIterator<int, Server_Game *> gameIterator(games);
@ -61,13 +59,14 @@ const ServerInfo_Room &Server_Room::getInfo(ServerInfo_Room &result, bool comple
result.add_game_list()->CopyFrom(externalGameIterator.next().value());
}
}
gamesMutex.unlock();
gamesLock.unlock();
usersLock.lockForRead();
result.set_player_count(userList.size() + externalUsers.size());
result.set_player_count(users.size() + externalUsers.size());
if (complete) {
for (int i = 0; i < userList.size(); ++i)
result.add_user_list()->CopyFrom(userList[i]->copyUserInfo(false));
QMapIterator<QString, Server_ProtocolHandler *> userIterator(users);
while (userIterator.hasNext())
result.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false));
if (includeExternalData) {
QMapIterator<QString, ServerInfo_User_Container> externalUserIterator(externalUsers);
while (externalUserIterator.hasNext())
@ -100,26 +99,44 @@ void Server_Room::addClient(Server_ProtocolHandler *client)
event.mutable_user_info()->CopyFrom(client->copyUserInfo(false));
sendRoomEvent(prepareRoomEvent(event));
ServerInfo_Room roomInfo;
roomInfo.set_room_id(id);
usersLock.lockForWrite();
userList.append(client);
users.insert(QString::fromStdString(client->getUserInfo()->name()), client);
roomInfo.set_player_count(users.size() + externalUsers.size());
usersLock.unlock();
ServerInfo_Room roomInfo;
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
// XXX This can be removed during the next client update.
gamesLock.lockForRead();
roomInfo.set_game_count(games.size() + externalGames.size());
gamesLock.unlock();
// -----------
emit roomInfoChanged(roomInfo);
}
void Server_Room::removeClient(Server_ProtocolHandler *client)
{
usersLock.lockForWrite();
userList.removeAt(userList.indexOf(client));
users.remove(QString::fromStdString(client->getUserInfo()->name()));
ServerInfo_Room roomInfo;
roomInfo.set_room_id(id);
roomInfo.set_player_count(users.size() + externalUsers.size());
usersLock.unlock();
Event_LeaveRoom event;
event.set_name(client->getUserInfo()->name());
sendRoomEvent(prepareRoomEvent(event));
ServerInfo_Room roomInfo;
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
// XXX This can be removed during the next client update.
gamesLock.lockForRead();
roomInfo.set_game_count(games.size() + externalGames.size());
gamesLock.unlock();
// -----------
emit roomInfoChanged(roomInfo);
}
void Server_Room::addExternalUser(const ServerInfo_User &userInfo)
@ -130,43 +147,52 @@ void Server_Room::addExternalUser(const ServerInfo_User &userInfo)
event.mutable_user_info()->CopyFrom(userInfoContainer.copyUserInfo(false));
sendRoomEvent(prepareRoomEvent(event), false);
ServerInfo_Room roomInfo;
roomInfo.set_room_id(id);
usersLock.lockForWrite();
externalUsers.insert(QString::fromStdString(userInfo.name()), userInfoContainer);
roomInfo.set_player_count(users.size() + externalUsers.size());
usersLock.unlock();
ServerInfo_Room roomInfo;
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
emit roomInfoChanged(roomInfo);
}
void Server_Room::removeExternalUser(const QString &name)
{
// This function is always called from the Server thread with server->roomsMutex locked.
ServerInfo_Room roomInfo;
roomInfo.set_room_id(id);
usersLock.lockForWrite();
if (externalUsers.contains(name))
externalUsers.remove(name);
roomInfo.set_player_count(users.size() + externalUsers.size());
usersLock.unlock();
Event_LeaveRoom event;
event.set_name(name.toStdString());
sendRoomEvent(prepareRoomEvent(event), false);
ServerInfo_Room roomInfo;
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
emit roomInfoChanged(roomInfo);
}
void Server_Room::updateExternalGameList(const ServerInfo_Game &gameInfo)
{
// This function is always called from the Server thread with server->roomsMutex locked.
gamesMutex.lock();
ServerInfo_Room roomInfo;
roomInfo.set_room_id(id);
gamesLock.lockForWrite();
if (!gameInfo.has_player_count() && externalGames.contains(gameInfo.game_id()))
externalGames.remove(gameInfo.game_id());
else
externalGames.insert(gameInfo.game_id(), gameInfo);
gamesMutex.unlock();
roomInfo.set_game_count(games.size() + externalGames.size());
gamesLock.unlock();
broadcastGameListUpdate(gameInfo, false);
ServerInfo_Room roomInfo;
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
emit roomInfoChanged(roomInfo);
}
Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGame &cmd, ResponseContainer &rc, Server_AbstractUserInterface *userInterface)
@ -174,7 +200,7 @@ Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGam
// This function is called from the Server thread and from the S_PH thread.
// server->roomsMutex is always locked.
QMutexLocker roomGamesLocker(&gamesMutex);
QReadLocker roomGamesLocker(&gamesLock);
Server_Game *g = games.value(cmd.game_id());
if (!g) {
if (externalGames.contains(cmd.game_id())) {
@ -210,8 +236,11 @@ void Server_Room::say(const QString &userName, const QString &s, bool sendToIsl)
void Server_Room::sendRoomEvent(RoomEvent *event, bool sendToIsl)
{
usersLock.lockForRead();
for (int i = 0; i < userList.size(); ++i)
userList[i]->sendProtocolItem(*event);
{
QMapIterator<QString, Server_ProtocolHandler *> userIterator(users);
while (userIterator.hasNext())
userIterator.next().value()->sendProtocolItem(*event);
}
usersLock.unlock();
if (sendToIsl)
@ -229,24 +258,33 @@ void Server_Room::broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool
void Server_Room::addGame(Server_Game *game)
{
// Lock gamesMutex before calling this
ServerInfo_Room roomInfo;
roomInfo.set_room_id(id);
gamesLock.lockForWrite();
connect(game, SIGNAL(gameInfoChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)));
game->gameMutex.lock();
games.insert(game->getGameId(), game);
ServerInfo_Game gameInfo;
game->getInfo(gameInfo);
roomInfo.set_game_count(games.size() + externalGames.size());
game->gameMutex.unlock();
gamesLock.unlock();
// XXX This can be removed during the next client update.
usersLock.lockForRead();
roomInfo.set_player_count(users.size() + externalUsers.size());
usersLock.unlock();
// -----------
emit gameListChanged(gameInfo);
ServerInfo_Room roomInfo;
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
emit roomInfoChanged(roomInfo);
}
void Server_Room::removeGame(Server_Game *game)
{
// No need to lock gamesMutex or gameMutex. This method is only
// No need to lock gamesLock or gameMutex. This method is only
// called from ~Server_Game, which locks both mutexes anyway beforehand.
disconnect(game, 0, this, 0);
@ -258,12 +296,21 @@ void Server_Room::removeGame(Server_Game *game)
games.remove(game->getGameId());
ServerInfo_Room roomInfo;
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
roomInfo.set_room_id(id);
roomInfo.set_game_count(games.size() + externalGames.size());
// XXX This can be removed during the next client update.
usersLock.lockForRead();
roomInfo.set_player_count(users.size() + externalUsers.size());
usersLock.unlock();
// -----------
emit roomInfoChanged(roomInfo);
}
int Server_Room::getGamesCreatedByUser(const QString &userName) const
{
QMutexLocker locker(&gamesMutex);
QReadLocker locker(&gamesLock);
QMapIterator<int, Server_Game *> gamesIterator(games);
int result = 0;
@ -275,7 +322,7 @@ int Server_Room::getGamesCreatedByUser(const QString &userName) const
QList<ServerInfo_Game> Server_Room::getGamesOfUser(const QString &userName) const
{
QMutexLocker locker(&gamesMutex);
QReadLocker locker(&gamesLock);
QList<ServerInfo_Game> result;
QMapIterator<int, Server_Game *> gamesIterator(games);

View file

@ -37,13 +37,13 @@ private:
QStringList gameTypes;
QMap<int, Server_Game *> games;
QMap<int, ServerInfo_Game> externalGames;
QList<Server_ProtocolHandler *> userList;
QMap<QString, Server_ProtocolHandler *> users;
QMap<QString, ServerInfo_User_Container> externalUsers;
private slots:
void broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl = true);
public:
mutable QReadWriteLock usersLock;
mutable QMutex gamesMutex;
mutable QReadWriteLock gamesLock;
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; }
@ -55,7 +55,7 @@ public:
const QMap<int, Server_Game *> &getGames() const { return games; }
const QMap<int, ServerInfo_Game> &getExternalGames() const { return externalGames; }
Server *getServer() const;
const ServerInfo_Room &getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes = false, bool updating = false, bool includeExternalData = true) const;
const ServerInfo_Room &getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes = false, bool includeExternalData = true) const;
int getGamesCreatedByUser(const QString &name) const;
QList<ServerInfo_Game> getGamesOfUser(const QString &name) const;

View file

@ -26,22 +26,27 @@ ServerInfo_User_Container::~ServerInfo_User_Container()
void ServerInfo_User_Container::setUserInfo(const ServerInfo_User &_userInfo)
{
userInfo = new ServerInfo_User;
userInfo->CopyFrom(_userInfo);
userInfo = new ServerInfo_User(_userInfo);
}
ServerInfo_User ServerInfo_User_Container::copyUserInfo(bool complete, bool moderatorInfo) const
ServerInfo_User &ServerInfo_User_Container::copyUserInfo(ServerInfo_User &result, bool complete, bool internalInfo, bool sessionInfo) const
{
ServerInfo_User result;
if (userInfo) {
result.CopyFrom(*userInfo);
if (!moderatorInfo) {
if (!sessionInfo) {
result.clear_session_id();
result.clear_address();
result.clear_id();
}
if (!internalInfo)
result.clear_id();
if (!complete)
result.clear_avatar_bmp();
}
return result;
return result;
}
ServerInfo_User ServerInfo_User_Container::copyUserInfo(bool complete, bool internalInfo, bool sessionInfo) const
{
ServerInfo_User result;
return copyUserInfo(result, complete, internalInfo, sessionInfo);
}

View file

@ -13,7 +13,8 @@ public:
virtual ~ServerInfo_User_Container();
ServerInfo_User *getUserInfo() const { return userInfo; }
void setUserInfo(const ServerInfo_User &_userInfo);
ServerInfo_User copyUserInfo(bool complete, bool moderatorInfo = false) const;
ServerInfo_User &copyUserInfo(ServerInfo_User &result, bool complete, bool internalInfo = false, bool sessionInfo = false) const;
ServerInfo_User copyUserInfo(bool complete, bool internalInfo = false, bool sessionInfo = false) const;
};
#endif