From 8481e61e8fa3b4903ec8e5a315a807e5dd4ae153 Mon Sep 17 00:00:00 2001 From: Max-Wilhelm Bruker Date: Fri, 2 Mar 2012 20:45:04 +0100 Subject: [PATCH 1/6] Replays split up, one for each subgame --- .../src/remotereplaylist_treewidget.cpp | 157 +++++++++++++----- cockatrice/src/remotereplaylist_treewidget.h | 45 ++++- cockatrice/src/tab_game.cpp | 2 +- cockatrice/src/tab_replays.cpp | 10 +- common/pb/CMakeLists.txt | 1 + common/pb/command_replay_download.proto | 2 +- common/pb/game_replay.proto | 1 + common/pb/response_replay_list.proto | 4 +- common/pb/serverinfo_replay.proto | 10 +- common/server.h | 2 +- common/server_game.cpp | 44 ++++- common/server_game.h | 6 +- servatrice/src/servatrice.cpp | 75 +++++---- servatrice/src/servatrice.h | 2 +- servatrice/src/serversocketinterface.cpp | 36 ++-- 15 files changed, 278 insertions(+), 119 deletions(-) diff --git a/cockatrice/src/remotereplaylist_treewidget.cpp b/cockatrice/src/remotereplaylist_treewidget.cpp index 379b7261..d978fe33 100644 --- a/cockatrice/src/remotereplaylist_treewidget.cpp +++ b/cockatrice/src/remotereplaylist_treewidget.cpp @@ -9,10 +9,24 @@ #include "pb/response_replay_list.pb.h" #include "pb/serverinfo_replay.pb.h" +RemoteReplayList_TreeModel::MatchNode::MatchNode(const ServerInfo_ReplayMatch &_matchInfo) + : RemoteReplayList_TreeModel::Node(QString::fromStdString(_matchInfo.game_name())), matchInfo(_matchInfo) +{ + for (int i = 0; i < matchInfo.replay_list_size(); ++i) + append(new ReplayNode(matchInfo.replay_list(i))); +} + +RemoteReplayList_TreeModel::MatchNode::~MatchNode() +{ + for (int i = 0; i < size(); ++i) + delete at(i); +} + RemoteReplayList_TreeModel::RemoteReplayList_TreeModel(AbstractClient *_client, QObject *parent) : QAbstractItemModel(parent), client(_client) { QFileIconProvider fip; + dirIcon = fip.icon(QFileIconProvider::Folder); fileIcon = fip.icon(QFileIconProvider::File); refreshTree(); @@ -20,47 +34,74 @@ RemoteReplayList_TreeModel::RemoteReplayList_TreeModel(AbstractClient *_client, RemoteReplayList_TreeModel::~RemoteReplayList_TreeModel() { + clearTree(); } int RemoteReplayList_TreeModel::rowCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : replays.size(); + if (!parent.isValid()) + return replayMatches.size(); + + MatchNode *matchNode = dynamic_cast(static_cast(parent.internalPointer())); + if (matchNode) + return matchNode->size(); + else + return 0; } int RemoteReplayList_TreeModel::columnCount(const QModelIndex &/*parent*/) const { - return 6; + return 5; } QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - if (index.column() > 5) + if (index.column() > 4) return QVariant(); - ServerInfo_Replay *replayInfo = static_cast(index.internalPointer()); - switch (role) { - case Qt::TextAlignmentRole: - return index.column() == 0 ? Qt::AlignRight : Qt::AlignLeft; - case Qt::DisplayRole: { - switch (index.column()) { - case 0: return replayInfo->game_id(); - case 1: return QString::fromStdString(replayInfo->game_name()); - case 2: return QString::fromStdString(replayInfo->replay_name()); - case 3: { - QStringList playerList; - for (int i = 0; i < replayInfo->player_names_size(); ++i) - playerList.append(QString::fromStdString(replayInfo->player_names(i))); - return playerList.join(", "); + ReplayNode *replayNode = dynamic_cast(static_cast(index.internalPointer())); + if (replayNode) { + const ServerInfo_Replay &replayInfo = replayNode->getReplayInfo(); + switch (role) { + case Qt::TextAlignmentRole: + return index.column() == 0 ? Qt::AlignRight : Qt::AlignLeft; + case Qt::DisplayRole: { + switch (index.column()) { + case 0: return replayInfo.replay_id(); + case 1: return QString::fromStdString(replayInfo.replay_name()); + case 4: return replayInfo.duration(); + default: return QVariant(); } - case 4: return QDateTime::fromTime_t(replayInfo->time_started()); - case 5: return replayInfo->length(); - default: return QVariant(); } + case Qt::DecorationRole: + return index.column() == 0 ? fileIcon : QVariant(); + } + } else { + MatchNode *matchNode = dynamic_cast(static_cast(index.internalPointer())); + const ServerInfo_ReplayMatch &matchInfo = matchNode->getMatchInfo(); + switch (role) { + case Qt::TextAlignmentRole: + return index.column() == 0 ? Qt::AlignRight : Qt::AlignLeft; + case Qt::DisplayRole: { + switch (index.column()) { + case 0: return matchInfo.game_id(); + case 1: return QString::fromStdString(matchInfo.game_name()); + case 2: { + QStringList playerList; + for (int i = 0; i < matchInfo.player_names_size(); ++i) + playerList.append(QString::fromStdString(matchInfo.player_names(i))); + return playerList.join(", "); + } + case 3: return QDateTime::fromTime_t(matchInfo.time_started()); + case 4: return matchInfo.length(); + default: return QVariant(); + } + } + case Qt::DecorationRole: + return index.column() == 0 ? dirIcon : QVariant(); } - case Qt::DecorationRole: - return index.column() == 0 ? fileIcon : QVariant(); } return QVariant(); } @@ -74,12 +115,11 @@ QVariant RemoteReplayList_TreeModel::headerData(int section, Qt::Orientation ori return section == 0 ? Qt::AlignRight : Qt::AlignLeft; case Qt::DisplayRole: { switch (section) { - case 0: return tr("Game ID"); - case 1: return tr("Game name"); - case 2: return tr("Replay name"); - case 3: return tr("Players"); - case 4: return tr("Time started"); - case 5: return tr("Duration (sec)"); + case 0: return tr("ID"); + case 1: return tr("Name"); + case 2: return tr("Players"); + case 3: return tr("Time started"); + case 4: return tr("Duration (sec)"); default: return QVariant(); } } @@ -91,13 +131,28 @@ QModelIndex RemoteReplayList_TreeModel::index(int row, int column, const QModelI { if (!hasIndex(row, column, parent)) return QModelIndex(); - - return createIndex(row, column, (void *) &(replays[row])); + + MatchNode *matchNode = dynamic_cast(static_cast(parent.internalPointer())); + if (matchNode) { + if (row >= matchNode->size()) + return QModelIndex(); + return createIndex(row, column, (void *) matchNode->at(row)); + } else { + if (row >= replayMatches.size()) + return QModelIndex(); + return createIndex(row, column, (void *) replayMatches[row]); + } } QModelIndex RemoteReplayList_TreeModel::parent(const QModelIndex &ind) const { - return QModelIndex(); + MatchNode const *matchNode = dynamic_cast(static_cast(ind.internalPointer())); + if (matchNode) + return QModelIndex(); + else { + ReplayNode *replayNode = dynamic_cast(static_cast(ind.internalPointer())); + return createIndex(replayNode->getParent()->indexOf(replayNode), 0, replayNode); + } } Qt::ItemFlags RemoteReplayList_TreeModel::flags(const QModelIndex &index) const @@ -108,12 +163,33 @@ Qt::ItemFlags RemoteReplayList_TreeModel::flags(const QModelIndex &index) const return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } -ServerInfo_Replay const* RemoteReplayList_TreeModel::getNode(const QModelIndex &index) const +ServerInfo_Replay const* RemoteReplayList_TreeModel::getReplay(const QModelIndex &index) const { if (!index.isValid()) return 0; - return &(replays[index.row()]); + ReplayNode *node = dynamic_cast(static_cast(index.internalPointer())); + if (!node) + return 0; + return &node->getReplayInfo(); +} + +ServerInfo_ReplayMatch const* RemoteReplayList_TreeModel::getReplayMatch(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + + MatchNode *node = dynamic_cast(static_cast(index.internalPointer())); + if (!node) + return 0; + return &node->getMatchInfo(); +} + +void RemoteReplayList_TreeModel::clearTree() +{ + for (int i = 0; i < replayMatches.size(); ++i) + delete replayMatches[i]; + replayMatches.clear(); } void RemoteReplayList_TreeModel::refreshTree() @@ -129,10 +205,10 @@ void RemoteReplayList_TreeModel::replayListFinished(const Response &r) const Response_ReplayList &resp = r.GetExtension(Response_ReplayList::ext); beginResetModel(); - replays.clear(); + clearTree(); - for (int i = 0; i < resp.replay_list_size(); ++i) - replays.append(resp.replay_list(i)); + for (int i = 0; i < resp.match_list_size(); ++i) + replayMatches.append(new MatchNode(resp.match_list(i))); endResetModel(); emit treeRefreshed(); @@ -156,12 +232,7 @@ RemoteReplayList_TreeWidget::RemoteReplayList_TreeWidget(AbstractClient *_client header()->setSortIndicator(0, Qt::AscendingOrder); } -ServerInfo_Replay const *RemoteReplayList_TreeWidget::getNode(const QModelIndex &ind) const +ServerInfo_Replay const *RemoteReplayList_TreeWidget::getCurrentReplay() const { - return treeModel->getNode(proxyModel->mapToSource(ind)); -} - -ServerInfo_Replay const *RemoteReplayList_TreeWidget::getCurrentItem() const -{ - return getNode(selectionModel()->currentIndex()); + return treeModel->getReplay(proxyModel->mapToSource(selectionModel()->currentIndex())); } diff --git a/cockatrice/src/remotereplaylist_treewidget.h b/cockatrice/src/remotereplaylist_treewidget.h index 5cbb6736..9c1567ea 100644 --- a/cockatrice/src/remotereplaylist_treewidget.h +++ b/cockatrice/src/remotereplaylist_treewidget.h @@ -5,6 +5,7 @@ #include #include #include "pb/serverinfo_replay.pb.h" +#include "pb/serverinfo_replay_match.pb.h" class Response; class AbstractClient; @@ -13,10 +14,42 @@ class QSortFilterProxyModel; class RemoteReplayList_TreeModel : public QAbstractItemModel { Q_OBJECT private: - AbstractClient *client; - QList replays; + class MatchNode; + class ReplayNode; + class Node { + protected: + QString name; + public: + Node(const QString &_name) + : name(_name) { } + virtual ~Node() { }; + QString getName() const { return name; } + }; + class MatchNode : public Node, public QList { + private: + ServerInfo_ReplayMatch matchInfo; + public: + MatchNode(const ServerInfo_ReplayMatch &_matchInfo); + ~MatchNode(); + void clearTree(); + const ServerInfo_ReplayMatch &getMatchInfo() { return matchInfo; } + }; + class ReplayNode : public Node { + private: + MatchNode *parent; + ServerInfo_Replay replayInfo; + public: + ReplayNode(const ServerInfo_Replay &_replayInfo, MatchNode *_parent = 0) + : Node(QString::fromStdString(_replayInfo.replay_name())), parent(_parent), replayInfo(_replayInfo) { } + MatchNode *getParent() const { return parent; } + const ServerInfo_Replay &getReplayInfo() { return replayInfo; } + }; - QIcon fileIcon; + AbstractClient *client; + QList replayMatches; + + QIcon dirIcon, fileIcon; + void clearTree(); signals: void treeRefreshed(); private slots: @@ -32,7 +65,8 @@ public: QModelIndex parent(const QModelIndex &index) const; Qt::ItemFlags flags(const QModelIndex &index) const; void refreshTree(); - ServerInfo_Replay const *getNode(const QModelIndex &index) const; + ServerInfo_Replay const* getReplay(const QModelIndex &index) const; + ServerInfo_ReplayMatch const* getReplayMatch(const QModelIndex &index) const; }; class RemoteReplayList_TreeWidget : public QTreeView { @@ -42,7 +76,8 @@ private: ServerInfo_Replay const *getNode(const QModelIndex &ind) const; public: RemoteReplayList_TreeWidget(AbstractClient *_client, QWidget *parent = 0); - ServerInfo_Replay const *getCurrentItem() const; + ServerInfo_Replay const *getCurrentReplay() const; + ServerInfo_ReplayMatch const *getCurrentReplayMatch() const; void refreshTree(); }; diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index 254f7d5c..bb606ab5 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -198,7 +198,7 @@ void DeckViewContainer::setDeck(DeckList *deck) deckView->setDeck(deck); readyStartButton->setEnabled(true); } -#include + TabGame::TabGame(GameReplay *_replay) : Tab(0), hostId(-1), diff --git a/cockatrice/src/tab_replays.cpp b/cockatrice/src/tab_replays.cpp index 4c9d258d..bc430a10 100644 --- a/cockatrice/src/tab_replays.cpp +++ b/cockatrice/src/tab_replays.cpp @@ -123,12 +123,12 @@ void TabReplays::actOpenLocalReplay() void TabReplays::actOpenRemoteReplay() { - ServerInfo_Replay const *curRight = serverDirView->getCurrentItem(); + ServerInfo_Replay const *curRight = serverDirView->getCurrentReplay(); if (!curRight) return; Command_ReplayDownload cmd; - cmd.set_game_id(curRight->game_id()); + cmd.set_replay_id(curRight->replay_id()); PendingCommand *pend = client->prepareSessionCommand(cmd); connect(pend, SIGNAL(finished(const Response &)), this, SLOT(openRemoteReplayFinished(const Response &))); @@ -156,13 +156,13 @@ void TabReplays::actDownload() filePath = localDirModel->filePath(curLeft); } - ServerInfo_Replay const *curRight = serverDirView->getCurrentItem(); + ServerInfo_Replay const *curRight = serverDirView->getCurrentReplay(); if (!curRight) return; - filePath += QString("/game_%1.cor").arg(curRight->game_id()); + filePath += QString("/replay_%1.cor").arg(curRight->replay_id()); Command_ReplayDownload cmd; - cmd.set_game_id(curRight->game_id()); + cmd.set_replay_id(curRight->replay_id()); PendingCommand *pend = client->prepareSessionCommand(cmd); pend->setExtraData(filePath); diff --git a/common/pb/CMakeLists.txt b/common/pb/CMakeLists.txt index 95628b02..f60ca819 100644 --- a/common/pb/CMakeLists.txt +++ b/common/pb/CMakeLists.txt @@ -126,6 +126,7 @@ SET(PROTO_FILES serverinfo_playerproperties.proto serverinfo_player.proto serverinfo_replay.proto + serverinfo_replay_match.proto serverinfo_room.proto serverinfo_user.proto serverinfo_zone.proto diff --git a/common/pb/command_replay_download.proto b/common/pb/command_replay_download.proto index 324f2f1e..ff7e2b34 100644 --- a/common/pb/command_replay_download.proto +++ b/common/pb/command_replay_download.proto @@ -4,5 +4,5 @@ message Command_ReplayDownload { extend SessionCommand { optional Command_ReplayDownload ext = 1101; } - optional sint32 game_id = 1 [default = -1]; + optional sint32 replay_id = 1 [default = -1]; } diff --git a/common/pb/game_replay.proto b/common/pb/game_replay.proto index 10bb233d..e3438e66 100644 --- a/common/pb/game_replay.proto +++ b/common/pb/game_replay.proto @@ -4,4 +4,5 @@ import "game_event_container.proto"; message GameReplay { optional ServerInfo_Game game_info = 1; repeated GameEventContainer event_list = 2; + optional uint32 duration_seconds = 3; } diff --git a/common/pb/response_replay_list.proto b/common/pb/response_replay_list.proto index 6a4c29f6..93ffb8bb 100644 --- a/common/pb/response_replay_list.proto +++ b/common/pb/response_replay_list.proto @@ -1,9 +1,9 @@ import "response.proto"; -import "serverinfo_replay.proto"; +import "serverinfo_replay_match.proto"; message Response_ReplayList { extend Response { optional Response_ReplayList ext = 1100; } - repeated ServerInfo_Replay replay_list = 1; + repeated ServerInfo_ReplayMatch match_list = 1; } diff --git a/common/pb/serverinfo_replay.proto b/common/pb/serverinfo_replay.proto index 1201e46c..18f68317 100644 --- a/common/pb/serverinfo_replay.proto +++ b/common/pb/serverinfo_replay.proto @@ -1,9 +1,5 @@ message ServerInfo_Replay { - optional sint32 game_id = 1 [default = -1]; - optional string room_name = 2; - optional uint32 time_started = 3; - optional uint32 length = 4; - optional string game_name = 5; - optional string replay_name = 6; - repeated string player_names = 7; + optional sint32 replay_id = 1 [default = -1]; + optional string replay_name = 2; + optional uint32 duration = 3; } diff --git a/common/server.h b/common/server.h index 843cf86a..b696190a 100644 --- a/common/server.h +++ b/common/server.h @@ -48,7 +48,7 @@ public: virtual bool isInBuddyList(const QString &whoseList, const QString &who) { return false; } virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; } - virtual void storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const GameReplay &replay) { } + virtual void storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replays) { } protected: void prepareDestroy(); QList clients; diff --git a/common/server_game.cpp b/common/server_game.cpp index 9924e4ae..99887d78 100644 --- a/common/server_game.cpp +++ b/common/server_game.cpp @@ -45,14 +45,15 @@ #include Server_Game::Server_Game(Server_ProtocolHandler *_creator, int _gameId, const QString &_description, const QString &_password, int _maxPlayers, const QList &_gameTypes, bool _onlyBuddies, bool _onlyRegistered, bool _spectatorsAllowed, bool _spectatorsNeedPassword, bool _spectatorsCanTalk, bool _spectatorsSeeEverything, Server_Room *_room) - : QObject(), room(_room), hostId(0), creatorInfo(new ServerInfo_User(_creator->copyUserInfo(false))), gameStarted(false), gameId(_gameId), description(_description), password(_password), maxPlayers(_maxPlayers), gameTypes(_gameTypes), activePlayer(-1), activePhase(-1), onlyBuddies(_onlyBuddies), onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed), spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk), spectatorsSeeEverything(_spectatorsSeeEverything), inactivityCounter(0), secondsElapsed(0), startTime(QDateTime::currentDateTime()), gameMutex(QMutex::Recursive) + : QObject(), room(_room), hostId(0), creatorInfo(new ServerInfo_User(_creator->copyUserInfo(false))), gameStarted(false), gameId(_gameId), description(_description), password(_password), maxPlayers(_maxPlayers), gameTypes(_gameTypes), activePlayer(-1), activePhase(-1), onlyBuddies(_onlyBuddies), onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed), spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk), spectatorsSeeEverything(_spectatorsSeeEverything), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false), startTime(QDateTime::currentDateTime()), gameMutex(QMutex::Recursive) { - replay = new GameReplay; + currentReplay = new GameReplay; connect(this, SIGNAL(sigStartGameIfReady()), this, SLOT(doStartGameIfReady()), Qt::QueuedConnection); addPlayer(_creator, false, false); - replay->mutable_game_info()->CopyFrom(getInfo()); + + currentReplay->mutable_game_info()->CopyFrom(getInfo()); if (room->getServer()->getGameShouldPing()) { pingClock = new QTimer(this); @@ -80,8 +81,12 @@ Server_Game::~Server_Game() gameMutex.unlock(); room->roomMutex.unlock(); - room->getServer()->storeGameInformation(secondsElapsed, allPlayersEver, allSpectatorsEver, *replay); - delete replay; + currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); + replayList.append(currentReplay); + room->getServer()->storeGameInformation(secondsElapsed, allPlayersEver, allSpectatorsEver, replayList); + + for (int i = 0; i < replayList.size(); ++i) + delete replayList[i]; qDebug() << "Server_Game destructor: gameId=" << gameId; } @@ -176,9 +181,9 @@ void Server_Game::sendGameStateToPlayers() omniscientEvent.add_player_list()->CopyFrom(omniscientGameStateIterator.next()); GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1); - replayCont->set_seconds_elapsed(secondsElapsed); + replayCont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); replayCont->clear_game_id(); - replay->add_event_list()->CopyFrom(*replayCont); + currentReplay->add_event_list()->CopyFrom(*replayCont); delete replayCont; // If spectators are not omniscient, we need an additional getGameState call, otherwise we can use the data we used for the replay. @@ -237,6 +242,27 @@ void Server_Game::doStartGameIfReady() player->setReadyStart(false); } + if (firstGameStarted) { + currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); + replayList.append(currentReplay); + currentReplay = new GameReplay; + currentReplay->mutable_game_info()->CopyFrom(getInfo()); + + Event_GameStateChanged omniscientEvent; + QListIterator omniscientGameStateIterator(getGameState(0, true, true)); + while (omniscientGameStateIterator.hasNext()) + omniscientEvent.add_player_list()->CopyFrom(omniscientGameStateIterator.next()); + + GameEventContainer *replayCont = prepareGameEvent(omniscientEvent, -1); + replayCont->set_seconds_elapsed(0); + replayCont->clear_game_id(); + currentReplay->add_event_list()->CopyFrom(*replayCont); + delete replayCont; + + startTimeOfThisGame = secondsElapsed; + } else + firstGameStarted = true; + sendGameStateToPlayers(); activePlayer = -1; @@ -624,9 +650,9 @@ void Server_Game::sendGameEventContainer(GameEventContainer *cont, GameEventStor p->sendGameEvent(*cont); } if (recipients.testFlag(GameEventStorageItem::SendToPrivate)) { - cont->set_seconds_elapsed(secondsElapsed); + cont->set_seconds_elapsed(secondsElapsed - startTimeOfThisGame); cont->clear_game_id(); - replay->add_event_list()->CopyFrom(*cont); + currentReplay->add_event_list()->CopyFrom(*cont); } delete cont; diff --git a/common/server_game.h b/common/server_game.h index e7efd1ba..5e3e3803 100644 --- a/common/server_game.h +++ b/common/server_game.h @@ -59,10 +59,12 @@ private: bool spectatorsCanTalk; bool spectatorsSeeEverything; int inactivityCounter; - int secondsElapsed; + int startTimeOfThisGame, secondsElapsed; + bool firstGameStarted; QDateTime startTime; QTimer *pingClock; - GameReplay *replay; + QList replayList; + GameReplay *currentReplay; signals: void sigStartGameIfReady(); private slots: diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index b0c07f6f..3ec03785 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -538,42 +538,21 @@ void Servatrice::statusUpdate() execSqlQuery(query); } -void Servatrice::storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const GameReplay &replay) +void Servatrice::storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replayList) { - Server_Room *room = rooms.value(replay.game_info().room_id()); + const ServerInfo_Game &gameInfo = replayList.first()->game_info(); + + Server_Room *room = rooms.value(gameInfo.room_id()); const QStringList &allGameTypes = room->getGameTypes(); QStringList gameTypes; - for (int i = replay.game_info().game_types_size() - 1; i >= 0; --i) - gameTypes.append(allGameTypes[replay.game_info().game_types(i)]); - - QByteArray replayBlob; - const unsigned int size = replay.ByteSize(); - replayBlob.resize(size); - replay.SerializeToArray(replayBlob.data(), size); - - QMutexLocker locker(&dbMutex); - if (!checkSql()) - return; - - QSqlQuery query1; - query1.prepare("insert into " + dbPrefix + "_games (room_name, id, descr, creator_name, password, game_types, player_count, time_started, time_finished, replay) values (:id_room, :id_game, :descr, :creator_name, :password, :game_types, :player_count, date_sub(now(), interval :seconds second), now(), :replay)"); - query1.bindValue(":room_name", room->getName()); - query1.bindValue(":id_game", replay.game_info().game_id()); - query1.bindValue(":descr", QString::fromStdString(replay.game_info().description())); - query1.bindValue(":creator_name", QString::fromStdString(replay.game_info().creator_info().name())); - query1.bindValue(":password", replay.game_info().with_password() ? 1 : 0); - query1.bindValue(":game_types", gameTypes.isEmpty() ? QString("") : gameTypes.join(", ")); - query1.bindValue(":player_count", replay.game_info().max_players()); - query1.bindValue(":seconds", secondsElapsed); - query1.bindValue(":replay", replayBlob); - if (!execSqlQuery(query1)) - return; + for (int i = gameInfo.game_types_size() - 1; i >= 0; --i) + gameTypes.append(allGameTypes[gameInfo.game_types(i)]); QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames; QSetIterator playerIterator(allPlayersEver); while (playerIterator.hasNext()) { - gameIds1.append(replay.game_info().game_id()); + gameIds1.append(gameInfo.game_id()); playerNames.append(playerIterator.next()); } QSet allUsersInGame = allPlayersEver + allSpectatorsEver; @@ -582,17 +561,53 @@ void Servatrice::storeGameInformation(int secondsElapsed, const QSet &a int id = getUserIdInDB(allUsersIterator.next()); if (id == -1) continue; - gameIds2.append(replay.game_info().game_id()); + gameIds2.append(gameInfo.game_id()); userIds.append(id); - replayNames.append(QString::fromStdString(replay.game_info().description())); + replayNames.append(QString::fromStdString(gameInfo.description())); } + QVariantList replayGameIds, replayDurations, replayBlobs; + for (int i = 0; i < replayList.size(); ++i) { + QByteArray blob; + const unsigned int size = replayList[i]->ByteSize(); + blob.resize(size); + replayList[i]->SerializeToArray(blob.data(), size); + + replayGameIds.append(gameInfo.game_id()); + replayDurations.append(replayList[i]->duration_seconds()); + replayBlobs.append(blob); + } + + QMutexLocker locker(&dbMutex); + if (!checkSql()) + return; + + QSqlQuery query1; + query1.prepare("insert into " + dbPrefix + "_games (room_name, id, descr, creator_name, password, game_types, player_count, time_started, time_finished) values (:id_room, :id_game, :descr, :creator_name, :password, :game_types, :player_count, date_sub(now(), interval :seconds second), now())"); + query1.bindValue(":room_name", room->getName()); + query1.bindValue(":id_game", gameInfo.game_id()); + query1.bindValue(":descr", QString::fromStdString(gameInfo.description())); + query1.bindValue(":creator_name", QString::fromStdString(gameInfo.creator_info().name())); + query1.bindValue(":password", gameInfo.with_password() ? 1 : 0); + query1.bindValue(":game_types", gameTypes.isEmpty() ? QString("") : gameTypes.join(", ")); + query1.bindValue(":player_count", gameInfo.max_players()); + query1.bindValue(":seconds", secondsElapsed); + if (!execSqlQuery(query1)) + return; + QSqlQuery query2; query2.prepare("insert into " + dbPrefix + "_games_players (id_game, player_name) values (:id_game, :player_name)"); query2.bindValue(":id_game", gameIds1); query2.bindValue(":player_name", playerNames); query2.execBatch(); + QSqlQuery replayQuery1; + replayQuery1.prepare("insert into " + dbPrefix + "_replays (id_game, duration, replay) values (:id_game, :duration, :replay)"); + replayQuery1.bindValue(":id_game", replayGameIds); + replayQuery1.bindValue(":duration", replayDurations); + replayQuery1.bindValue(":replay", replayBlobs); + replayQuery1.execBatch(); + QSqlQuery query3; query3.prepare("insert into " + dbPrefix + "_replays_access (id_game, id_player, replay_name) values (:id_game, :id_player, :replay_name)"); query3.bindValue(":id_game", gameIds2); diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index 8aa84fef..99a22300 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -82,7 +82,7 @@ public: void incTxBytes(quint64 num); void incRxBytes(quint64 num); int getUserIdInDB(const QString &name); - void storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const GameReplay &replay); + void storeGameInformation(int secondsElapsed, const QSet &allPlayersEver, const QSet &allSpectatorsEver, const QList &replays); protected: int startSession(const QString &userName, const QString &address); void endSession(int sessionId); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index c46fdc87..95a844ad 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -503,23 +503,35 @@ Response::ResponseCode ServerSocketInterface::cmdReplayList(const Command_Replay query1.bindValue(":id_player", userInfo->id()); servatrice->execSqlQuery(query1); while (query1.next()) { - ServerInfo_Replay *replayInfo = re->add_replay_list(); + ServerInfo_ReplayMatch *matchInfo = re->add_match_list(); + const int gameId = query1.value(0).toInt(); - replayInfo->set_game_id(gameId); - replayInfo->set_room_name(query1.value(2).toString().toStdString()); + matchInfo->set_game_id(gameId); + matchInfo->set_room_name(query1.value(2).toString().toStdString()); const int timeStarted = query1.value(3).toDateTime().toTime_t(); const int timeFinished = query1.value(4).toDateTime().toTime_t(); - replayInfo->set_time_started(timeStarted); - replayInfo->set_length(timeFinished - timeStarted); - replayInfo->set_game_name(query1.value(5).toString().toStdString()); - replayInfo->set_replay_name(query1.value(1).toString().toStdString()); + matchInfo->set_time_started(timeStarted); + matchInfo->set_length(timeFinished - timeStarted); + matchInfo->set_game_name(query1.value(5).toString().toStdString()); + const QString replayName = query1.value(1).toString(); QSqlQuery query2; query2.prepare("select player_name from cockatrice_games_players where id_game = :id_game"); query2.bindValue(":id_game", gameId); servatrice->execSqlQuery(query2); while (query2.next()) - replayInfo->add_player_names(query2.value(0).toString().toStdString()); + matchInfo->add_player_names(query2.value(0).toString().toStdString()); + + QSqlQuery query3; + query3.prepare("select id, duration from " + servatrice->getDbPrefix() + "_replays where id_game = :id_game"); + query3.bindValue(":id_game", gameId); + servatrice->execSqlQuery(query3); + while (query3.next()) { + ServerInfo_Replay *replayInfo = matchInfo->add_replay_list(); + replayInfo->set_replay_id(query3.value(0).toInt()); + replayInfo->set_replay_name(replayName.toStdString()); + replayInfo->set_duration(query3.value(1).toInt()); + } } servatrice->dbMutex.unlock(); @@ -535,8 +547,8 @@ Response::ResponseCode ServerSocketInterface::cmdReplayDownload(const Command_Re QMutexLocker dbLocker(&servatrice->dbMutex); QSqlQuery query1; - query1.prepare("select 1 from " + servatrice->getDbPrefix() + "_replays_access where id_game = :id_game and id_player = :id_player"); - query1.bindValue(":id_game", cmd.game_id()); + query1.prepare("select 1 from " + servatrice->getDbPrefix() + "_replays_access a left join " + servatrice->getDbPrefix() + "_replays b on a.id_game = b.id_game where b.id = :id_replay and a.id_player = :id_player"); + query1.bindValue(":id_replay", cmd.replay_id()); query1.bindValue(":id_player", userInfo->id()); if (!servatrice->execSqlQuery(query1)) return Response::RespInternalError; @@ -544,8 +556,8 @@ Response::ResponseCode ServerSocketInterface::cmdReplayDownload(const Command_Re return Response::RespAccessDenied; QSqlQuery query2; - query2.prepare("select replay from " + servatrice->getDbPrefix() + "_games where id = :id_game"); - query2.bindValue(":id_game", cmd.game_id()); + query2.prepare("select replay from " + servatrice->getDbPrefix() + "_replays where id = :id_replay"); + query2.bindValue(":id_replay", cmd.replay_id()); if (!servatrice->execSqlQuery(query2)) return Response::RespInternalError; if (!query2.next()) From a27bc20887f555c35be2b0fc4fd2464a18622d9d Mon Sep 17 00:00:00 2001 From: Max-Wilhelm Bruker Date: Fri, 2 Mar 2012 20:45:57 +0100 Subject: [PATCH 2/6] added missing file --- common/pb/serverinfo_replay_match.proto | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 common/pb/serverinfo_replay_match.proto diff --git a/common/pb/serverinfo_replay_match.proto b/common/pb/serverinfo_replay_match.proto new file mode 100644 index 00000000..775835b6 --- /dev/null +++ b/common/pb/serverinfo_replay_match.proto @@ -0,0 +1,12 @@ +import "serverinfo_replay.proto"; + +message ServerInfo_ReplayMatch { + repeated ServerInfo_Replay replay_list = 1; + + optional sint32 game_id = 2 [default = -1]; + optional string room_name = 3; + optional uint32 time_started = 4; + optional uint32 length = 5; + optional string game_name = 6; + repeated string player_names = 7; +} From 2487476fcc917c46ef14b8be60a57105e070328c Mon Sep 17 00:00:00 2001 From: Max-Wilhelm Bruker Date: Sat, 3 Mar 2012 15:54:50 +0100 Subject: [PATCH 3/6] added checkbox to disable password storage --- cockatrice/src/dlg_connect.cpp | 8 +++++++- cockatrice/src/dlg_connect.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/dlg_connect.cpp b/cockatrice/src/dlg_connect.cpp index 0497448d..a851c268 100644 --- a/cockatrice/src/dlg_connect.cpp +++ b/cockatrice/src/dlg_connect.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include "dlg_connect.h" @@ -27,6 +28,9 @@ DlgConnect::DlgConnect(QWidget *parent) passwordEdit = new QLineEdit(settings.value("password").toString()); passwordLabel->setBuddy(passwordEdit); passwordEdit->setEchoMode(QLineEdit::Password); + + savePasswordCheckBox = new QCheckBox(tr("&Save password")); + savePasswordCheckBox->setChecked(settings.value("save_password", 1).toInt()); okButton = new QPushButton(tr("&OK")); okButton->setDefault(true); @@ -41,6 +45,7 @@ DlgConnect::DlgConnect(QWidget *parent) grid->addWidget(playernameEdit, 2, 1); grid->addWidget(passwordLabel, 3, 0); grid->addWidget(passwordEdit, 3, 1); + grid->addWidget(savePasswordCheckBox, 4, 0, 1, 2); QHBoxLayout *buttonLayout = new QHBoxLayout; buttonLayout->addStretch(); @@ -67,7 +72,8 @@ void DlgConnect::actOk() settings.setValue("hostname", hostEdit->text()); settings.setValue("port", portEdit->text()); settings.setValue("playername", playernameEdit->text()); - settings.setValue("password", passwordEdit->text()); + settings.setValue("password", savePasswordCheckBox->isChecked() ? passwordEdit->text() : QString()); + settings.setValue("save_password", savePasswordCheckBox->isChecked() ? 1 : 0); settings.endGroup(); accept(); diff --git a/cockatrice/src/dlg_connect.h b/cockatrice/src/dlg_connect.h index 036595f3..ad29c4de 100644 --- a/cockatrice/src/dlg_connect.h +++ b/cockatrice/src/dlg_connect.h @@ -6,6 +6,7 @@ class QLabel; class QPushButton; +class QCheckBox; class DlgConnect : public QDialog { Q_OBJECT @@ -20,6 +21,7 @@ private slots: private: QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel; QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit; + QCheckBox *savePasswordCheckBox; QPushButton *okButton, *cancelButton; }; From a876a0bf5f7aae08dc001046f79265b7c31f1932 Mon Sep 17 00:00:00 2001 From: Max-Wilhelm Bruker Date: Sat, 3 Mar 2012 18:10:16 +0100 Subject: [PATCH 4/6] tell logged in clients about new replays --- cockatrice/src/abstractclient.cpp | 2 + cockatrice/src/abstractclient.h | 2 + .../src/remotereplaylist_treewidget.cpp | 16 ++++++- cockatrice/src/remotereplaylist_treewidget.h | 4 +- cockatrice/src/tab_replays.cpp | 8 ++++ cockatrice/src/tab_replays.h | 5 ++- common/pb/CMakeLists.txt | 1 + common/pb/event_replay_added.proto | 9 ++++ common/pb/game_replay.proto | 7 +-- common/pb/session_event.proto | 1 + common/server.cpp | 2 +- common/server.h | 3 +- common/server_game.cpp | 2 + servatrice/src/servatrice.cpp | 44 +++++++++++++++++-- 14 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 common/pb/event_replay_added.proto diff --git a/cockatrice/src/abstractclient.cpp b/cockatrice/src/abstractclient.cpp index ce3cdfe8..8d27be49 100644 --- a/cockatrice/src/abstractclient.cpp +++ b/cockatrice/src/abstractclient.cpp @@ -14,6 +14,7 @@ #include "pb/event_user_joined.pb.h" #include "pb/event_user_left.pb.h" #include "pb/event_game_joined.pb.h" +#include "pb/event_replay_added.pb.h" #include "get_pb_extension.h" #include @@ -55,6 +56,7 @@ void AbstractClient::processProtocolItem(const ServerMessage &item) case SessionEvent::USER_JOINED: emit userJoinedEventReceived(event.GetExtension(Event_UserJoined::ext)); break; case SessionEvent::USER_LEFT: emit userLeftEventReceived(event.GetExtension(Event_UserLeft::ext)); break; case SessionEvent::GAME_JOINED: emit gameJoinedEventReceived(event.GetExtension(Event_GameJoined::ext)); break; + case SessionEvent::REPLAY_ADDED: emit replayAddedEventReceived(event.GetExtension(Event_ReplayAdded::ext)); break; } break; } diff --git a/cockatrice/src/abstractclient.h b/cockatrice/src/abstractclient.h index 86bd8266..3a1cbb9d 100644 --- a/cockatrice/src/abstractclient.h +++ b/cockatrice/src/abstractclient.h @@ -22,6 +22,7 @@ class Event_GameJoined; class Event_UserMessage; class Event_ConnectionClosed; class Event_ServerShutdown; +class Event_ReplayAdded; enum ClientStatus { StatusDisconnected, @@ -57,6 +58,7 @@ signals: void userInfoChanged(const ServerInfo_User &userInfo); void buddyListReceived(const QList &buddyList); void ignoreListReceived(const QList &ignoreList); + void replayAddedEventReceived(const Event_ReplayAdded &event); private: int nextCmdId; protected slots: diff --git a/cockatrice/src/remotereplaylist_treewidget.cpp b/cockatrice/src/remotereplaylist_treewidget.cpp index d978fe33..e8e4a5d1 100644 --- a/cockatrice/src/remotereplaylist_treewidget.cpp +++ b/cockatrice/src/remotereplaylist_treewidget.cpp @@ -13,7 +13,7 @@ RemoteReplayList_TreeModel::MatchNode::MatchNode(const ServerInfo_ReplayMatch &_ : RemoteReplayList_TreeModel::Node(QString::fromStdString(_matchInfo.game_name())), matchInfo(_matchInfo) { for (int i = 0; i < matchInfo.replay_list_size(); ++i) - append(new ReplayNode(matchInfo.replay_list(i))); + append(new ReplayNode(matchInfo.replay_list(i), this)); } RemoteReplayList_TreeModel::MatchNode::~MatchNode() @@ -200,6 +200,15 @@ void RemoteReplayList_TreeModel::refreshTree() client->sendCommand(pend); } +void RemoteReplayList_TreeModel::addMatchInfo(const ServerInfo_ReplayMatch &matchInfo) +{ + beginInsertRows(QModelIndex(), replayMatches.size(), replayMatches.size()); + replayMatches.append(new MatchNode(matchInfo)); + endInsertRows(); + + emit treeRefreshed(); +} + void RemoteReplayList_TreeModel::replayListFinished(const Response &r) { const Response_ReplayList &resp = r.GetExtension(Response_ReplayList::ext); @@ -236,3 +245,8 @@ ServerInfo_Replay const *RemoteReplayList_TreeWidget::getCurrentReplay() const { return treeModel->getReplay(proxyModel->mapToSource(selectionModel()->currentIndex())); } + +void RemoteReplayList_TreeWidget::addMatchInfo(const ServerInfo_ReplayMatch &matchInfo) +{ + treeModel->addMatchInfo(matchInfo); +} diff --git a/cockatrice/src/remotereplaylist_treewidget.h b/cockatrice/src/remotereplaylist_treewidget.h index 9c1567ea..299bf59e 100644 --- a/cockatrice/src/remotereplaylist_treewidget.h +++ b/cockatrice/src/remotereplaylist_treewidget.h @@ -39,7 +39,7 @@ private: MatchNode *parent; ServerInfo_Replay replayInfo; public: - ReplayNode(const ServerInfo_Replay &_replayInfo, MatchNode *_parent = 0) + ReplayNode(const ServerInfo_Replay &_replayInfo, MatchNode *_parent) : Node(QString::fromStdString(_replayInfo.replay_name())), parent(_parent), replayInfo(_replayInfo) { } MatchNode *getParent() const { return parent; } const ServerInfo_Replay &getReplayInfo() { return replayInfo; } @@ -67,6 +67,7 @@ public: void refreshTree(); ServerInfo_Replay const* getReplay(const QModelIndex &index) const; ServerInfo_ReplayMatch const* getReplayMatch(const QModelIndex &index) const; + void addMatchInfo(const ServerInfo_ReplayMatch &matchInfo); }; class RemoteReplayList_TreeWidget : public QTreeView { @@ -79,6 +80,7 @@ public: ServerInfo_Replay const *getCurrentReplay() const; ServerInfo_ReplayMatch const *getCurrentReplayMatch() const; void refreshTree(); + void addMatchInfo(const ServerInfo_ReplayMatch &matchInfo); }; #endif diff --git a/cockatrice/src/tab_replays.cpp b/cockatrice/src/tab_replays.cpp index bc430a10..5a9372ce 100644 --- a/cockatrice/src/tab_replays.cpp +++ b/cockatrice/src/tab_replays.cpp @@ -20,6 +20,7 @@ #include "pb/response.pb.h" #include "pb/response_replay_download.pb.h" #include "pb/command_replay_download.pb.h" +#include "pb/event_replay_added.pb.h" TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client) : Tab(_tabSupervisor), client(_client) @@ -90,6 +91,8 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client) retranslateUi(); setLayout(hbox); + + connect(client, SIGNAL(replayAddedEventReceived(const Event_ReplayAdded &)), this, SLOT(replayAddedEventReceived(const Event_ReplayAdded &))); } void TabReplays::retranslateUi() @@ -183,3 +186,8 @@ void TabReplays::downloadFinished(const Response &r) f.write((const char *) data.data(), data.size()); f.close(); } + +void TabReplays::replayAddedEventReceived(const Event_ReplayAdded &event) +{ + serverDirView->addMatchInfo(event.match_info()); +} diff --git a/cockatrice/src/tab_replays.h b/cockatrice/src/tab_replays.h index d564e531..70ea86eb 100644 --- a/cockatrice/src/tab_replays.h +++ b/cockatrice/src/tab_replays.h @@ -2,8 +2,8 @@ #define TAB_REPLAYS_H #include "tab.h" -#include "pb/response.pb.h" +class Response; class AbstractClient; class QTreeView; class QFileSystemModel; @@ -12,6 +12,7 @@ class QToolBar; class QGroupBox; class RemoteReplayList_TreeWidget; class GameReplay; +class Event_ReplayAdded; class TabReplays : public Tab { Q_OBJECT @@ -33,6 +34,8 @@ private slots: void actDownload(); void downloadFinished(const Response &r); + + void replayAddedEventReceived(const Event_ReplayAdded &event); signals: void openReplay(GameReplay *replay); public: diff --git a/common/pb/CMakeLists.txt b/common/pb/CMakeLists.txt index f60ca819..d5efd237 100644 --- a/common/pb/CMakeLists.txt +++ b/common/pb/CMakeLists.txt @@ -78,6 +78,7 @@ SET(PROTO_FILES event_move_card.proto event_player_properties_changed.proto event_remove_from_list.proto + event_replay_added.proto event_reveal_cards.proto event_roll_die.proto event_room_say.proto diff --git a/common/pb/event_replay_added.proto b/common/pb/event_replay_added.proto new file mode 100644 index 00000000..c3de2082 --- /dev/null +++ b/common/pb/event_replay_added.proto @@ -0,0 +1,9 @@ +import "session_event.proto"; +import "serverinfo_replay_match.proto"; + +message Event_ReplayAdded { + extend SessionEvent { + optional Event_ReplayAdded ext = 1100; + } + optional ServerInfo_ReplayMatch match_info = 1; +} diff --git a/common/pb/game_replay.proto b/common/pb/game_replay.proto index e3438e66..05bc67d0 100644 --- a/common/pb/game_replay.proto +++ b/common/pb/game_replay.proto @@ -2,7 +2,8 @@ import "serverinfo_game.proto"; import "game_event_container.proto"; message GameReplay { - optional ServerInfo_Game game_info = 1; - repeated GameEventContainer event_list = 2; - optional uint32 duration_seconds = 3; + optional uint64 replay_id = 1; + optional ServerInfo_Game game_info = 2; + repeated GameEventContainer event_list = 3; + optional uint32 duration_seconds = 4; } diff --git a/common/pb/session_event.proto b/common/pb/session_event.proto index f97778ab..a0b6254d 100644 --- a/common/pb/session_event.proto +++ b/common/pb/session_event.proto @@ -11,6 +11,7 @@ message SessionEvent { USER_JOINED = 1007; USER_LEFT = 1008; GAME_JOINED = 1009; + REPLAY_ADDED = 1100; } extensions 100 to max; } diff --git a/common/server.cpp b/common/server.cpp index 886fd9df..75633dc6 100644 --- a/common/server.cpp +++ b/common/server.cpp @@ -29,7 +29,7 @@ #include Server::Server(QObject *parent) - : QObject(parent), serverMutex(QMutex::Recursive), nextGameId(0) + : QObject(parent), serverMutex(QMutex::Recursive), nextGameId(0), nextReplayId(0) { } diff --git a/common/server.h b/common/server.h index b696190a..e05a67f2 100644 --- a/common/server.h +++ b/common/server.h @@ -28,6 +28,7 @@ public: AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason); const QMap &getRooms() { return rooms; } int getNextGameId() { return nextGameId++; } + int getNextReplayId() { return nextReplayId++; } const QMap &getUsers() const { return users; } void addClient(Server_ProtocolHandler *player); @@ -62,7 +63,7 @@ protected: virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0; int getUsersCount() const; int getGamesCount() const; - int nextGameId; + int nextGameId, nextReplayId; void addRoom(Server_Room *newRoom); }; diff --git a/common/server_game.cpp b/common/server_game.cpp index 99887d78..ef2d152d 100644 --- a/common/server_game.cpp +++ b/common/server_game.cpp @@ -48,6 +48,7 @@ Server_Game::Server_Game(Server_ProtocolHandler *_creator, int _gameId, const QS : QObject(), room(_room), hostId(0), creatorInfo(new ServerInfo_User(_creator->copyUserInfo(false))), gameStarted(false), gameId(_gameId), description(_description), password(_password), maxPlayers(_maxPlayers), gameTypes(_gameTypes), activePlayer(-1), activePhase(-1), onlyBuddies(_onlyBuddies), onlyRegistered(_onlyRegistered), spectatorsAllowed(_spectatorsAllowed), spectatorsNeedPassword(_spectatorsNeedPassword), spectatorsCanTalk(_spectatorsCanTalk), spectatorsSeeEverything(_spectatorsSeeEverything), inactivityCounter(0), startTimeOfThisGame(0), secondsElapsed(0), firstGameStarted(false), startTime(QDateTime::currentDateTime()), gameMutex(QMutex::Recursive) { currentReplay = new GameReplay; + currentReplay->set_replay_id(room->getServer()->getNextReplayId()); connect(this, SIGNAL(sigStartGameIfReady()), this, SLOT(doStartGameIfReady()), Qt::QueuedConnection); @@ -246,6 +247,7 @@ void Server_Game::doStartGameIfReady() currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame); replayList.append(currentReplay); currentReplay = new GameReplay; + currentReplay->set_replay_id(room->getServer()->getNextReplayId()); currentReplay->mutable_game_info()->CopyFrom(getInfo()); Event_GameStateChanged omniscientEvent; diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 3ec03785..df47803a 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -29,6 +29,7 @@ #include "main.h" #include "passwordhasher.h" #include "pb/game_replay.pb.h" +#include "pb/event_replay_added.pb.h" #include "pb/event_server_message.pb.h" #include "pb/event_server_shutdown.pb.h" #include "pb/event_connection_closed.pb.h" @@ -159,6 +160,15 @@ bool Servatrice::openDatabase() nextGameId = query.value(0).toInt() + 1; qDebug() << "set nextGameId to " << nextGameId; } + if (!nextReplayId) { + QSqlQuery query; + if (!query.exec("select max(id) from " + dbPrefix + "_replays")) + return false; + if (!query.next()) + return false; + nextReplayId = query.value(0).toInt() + 1; + qDebug() << "set nextReplayId to " << nextReplayId; + } return true; } @@ -544,6 +554,14 @@ void Servatrice::storeGameInformation(int secondsElapsed, const QSet &a Server_Room *room = rooms.value(gameInfo.room_id()); + 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) @@ -553,7 +571,9 @@ void Servatrice::storeGameInformation(int secondsElapsed, const QSet &a QSetIterator playerIterator(allPlayersEver); while (playerIterator.hasNext()) { gameIds1.append(gameInfo.game_id()); - playerNames.append(playerIterator.next()); + const QString &playerName = playerIterator.next(); + playerNames.append(playerName); + replayMatchInfo->add_player_names(playerName.toStdString()); } QSet allUsersInGame = allPlayersEver + allSpectatorsEver; QSetIterator allUsersIterator(allUsersInGame); @@ -566,18 +586,35 @@ void Servatrice::storeGameInformation(int secondsElapsed, const QSet &a replayNames.append(QString::fromStdString(gameInfo.description())); } - QVariantList replayGameIds, replayDurations, replayBlobs; + QVariantList replayIds, replayGameIds, replayDurations, replayBlobs; for (int i = 0; i < replayList.size(); ++i) { QByteArray blob; const unsigned int size = replayList[i]->ByteSize(); blob.resize(size); replayList[i]->SerializeToArray(blob.data(), size); + replayIds.append(QVariant((qulonglong) replayList[i]->replay_id())); replayGameIds.append(gameInfo.game_id()); replayDurations.append(replayList[i]->duration_seconds()); replayBlobs.append(blob); + + 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()); } + SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent); + allUsersIterator.toFront(); + serverMutex.lock(); + while (allUsersIterator.hasNext()) { + Server_ProtocolHandler *userHandler = users.value(allUsersIterator.next()); + if (userHandler) + userHandler->sendProtocolItem(*sessionEvent); + } + serverMutex.unlock(); + delete sessionEvent; + QMutexLocker locker(&dbMutex); if (!checkSql()) return; @@ -602,7 +639,8 @@ void Servatrice::storeGameInformation(int secondsElapsed, const QSet &a query2.execBatch(); QSqlQuery replayQuery1; - replayQuery1.prepare("insert into " + dbPrefix + "_replays (id_game, duration, replay) values (:id_game, :duration, :replay)"); + replayQuery1.prepare("insert into " + dbPrefix + "_replays (id, id_game, duration, replay) values (:id_replay, :id_game, :duration, :replay)"); + replayQuery1.bindValue(":id_replay", replayIds); replayQuery1.bindValue(":id_game", replayGameIds); replayQuery1.bindValue(":duration", replayDurations); replayQuery1.bindValue(":replay", replayBlobs); From acb03c2bf20809cea241dcb9cada032174bbe434 Mon Sep 17 00:00:00 2001 From: Max-Wilhelm Bruker Date: Sun, 4 Mar 2012 11:35:56 +0100 Subject: [PATCH 5/6] replays expire after two weeks, lock can be toggled to prevent that --- cockatrice/cockatrice.qrc | 1 + cockatrice/src/localserverinterface.h | 1 + .../src/remotereplaylist_treewidget.cpp | 56 +++++++++++++------ cockatrice/src/remotereplaylist_treewidget.h | 11 +++- cockatrice/src/tab_replays.cpp | 35 ++++++++++++ cockatrice/src/tab_replays.h | 5 +- common/.directory | 3 +- common/pb/CMakeLists.txt | 1 + common/pb/command_replay_modify_match.proto | 10 ++++ common/pb/serverinfo_replay_match.proto | 1 + common/pb/session_commands.proto | 1 + common/server_protocolhandler.cpp | 2 + common/server_protocolhandler.h | 2 + servatrice/src/serversocketinterface.cpp | 20 ++++++- servatrice/src/serversocketinterface.h | 1 + 15 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 common/pb/command_replay_modify_match.proto diff --git a/cockatrice/cockatrice.qrc b/cockatrice/cockatrice.qrc index c6bb75eb..2da7f2b8 100644 --- a/cockatrice/cockatrice.qrc +++ b/cockatrice/cockatrice.qrc @@ -1,6 +1,7 @@ resources/back.svg + resources/lock.svg resources/icon_delete.svg resources/icon_tab_changed.svg resources/icon_config_general.svg diff --git a/cockatrice/src/localserverinterface.h b/cockatrice/src/localserverinterface.h index c44a32a6..5520a757 100644 --- a/cockatrice/src/localserverinterface.h +++ b/cockatrice/src/localserverinterface.h @@ -20,6 +20,7 @@ private: Response::ResponseCode cmdDeckDownload(const Command_DeckDownload & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } Response::ResponseCode cmdReplayList(const Command_ReplayList & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } + Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc) { return Response::RespFunctionNotAllowed; } Response::ResponseCode cmdBanFromServer(const Command_BanFromServer & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } diff --git a/cockatrice/src/remotereplaylist_treewidget.cpp b/cockatrice/src/remotereplaylist_treewidget.cpp index e8e4a5d1..abbdf764 100644 --- a/cockatrice/src/remotereplaylist_treewidget.cpp +++ b/cockatrice/src/remotereplaylist_treewidget.cpp @@ -9,6 +9,8 @@ #include "pb/response_replay_list.pb.h" #include "pb/serverinfo_replay.pb.h" +const int RemoteReplayList_TreeModel::numberOfColumns = 6; + RemoteReplayList_TreeModel::MatchNode::MatchNode(const ServerInfo_ReplayMatch &_matchInfo) : RemoteReplayList_TreeModel::Node(QString::fromStdString(_matchInfo.game_name())), matchInfo(_matchInfo) { @@ -22,12 +24,18 @@ RemoteReplayList_TreeModel::MatchNode::~MatchNode() delete at(i); } +void RemoteReplayList_TreeModel::MatchNode::updateMatchInfo(const ServerInfo_ReplayMatch &_matchInfo) +{ + matchInfo.MergeFrom(_matchInfo); +} + RemoteReplayList_TreeModel::RemoteReplayList_TreeModel(AbstractClient *_client, QObject *parent) : QAbstractItemModel(parent), client(_client) { QFileIconProvider fip; dirIcon = fip.icon(QFileIconProvider::Folder); fileIcon = fip.icon(QFileIconProvider::File); + lockIcon = QIcon(":/resources/lock.svg"); refreshTree(); } @@ -49,16 +57,11 @@ int RemoteReplayList_TreeModel::rowCount(const QModelIndex &parent) const return 0; } -int RemoteReplayList_TreeModel::columnCount(const QModelIndex &/*parent*/) const -{ - return 5; -} - QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - if (index.column() > 4) + if (index.column() >= numberOfColumns) return QVariant(); ReplayNode *replayNode = dynamic_cast(static_cast(index.internalPointer())); @@ -71,7 +74,7 @@ QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) co switch (index.column()) { case 0: return replayInfo.replay_id(); case 1: return QString::fromStdString(replayInfo.replay_name()); - case 4: return replayInfo.duration(); + case 5: return replayInfo.duration(); default: return QVariant(); } } @@ -94,13 +97,17 @@ QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) co playerList.append(QString::fromStdString(matchInfo.player_names(i))); return playerList.join(", "); } - case 3: return QDateTime::fromTime_t(matchInfo.time_started()); - case 4: return matchInfo.length(); + case 4: return QDateTime::fromTime_t(matchInfo.time_started()); + case 5: return matchInfo.length(); default: return QVariant(); } } case Qt::DecorationRole: - return index.column() == 0 ? dirIcon : QVariant(); + switch (index.column()) { + case 0: return dirIcon; + case 3: return matchInfo.do_not_hide() ? lockIcon : QVariant(); + default: return QVariant(); + } } } return QVariant(); @@ -118,8 +125,9 @@ QVariant RemoteReplayList_TreeModel::headerData(int section, Qt::Orientation ori case 0: return tr("ID"); case 1: return tr("Name"); case 2: return tr("Players"); - case 3: return tr("Time started"); - case 4: return tr("Duration (sec)"); + case 3: return tr("Keep"); + case 4: return tr("Time started"); + case 5: return tr("Duration (sec)"); default: return QVariant(); } } @@ -180,9 +188,13 @@ ServerInfo_ReplayMatch const* RemoteReplayList_TreeModel::getReplayMatch(const Q return 0; MatchNode *node = dynamic_cast(static_cast(index.internalPointer())); - if (!node) - return 0; - return &node->getMatchInfo(); + if (!node) { + ReplayNode *node = dynamic_cast(static_cast(index.internalPointer())); + if (!node) + return 0; + return &node->getParent()->getMatchInfo(); + } else + return &node->getMatchInfo(); } void RemoteReplayList_TreeModel::clearTree() @@ -209,6 +221,16 @@ void RemoteReplayList_TreeModel::addMatchInfo(const ServerInfo_ReplayMatch &matc emit treeRefreshed(); } +void RemoteReplayList_TreeModel::updateMatchInfo(int gameId, const ServerInfo_ReplayMatch &matchInfo) +{ + for (int i = 0; i < replayMatches.size(); ++i) + if (replayMatches[i]->getMatchInfo().game_id() == gameId) { + replayMatches[i]->updateMatchInfo(matchInfo); + emit dataChanged(createIndex(i, 0, (void *) replayMatches[i]), createIndex(i, numberOfColumns - 1, (void *) replayMatches[i])); + break; + } +} + void RemoteReplayList_TreeModel::replayListFinished(const Response &r) { const Response_ReplayList &resp = r.GetExtension(Response_ReplayList::ext); @@ -246,7 +268,7 @@ ServerInfo_Replay const *RemoteReplayList_TreeWidget::getCurrentReplay() const return treeModel->getReplay(proxyModel->mapToSource(selectionModel()->currentIndex())); } -void RemoteReplayList_TreeWidget::addMatchInfo(const ServerInfo_ReplayMatch &matchInfo) +ServerInfo_ReplayMatch const *RemoteReplayList_TreeWidget::getCurrentReplayMatch() const { - treeModel->addMatchInfo(matchInfo); + return treeModel->getReplayMatch(proxyModel->mapToSource(selectionModel()->currentIndex())); } diff --git a/cockatrice/src/remotereplaylist_treewidget.h b/cockatrice/src/remotereplaylist_treewidget.h index 299bf59e..e9e695ce 100644 --- a/cockatrice/src/remotereplaylist_treewidget.h +++ b/cockatrice/src/remotereplaylist_treewidget.h @@ -33,6 +33,7 @@ private: ~MatchNode(); void clearTree(); const ServerInfo_ReplayMatch &getMatchInfo() { return matchInfo; } + void updateMatchInfo(const ServerInfo_ReplayMatch &_matchInfo); }; class ReplayNode : public Node { private: @@ -48,8 +49,10 @@ private: AbstractClient *client; QList replayMatches; - QIcon dirIcon, fileIcon; + QIcon dirIcon, fileIcon, lockIcon; void clearTree(); + + static const int numberOfColumns; signals: void treeRefreshed(); private slots: @@ -58,7 +61,7 @@ public: RemoteReplayList_TreeModel(AbstractClient *_client, QObject *parent = 0); ~RemoteReplayList_TreeModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const; - int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const; + int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const { return numberOfColumns; } QVariant data(const QModelIndex &index, int role) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; @@ -68,6 +71,7 @@ public: ServerInfo_Replay const* getReplay(const QModelIndex &index) const; ServerInfo_ReplayMatch const* getReplayMatch(const QModelIndex &index) const; void addMatchInfo(const ServerInfo_ReplayMatch &matchInfo); + void updateMatchInfo(int gameId, const ServerInfo_ReplayMatch &matchInfo); }; class RemoteReplayList_TreeWidget : public QTreeView { @@ -80,7 +84,8 @@ public: ServerInfo_Replay const *getCurrentReplay() const; ServerInfo_ReplayMatch const *getCurrentReplayMatch() const; void refreshTree(); - void addMatchInfo(const ServerInfo_ReplayMatch &matchInfo); + void addMatchInfo(const ServerInfo_ReplayMatch &matchInfo) { treeModel->addMatchInfo(matchInfo); } + void updateMatchInfo(int gameId, const ServerInfo_ReplayMatch &matchInfo) { treeModel->updateMatchInfo(gameId, matchInfo); } }; #endif diff --git a/cockatrice/src/tab_replays.cpp b/cockatrice/src/tab_replays.cpp index 5a9372ce..9e4ea43b 100644 --- a/cockatrice/src/tab_replays.cpp +++ b/cockatrice/src/tab_replays.cpp @@ -20,6 +20,7 @@ #include "pb/response.pb.h" #include "pb/response_replay_download.pb.h" #include "pb/command_replay_download.pb.h" +#include "pb/command_replay_modify_match.pb.h" #include "pb/event_replay_added.pb.h" TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client) @@ -84,10 +85,14 @@ TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client) aDownload = new QAction(this); aDownload->setIcon(QIcon(":/resources/arrow_left_green.svg")); connect(aDownload, SIGNAL(triggered()), this, SLOT(actDownload())); + aKeep = new QAction(this); + aKeep->setIcon(QIcon(":/resources/lock.svg")); + connect(aKeep, SIGNAL(triggered()), this, SLOT(actKeepRemoteReplay())); leftToolBar->addAction(aOpenLocalReplay); rightToolBar->addAction(aOpenRemoteReplay); rightToolBar->addAction(aDownload); + rightToolBar->addAction(aKeep); retranslateUi(); setLayout(hbox); @@ -103,6 +108,7 @@ void TabReplays::retranslateUi() aOpenLocalReplay->setText(tr("Watch replay")); aOpenRemoteReplay->setText(tr("Watch replay")); aDownload->setText(tr("Download replay")); + aKeep->setText(tr("Toggle expiration lock")); } void TabReplays::actOpenLocalReplay() @@ -187,6 +193,35 @@ void TabReplays::downloadFinished(const Response &r) f.close(); } +void TabReplays::actKeepRemoteReplay() +{ + ServerInfo_ReplayMatch const *curRight = serverDirView->getCurrentReplayMatch(); + if (!curRight) + return; + + Command_ReplayModifyMatch cmd; + cmd.set_game_id(curRight->game_id()); + cmd.set_do_not_hide(!curRight->do_not_hide()); + + PendingCommand *pend = client->prepareSessionCommand(cmd); + connect(pend, SIGNAL(finished(const Response &)), this, SLOT(keepRemoteReplayFinished(const Response &))); + client->sendCommand(pend); +} + +void TabReplays::keepRemoteReplayFinished(const Response &r) +{ + if (r.response_code() != Response::RespOk) + return; + + PendingCommand *pend = static_cast(sender()); + const Command_ReplayModifyMatch &cmd = pend->getCommandContainer().session_command(0).GetExtension(Command_ReplayModifyMatch::ext); + + ServerInfo_ReplayMatch temp; + temp.set_do_not_hide(cmd.do_not_hide()); + + serverDirView->updateMatchInfo(cmd.game_id(), temp); +} + void TabReplays::replayAddedEventReceived(const Event_ReplayAdded &event) { serverDirView->addMatchInfo(event.match_info()); diff --git a/cockatrice/src/tab_replays.h b/cockatrice/src/tab_replays.h index 70ea86eb..0cfc6ed1 100644 --- a/cockatrice/src/tab_replays.h +++ b/cockatrice/src/tab_replays.h @@ -25,7 +25,7 @@ private: RemoteReplayList_TreeWidget *serverDirView; QGroupBox *leftGroupBox, *rightGroupBox; - QAction *aOpenLocalReplay, *aOpenRemoteReplay, *aDownload; + QAction *aOpenLocalReplay, *aOpenRemoteReplay, *aDownload, *aKeep; private slots: void actOpenLocalReplay(); @@ -35,6 +35,9 @@ private slots: void actDownload(); void downloadFinished(const Response &r); + void actKeepRemoteReplay(); + void keepRemoteReplayFinished(const Response &r); + void replayAddedEventReceived(const Event_ReplayAdded &event); signals: void openReplay(GameReplay *replay); diff --git a/common/.directory b/common/.directory index fa92fd25..8edc02d1 100644 --- a/common/.directory +++ b/common/.directory @@ -1,3 +1,4 @@ [Dolphin] -Timestamp=2012,1,1,23,11,32 +Timestamp=2012,3,4,11,22,24 Version=2 +ViewMode=1 diff --git a/common/pb/CMakeLists.txt b/common/pb/CMakeLists.txt index d5efd237..f10e55e8 100644 --- a/common/pb/CMakeLists.txt +++ b/common/pb/CMakeLists.txt @@ -32,6 +32,7 @@ SET(PROTO_FILES command_ready_start.proto command_replay_list.proto command_replay_download.proto + command_replay_modify_match.proto command_reveal_cards.proto command_roll_die.proto command_set_active_phase.proto diff --git a/common/pb/command_replay_modify_match.proto b/common/pb/command_replay_modify_match.proto new file mode 100644 index 00000000..28e0fc0a --- /dev/null +++ b/common/pb/command_replay_modify_match.proto @@ -0,0 +1,10 @@ +import "session_commands.proto"; + +message Command_ReplayModifyMatch { + extend SessionCommand { + optional Command_ReplayModifyMatch ext = 1102; + } + optional sint32 game_id = 1 [default = -1]; + optional bool do_not_hide = 2; +} + diff --git a/common/pb/serverinfo_replay_match.proto b/common/pb/serverinfo_replay_match.proto index 775835b6..64576ecc 100644 --- a/common/pb/serverinfo_replay_match.proto +++ b/common/pb/serverinfo_replay_match.proto @@ -9,4 +9,5 @@ message ServerInfo_ReplayMatch { optional uint32 length = 5; optional string game_name = 6; repeated string player_names = 7; + optional bool do_not_hide = 8; } diff --git a/common/pb/session_commands.proto b/common/pb/session_commands.proto index dc9a61cc..9c2af4e6 100644 --- a/common/pb/session_commands.proto +++ b/common/pb/session_commands.proto @@ -18,6 +18,7 @@ message SessionCommand { JOIN_ROOM = 1015; REPLAY_LIST = 1100; REPLAY_DOWNLOAD = 1101; + REPLAY_MODIFY_MATCH = 1102; } extensions 100 to max; } diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 3091c403..427bc6fe 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -50,6 +50,7 @@ #include "pb/command_deck_del.pb.h" #include "pb/command_replay_list.pb.h" #include "pb/command_replay_download.pb.h" +#include "pb/command_replay_modify_match.pb.h" #include "pb/response.pb.h" #include "pb/response_login.pb.h" #include "pb/response_list_users.pb.h" @@ -241,6 +242,7 @@ Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(co case SessionCommand::DECK_DOWNLOAD: resp = cmdDeckDownload(sc.GetExtension(Command_DeckDownload::ext), rc); break; case SessionCommand::REPLAY_LIST: resp = cmdReplayList(sc.GetExtension(Command_ReplayList::ext), rc); break; case SessionCommand::REPLAY_DOWNLOAD: resp = cmdReplayDownload(sc.GetExtension(Command_ReplayDownload::ext), rc); break; + case SessionCommand::REPLAY_MODIFY_MATCH: resp = cmdReplayModifyMatch(sc.GetExtension(Command_ReplayModifyMatch::ext), rc); break; case SessionCommand::GET_GAMES_OF_USER: resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::ext), rc); break; case SessionCommand::GET_USER_INFO: resp = cmdGetUserInfo(sc.GetExtension(Command_GetUserInfo::ext), rc); break; case SessionCommand::LIST_ROOMS: resp = cmdListRooms(sc.GetExtension(Command_ListRooms::ext), rc); break; diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 9e35790c..152dd455 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -41,6 +41,7 @@ class Command_DeckDownload; class Command_DeckUpload; class Command_ReplayList; class Command_ReplayDownload; +class Command_ReplayModifyMatch; class Command_ListRooms; class Command_JoinRoom; class Command_LeaveRoom; @@ -123,6 +124,7 @@ private: virtual Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc) = 0; virtual Response::ResponseCode cmdReplayList(const Command_ReplayList &cmd, ResponseContainer &rc) = 0; virtual Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc) = 0; + virtual Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc) = 0; Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc); Response::ResponseCode cmdListRooms(const Command_ListRooms &cmd, ResponseContainer &rc); diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 95a844ad..44612c8a 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -36,6 +36,7 @@ #include "pb/command_deck_del.pb.h" #include "pb/command_replay_list.pb.h" #include "pb/command_replay_download.pb.h" +#include "pb/command_replay_modify_match.pb.h" #include "pb/event_connection_closed.pb.h" #include "pb/event_server_message.pb.h" #include "pb/event_server_identification.pb.h" @@ -499,7 +500,7 @@ Response::ResponseCode ServerSocketInterface::cmdReplayList(const Command_Replay servatrice->dbMutex.lock(); QSqlQuery query1; - query1.prepare("select a.id_game, a.replay_name, b.room_name, b.time_started, b.time_finished, b.descr from cockatrice_replays_access a left join cockatrice_games b on b.id = a.id_game where a.id_player = :id_player"); + query1.prepare("select a.id_game, a.replay_name, b.room_name, b.time_started, b.time_finished, b.descr, a.do_not_hide from cockatrice_replays_access a left join cockatrice_games b on b.id = a.id_game where a.id_player = :id_player and (a.do_not_hide = 1 or date_add(b.time_started, interval 14 day) > now())"); query1.bindValue(":id_player", userInfo->id()); servatrice->execSqlQuery(query1); while (query1.next()) { @@ -514,6 +515,7 @@ Response::ResponseCode ServerSocketInterface::cmdReplayList(const Command_Replay matchInfo->set_length(timeFinished - timeStarted); matchInfo->set_game_name(query1.value(5).toString().toStdString()); const QString replayName = query1.value(1).toString(); + matchInfo->set_do_not_hide(query1.value(6).toBool()); QSqlQuery query2; query2.prepare("select player_name from cockatrice_games_players where id_game = :id_game"); @@ -572,6 +574,22 @@ Response::ResponseCode ServerSocketInterface::cmdReplayDownload(const Command_Re return Response::RespOk; } +Response::ResponseCode ServerSocketInterface::cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer & /*rc*/) +{ + if (authState != PasswordRight) + return Response::RespFunctionNotAllowed; + + QMutexLocker dbLocker(&servatrice->dbMutex); + + QSqlQuery query1; + query1.prepare("update " + servatrice->getDbPrefix() + "_replays_access set do_not_hide=:do_not_hide where id_player = :id_player and id_game = :id_game"); + query1.bindValue(":id_player", userInfo->id()); + query1.bindValue(":id_game", cmd.game_id()); + query1.bindValue(":do_not_hide", cmd.do_not_hide()); + + return servatrice->execSqlQuery(query1) ? Response::RespOk : Response::RespNameNotFound; +} + // MODERATOR FUNCTIONS. // May be called by admins and moderators. Permission is checked by the calling function. diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index ef0dfd48..7e04b9a5 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -63,6 +63,7 @@ private: Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc); Response::ResponseCode cmdReplayList(const Command_ReplayList &cmd, ResponseContainer &rc); Response::ResponseCode cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc); + Response::ResponseCode cmdReplayModifyMatch(const Command_ReplayModifyMatch &cmd, ResponseContainer &rc); Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); From b0378544c457d78b596d766c7ca50d2fb88ceb0f Mon Sep 17 00:00:00 2001 From: Max-Wilhelm Bruker Date: Sun, 4 Mar 2012 12:05:28 +0100 Subject: [PATCH 6/6] added icon for expiration lock button, added close button to replay tab, minor GUI fixes --- cockatrice/resources/lock.svg | 103 ++++++++++++++++++++++ cockatrice/src/replay_timeline_widget.cpp | 5 ++ cockatrice/src/replay_timeline_widget.h | 1 + cockatrice/src/tab_game.cpp | 25 ++++-- cockatrice/src/tab_game.h | 2 +- cockatrice/src/tab_supervisor.cpp | 40 ++++++--- cockatrice/src/tab_supervisor.h | 2 + cockatrice/src/window_deckeditor.cpp | 2 + 8 files changed, 162 insertions(+), 18 deletions(-) create mode 100644 cockatrice/resources/lock.svg diff --git a/cockatrice/resources/lock.svg b/cockatrice/resources/lock.svg new file mode 100644 index 00000000..ab32a23e --- /dev/null +++ b/cockatrice/resources/lock.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cockatrice/src/replay_timeline_widget.cpp b/cockatrice/src/replay_timeline_widget.cpp index 552b3a35..071cb7b6 100644 --- a/cockatrice/src/replay_timeline_widget.cpp +++ b/cockatrice/src/replay_timeline_widget.cpp @@ -63,6 +63,11 @@ QSize ReplayTimelineWidget::sizeHint() const return QSize(-1, 50); } +QSize ReplayTimelineWidget::minimumSizeHint() const +{ + return QSize(400, 50); +} + void ReplayTimelineWidget::replayTimerTimeout() { currentTime += 200; diff --git a/cockatrice/src/replay_timeline_widget.h b/cockatrice/src/replay_timeline_widget.h index d5fd0ce4..3a1b6712 100644 --- a/cockatrice/src/replay_timeline_widget.h +++ b/cockatrice/src/replay_timeline_widget.h @@ -27,6 +27,7 @@ public: ReplayTimelineWidget(QWidget *parent = 0); void setTimeline(const QList &_replayTimeline); QSize sizeHint() const; + QSize minimumSizeHint() const; void setTimeScaleFactor(qreal _timeScaleFactor); int getCurrentEvent() const { return currentEvent; } public slots: diff --git a/cockatrice/src/tab_game.cpp b/cockatrice/src/tab_game.cpp index bb606ab5..27b15485 100644 --- a/cockatrice/src/tab_game.cpp +++ b/cockatrice/src/tab_game.cpp @@ -213,6 +213,8 @@ TabGame::TabGame(GameReplay *_replay) replay(_replay), currentReplayStep(0) { + setAttribute(Qt::WA_DeleteOnClose); + gameId = replay->game_info().game_id(); gameDescription = QString::fromStdString(replay->game_info().description()); @@ -323,12 +325,13 @@ TabGame::TabGame(GameReplay *_replay) aNextTurn = 0; aRemoveLocalArrows = 0; aConcede = 0; - aLeaveGame = new QAction(this); - connect(aLeaveGame, SIGNAL(triggered()), this, SLOT(actLeaveGame())); + aLeaveGame = 0; + aCloseReplay = new QAction(this); + connect(aCloseReplay, SIGNAL(triggered()), this, SLOT(actLeaveGame())); phasesMenu = 0; tabMenu = new QMenu(this); - tabMenu->addAction(aLeaveGame); + tabMenu->addAction(aCloseReplay); retranslateUi(); setLayout(superMainLayout); @@ -424,6 +427,7 @@ TabGame::TabGame(TabSupervisor *_tabSupervisor, QList &_client connect(aConcede, SIGNAL(triggered()), this, SLOT(actConcede())); aLeaveGame = new QAction(this); connect(aLeaveGame, SIGNAL(triggered()), this, SLOT(actLeaveGame())); + aCloseReplay = 0; phasesMenu = new QMenu(this); for (int i = 0; i < phasesToolbar->phaseCount(); ++i) { @@ -502,8 +506,14 @@ void TabGame::retranslateUi() aConcede->setText(tr("&Concede")); aConcede->setShortcut(tr("F2")); } - aLeaveGame->setText(tr("&Leave game")); - aLeaveGame->setShortcut(tr("Ctrl+Q")); + if (aLeaveGame) { + aLeaveGame->setText(tr("&Leave game")); + aLeaveGame->setShortcut(tr("Ctrl+Q")); + } + if (aCloseReplay) { + aCloseReplay->setText(tr("C&lose replay")); + aCloseReplay->setShortcut(tr("Ctrl+Q")); + } if (sayLabel) sayLabel->setText(tr("&Say:")); @@ -613,8 +623,9 @@ void TabGame::actLeaveGame() if (!spectator) if (QMessageBox::question(this, tr("Leave game"), tr("Are you sure you want to leave this game?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) return; - - sendGameCommand(Command_LeaveGame()); + + if (!replay) + sendGameCommand(Command_LeaveGame()); deleteLater(); } diff --git a/cockatrice/src/tab_game.h b/cockatrice/src/tab_game.h index 8c40a6fe..bb4dda88 100644 --- a/cockatrice/src/tab_game.h +++ b/cockatrice/src/tab_game.h @@ -134,7 +134,7 @@ private: ZoneViewLayout *zoneLayout; QAction *playersSeparator; QMenu *phasesMenu; - QAction *aConcede, *aLeaveGame, *aNextPhase, *aNextTurn, *aRemoveLocalArrows; + QAction *aConcede, *aLeaveGame, *aCloseReplay, *aNextPhase, *aNextTurn, *aRemoveLocalArrows; QList phaseActions; Player *addPlayer(int playerId, const ServerInfo_User &info); diff --git a/cockatrice/src/tab_supervisor.cpp b/cockatrice/src/tab_supervisor.cpp index 02ab078e..6e23afcc 100644 --- a/cockatrice/src/tab_supervisor.cpp +++ b/cockatrice/src/tab_supervisor.cpp @@ -209,6 +209,11 @@ void TabSupervisor::stop() gameIterator.next().value()->deleteLater(); gameTabs.clear(); + QListIterator replayIterator(replayTabs); + while (replayIterator.hasNext()) + replayIterator.next()->deleteLater(); + replayTabs.clear(); + QMapIterator messageIterator(messageTabs); while (messageIterator.hasNext()) messageIterator.next().value()->deleteLater(); @@ -272,7 +277,8 @@ void TabSupervisor::localGameJoined(const Event_GameJoined &event) void TabSupervisor::gameLeft(TabGame *tab) { - emit setMenu(0); + if (tab == currentWidget()) + emit setMenu(0); gameTabs.remove(tab->getGameId()); removeTab(indexOf(tab)); @@ -295,12 +301,31 @@ void TabSupervisor::addRoomTab(const ServerInfo_Room &info, bool setCurrent) void TabSupervisor::roomLeft(TabRoom *tab) { - emit setMenu(0); - + if (tab == currentWidget()) + emit setMenu(0); + roomTabs.remove(tab->getRoomId()); removeTab(indexOf(tab)); } +void TabSupervisor::openReplay(GameReplay *replay) +{ + TabGame *replayTab = new TabGame(replay); + connect(replayTab, SIGNAL(gameClosing(TabGame *)), this, SLOT(replayLeft(TabGame *))); + int tabIndex = myAddTab(replayTab); + addCloseButtonToTab(replayTab, tabIndex); + replayTabs.append(replayTab); + setCurrentWidget(replayTab); +} + +void TabSupervisor::replayLeft(TabGame *tab) +{ + if (tab == currentWidget()) + emit setMenu(0); + + replayTabs.removeAt(replayTabs.indexOf(tab)); +} + TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus) { if (receiverName == QString::fromStdString(userInfo->name())) @@ -316,15 +341,10 @@ TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus return tab; } -void TabSupervisor::openReplay(GameReplay *replay) -{ - TabGame *replayTab = new TabGame(replay); - myAddTab(replayTab); -} - void TabSupervisor::talkLeft(TabMessage *tab) { - emit setMenu(0); + if (tab == currentWidget()) + emit setMenu(0); messageTabs.remove(tab->getUserName()); removeTab(indexOf(tab)); diff --git a/cockatrice/src/tab_supervisor.h b/cockatrice/src/tab_supervisor.h index 9a60998e..9f72cc4e 100644 --- a/cockatrice/src/tab_supervisor.h +++ b/cockatrice/src/tab_supervisor.h @@ -50,6 +50,7 @@ private: TabAdmin *tabAdmin; QMap roomTabs; QMap gameTabs; + QList replayTabs; QMap messageTabs; int myAddTab(Tab *tab); void addCloseButtonToTab(Tab *tab, int tabIndex); @@ -81,6 +82,7 @@ private slots: void roomLeft(TabRoom *tab); TabMessage *addMessageTab(const QString &userName, bool focus); void openReplay(GameReplay *replay); + void replayLeft(TabGame *tab); void processUserLeft(const QString &userName); void processUserJoined(const QString &userName); void talkLeft(TabMessage *tab); diff --git a/cockatrice/src/window_deckeditor.cpp b/cockatrice/src/window_deckeditor.cpp index c8723f0a..b7d9b836 100644 --- a/cockatrice/src/window_deckeditor.cpp +++ b/cockatrice/src/window_deckeditor.cpp @@ -38,6 +38,8 @@ void SearchLineEdit::keyPressEvent(QKeyEvent *event) WndDeckEditor::WndDeckEditor(QWidget *parent) : QMainWindow(parent) { + setAttribute(Qt::WA_DeleteOnClose); + aSearch = new QAction(tr("&Search..."), this); aSearch->setIcon(QIcon(":/resources/icon_search.svg")); connect(aSearch, SIGNAL(triggered()), this, SLOT(actSearch()));