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);