interface & client tab for replay transfer

This commit is contained in:
Max-Wilhelm Bruker 2012-02-25 19:33:44 +01:00
parent faf6b2c5cb
commit c6c6a970c6
36 changed files with 702 additions and 52 deletions

View file

@ -54,6 +54,7 @@ SET(cockatrice_SOURCES
src/tab_message.cpp src/tab_message.cpp
src/tab_game.cpp src/tab_game.cpp
src/tab_deck_storage.cpp src/tab_deck_storage.cpp
src/tab_replays.cpp
src/tab_supervisor.cpp src/tab_supervisor.cpp
src/tab_admin.cpp src/tab_admin.cpp
src/tab_userlists.cpp src/tab_userlists.cpp
@ -61,6 +62,7 @@ SET(cockatrice_SOURCES
src/userlist.cpp src/userlist.cpp
src/userinfobox.cpp src/userinfobox.cpp
src/remotedecklist_treewidget.cpp src/remotedecklist_treewidget.cpp
src/remotereplaylist_treewidget.cpp
src/deckview.cpp src/deckview.cpp
src/playerlistwidget.cpp src/playerlistwidget.cpp
src/pixmapgenerator.cpp src/pixmapgenerator.cpp
@ -122,6 +124,7 @@ SET(cockatrice_HEADERS
src/tab_message.h src/tab_message.h
src/tab_game.h src/tab_game.h
src/tab_deck_storage.h src/tab_deck_storage.h
src/tab_replays.h
src/tab_supervisor.h src/tab_supervisor.h
src/tab_admin.h src/tab_admin.h
src/tab_userlists.h src/tab_userlists.h
@ -129,6 +132,7 @@ SET(cockatrice_HEADERS
src/userlist.h src/userlist.h
src/userinfobox.h src/userinfobox.h
src/remotedecklist_treewidget.h src/remotedecklist_treewidget.h
src/remotereplaylist_treewidget.h
src/deckview.h src/deckview.h
src/playerlistwidget.h src/playerlistwidget.h
src/settingscache.h src/settingscache.h

View file

@ -20,7 +20,7 @@ LocalServerInterface *LocalServer::newConnection()
return lsi; return lsi;
} }
ServerInfo_User LocalServer::getUserData(const QString &name) ServerInfo_User LocalServer::getUserData(const QString &name, bool /*withId*/)
{ {
ServerInfo_User result; ServerInfo_User result;
result.set_name(name.toStdString()); result.set_name(name.toStdString());

View file

@ -14,7 +14,7 @@ public:
LocalServerInterface *newConnection(); LocalServerInterface *newConnection();
protected: protected:
ServerInfo_User getUserData(const QString &name); ServerInfo_User getUserData(const QString &name, bool withId = false);
}; };
#endif #endif

View file

@ -18,6 +18,8 @@ private:
Response::ResponseCode cmdDeckDel(const Command_DeckDel & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } Response::ResponseCode cmdDeckDel(const Command_DeckDel & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; }
Response::ResponseCode cmdDeckUpload(const Command_DeckUpload & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } Response::ResponseCode cmdDeckUpload(const Command_DeckUpload & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; }
Response::ResponseCode cmdDeckDownload(const Command_DeckDownload & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } 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 cmdBanFromServer(const Command_BanFromServer & /*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 cmdShutdownServer(const Command_ShutdownServer & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; }
Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; } Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage & /*cmd*/, ResponseContainer & /*rc*/) { return Response::RespFunctionNotAllowed; }

View file

@ -51,7 +51,7 @@ PlayerTarget::PlayerTarget(Player *_owner, QGraphicsItem *parentItem)
{ {
setCacheMode(DeviceCoordinateCache); setCacheMode(DeviceCoordinateCache);
const std::string bmp = _owner->getUserInfo()->avatar_bmp(); const std::string &bmp = _owner->getUserInfo()->avatar_bmp();
if (!fullPixmap.loadFromData((const uchar *) bmp.data(), bmp.size())) if (!fullPixmap.loadFromData((const uchar *) bmp.data(), bmp.size()))
fullPixmap = QPixmap(); fullPixmap = QPixmap();
} }

View file

@ -5,7 +5,7 @@
#include "abstractclient.h" #include "abstractclient.h"
#include "pending_command.h" #include "pending_command.h"
#include "pb/session_commands.pb.h" #include "pb/command_deck_list.pb.h"
#include "pb/response_deck_list.pb.h" #include "pb/response_deck_list.pb.h"
#include "pb/serverinfo_deckstorage.pb.h" #include "pb/serverinfo_deckstorage.pb.h"

View file

@ -0,0 +1,167 @@
#include <QFileIconProvider>
#include <QHeaderView>
#include <QSortFilterProxyModel>
#include "remotereplaylist_treewidget.h"
#include "abstractclient.h"
#include "pending_command.h"
#include "pb/command_replay_list.pb.h"
#include "pb/response_replay_list.pb.h"
#include "pb/serverinfo_replay.pb.h"
RemoteReplayList_TreeModel::RemoteReplayList_TreeModel(AbstractClient *_client, QObject *parent)
: QAbstractItemModel(parent), client(_client)
{
QFileIconProvider fip;
fileIcon = fip.icon(QFileIconProvider::File);
refreshTree();
}
RemoteReplayList_TreeModel::~RemoteReplayList_TreeModel()
{
}
int RemoteReplayList_TreeModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : replays.size();
}
int RemoteReplayList_TreeModel::columnCount(const QModelIndex &/*parent*/) const
{
return 6;
}
QVariant RemoteReplayList_TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (index.column() > 5)
return QVariant();
ServerInfo_Replay *replayInfo = static_cast<ServerInfo_Replay *>(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(", ");
}
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();
}
return QVariant();
}
QVariant RemoteReplayList_TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Horizontal)
return QVariant();
switch (role) {
case Qt::TextAlignmentRole:
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)");
default: return QVariant();
}
}
default: return QVariant();
}
}
QModelIndex RemoteReplayList_TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
return createIndex(row, column, (void *) &(replays[row]));
}
QModelIndex RemoteReplayList_TreeModel::parent(const QModelIndex &ind) const
{
return QModelIndex();
}
Qt::ItemFlags RemoteReplayList_TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
ServerInfo_Replay const* RemoteReplayList_TreeModel::getNode(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return &(replays[index.row()]);
}
void RemoteReplayList_TreeModel::refreshTree()
{
PendingCommand *pend = client->prepareSessionCommand(Command_ReplayList());
connect(pend, SIGNAL(finished(const Response &)), this, SLOT(replayListFinished(const Response &)));
client->sendCommand(pend);
}
void RemoteReplayList_TreeModel::replayListFinished(const Response &r)
{
const Response_ReplayList &resp = r.GetExtension(Response_ReplayList::ext);
beginResetModel();
replays.clear();
for (int i = 0; i < resp.replay_list_size(); ++i)
replays.append(resp.replay_list(i));
endResetModel();
emit treeRefreshed();
}
RemoteReplayList_TreeWidget::RemoteReplayList_TreeWidget(AbstractClient *_client, QWidget *parent)
: QTreeView(parent)
{
treeModel = new RemoteReplayList_TreeModel(_client, this);
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(treeModel);
proxyModel->setDynamicSortFilter(true);
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
setModel(proxyModel);
connect(treeModel, SIGNAL(treeRefreshed()), this, SLOT(expandAll()));
header()->setResizeMode(QHeaderView::ResizeToContents);
setUniformRowHeights(true);
setSortingEnabled(true);
proxyModel->sort(0, Qt::AscendingOrder);
header()->setSortIndicator(0, Qt::AscendingOrder);
}
ServerInfo_Replay const *RemoteReplayList_TreeWidget::getNode(const QModelIndex &ind) const
{
return treeModel->getNode(proxyModel->mapToSource(ind));
}
ServerInfo_Replay const *RemoteReplayList_TreeWidget::getCurrentItem() const
{
return getNode(selectionModel()->currentIndex());
}

View file

@ -0,0 +1,49 @@
#ifndef REMOTEREPLAYLIST_TREEWIDGET_H
#define REMOTEREPLAYLIST_TREEWIDGET_H
#include <QAbstractItemModel>
#include <QDateTime>
#include <QTreeView>
#include "pb/serverinfo_replay.pb.h"
class Response;
class AbstractClient;
class QSortFilterProxyModel;
class RemoteReplayList_TreeModel : public QAbstractItemModel {
Q_OBJECT
private:
AbstractClient *client;
QList<ServerInfo_Replay> replays;
QIcon fileIcon;
signals:
void treeRefreshed();
private slots:
void replayListFinished(const Response &r);
public:
RemoteReplayList_TreeModel(AbstractClient *_client, QObject *parent = 0);
~RemoteReplayList_TreeModel();
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &/*parent*/ = QModelIndex()) const;
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;
QModelIndex parent(const QModelIndex &index) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
void refreshTree();
ServerInfo_Replay const *getNode(const QModelIndex &index) const;
};
class RemoteReplayList_TreeWidget : public QTreeView {
private:
RemoteReplayList_TreeModel *treeModel;
QSortFilterProxyModel *proxyModel;
ServerInfo_Replay const *getNode(const QModelIndex &ind) const;
public:
RemoteReplayList_TreeWidget(AbstractClient *_client, QWidget *parent = 0);
ServerInfo_Replay const *getCurrentItem() const;
void refreshTree();
};
#endif

View file

@ -12,7 +12,6 @@ class QToolBar;
class QTreeWidget; class QTreeWidget;
class QTreeWidgetItem; class QTreeWidgetItem;
class QGroupBox; class QGroupBox;
class ProtocolResponse;
class RemoteDeckList_TreeWidget; class RemoteDeckList_TreeWidget;
class TabDeckStorage : public Tab { class TabDeckStorage : public Tab {

View file

@ -957,6 +957,14 @@ CardItem *TabGame::getCard(int playerId, const QString &zoneName, int cardId) co
return zone->getCard(cardId, QString()); return zone->getCard(cardId, QString());
} }
QString TabGame::getTabText() const
{
if (replay)
return tr("Replay %1: %2").arg(gameId).arg(gameDescription);
else
return tr("Game %1: %2").arg(gameId).arg(gameDescription);
}
Player *TabGame::getActiveLocalPlayer() const Player *TabGame::getActiveLocalPlayer() const
{ {
Player *active = players.value(activePlayer, 0); Player *active = players.value(activePlayer, 0);

View file

@ -180,7 +180,7 @@ public:
CardItem *getCard(int playerId, const QString &zoneName, int cardId) const; CardItem *getCard(int playerId, const QString &zoneName, int cardId) const;
bool isHost() const { return hostId == localPlayerId; } bool isHost() const { return hostId == localPlayerId; }
int getGameId() const { return gameId; } int getGameId() const { return gameId; }
QString getTabText() const { return tr("Game %1: %2").arg(gameId).arg(gameDescription); } QString getTabText() const;
bool getSpectator() const { return spectator; } bool getSpectator() const { return spectator; }
bool getSpectatorsCanTalk() const { return spectatorsCanTalk; } bool getSpectatorsCanTalk() const { return spectatorsCanTalk; }
bool getSpectatorsSeeEverything() const { return spectatorsSeeEverything; } bool getSpectatorsSeeEverything() const { return spectatorsSeeEverything; }

View file

@ -0,0 +1,185 @@
#include <QTreeView>
#include <QFileSystemModel>
#include <QSortFilterProxyModel>
#include <QToolBar>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QAction>
#include <QGroupBox>
#include <QHeaderView>
#include <QApplication>
#include <QInputDialog>
#include "tab_replays.h"
#include "remotereplaylist_treewidget.h"
#include "abstractclient.h"
#include "tab_game.h"
#include "settingscache.h"
#include "pending_command.h"
#include "pb/game_replay.pb.h"
#include "pb/response.pb.h"
#include "pb/response_replay_download.pb.h"
#include "pb/command_replay_download.pb.h"
TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client)
: Tab(_tabSupervisor), client(_client)
{
localDirModel = new QFileSystemModel(this);
localDirModel->setRootPath(settingsCache->getReplaysPath());
sortFilter = new QSortFilterProxyModel(this);
sortFilter->setSourceModel(localDirModel);
sortFilter->setDynamicSortFilter(true);
localDirView = new QTreeView;
localDirView->setModel(sortFilter);
localDirView->setColumnHidden(1, true);
localDirView->setRootIndex(sortFilter->mapFromSource(localDirModel->index(localDirModel->rootPath(), 0)));
localDirView->setSortingEnabled(true);
localDirView->header()->setResizeMode(QHeaderView::ResizeToContents);
sortFilter->sort(0, Qt::AscendingOrder);
localDirView->header()->setSortIndicator(0, Qt::AscendingOrder);
leftToolBar = new QToolBar;
leftToolBar->setOrientation(Qt::Horizontal);
leftToolBar->setIconSize(QSize(32, 32));
QHBoxLayout *leftToolBarLayout = new QHBoxLayout;
leftToolBarLayout->addStretch();
leftToolBarLayout->addWidget(leftToolBar);
leftToolBarLayout->addStretch();
QVBoxLayout *leftVbox = new QVBoxLayout;
leftVbox->addWidget(localDirView);
leftVbox->addLayout(leftToolBarLayout);
leftGroupBox = new QGroupBox;
leftGroupBox->setLayout(leftVbox);
rightToolBar = new QToolBar;
rightToolBar->setOrientation(Qt::Horizontal);
rightToolBar->setIconSize(QSize(32, 32));
QHBoxLayout *rightToolBarLayout = new QHBoxLayout;
rightToolBarLayout->addStretch();
rightToolBarLayout->addWidget(rightToolBar);
rightToolBarLayout->addStretch();
serverDirView = new RemoteReplayList_TreeWidget(client);
QVBoxLayout *rightVbox = new QVBoxLayout;
rightVbox->addWidget(serverDirView);
rightVbox->addLayout(rightToolBarLayout);
rightGroupBox = new QGroupBox;
rightGroupBox->setLayout(rightVbox);
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(leftGroupBox);
hbox->addWidget(rightGroupBox);
aOpenLocalReplay = new QAction(this);
aOpenLocalReplay->setIcon(QIcon(":/resources/pencil.svg"));
connect(aOpenLocalReplay, SIGNAL(triggered()), this, SLOT(actOpenLocalReplay()));
aOpenRemoteReplay = new QAction(this);
aOpenRemoteReplay->setIcon(QIcon(":/resources/pencil.svg"));
connect(aOpenRemoteReplay, SIGNAL(triggered()), this, SLOT(actOpenRemoteReplay()));
aDownload = new QAction(this);
aDownload->setIcon(QIcon(":/resources/arrow_left_green.svg"));
connect(aDownload, SIGNAL(triggered()), this, SLOT(actDownload()));
leftToolBar->addAction(aOpenLocalReplay);
rightToolBar->addAction(aOpenRemoteReplay);
rightToolBar->addAction(aDownload);
retranslateUi();
setLayout(hbox);
}
void TabReplays::retranslateUi()
{
leftGroupBox->setTitle(tr("Local file system"));
rightGroupBox->setTitle(tr("Server replay storage"));
aOpenLocalReplay->setText(tr("Watch replay"));
aOpenRemoteReplay->setText(tr("Watch replay"));
aDownload->setText(tr("Download replay"));
}
void TabReplays::actOpenLocalReplay()
{
QModelIndex curLeft = sortFilter->mapToSource(localDirView->selectionModel()->currentIndex());
if (localDirModel->isDir(curLeft))
return;
QString filePath = localDirModel->filePath(curLeft);
QFile f(filePath);
if (!f.open(QIODevice::ReadOnly))
return;
QByteArray data = f.readAll();
f.close();
GameReplay *replay = new GameReplay;
replay->ParseFromArray(data.data(), data.size());
emit openReplay(replay);
}
void TabReplays::actOpenRemoteReplay()
{
ServerInfo_Replay const *curRight = serverDirView->getCurrentItem();
if (!curRight)
return;
Command_ReplayDownload cmd;
cmd.set_game_id(curRight->game_id());
PendingCommand *pend = client->prepareSessionCommand(cmd);
connect(pend, SIGNAL(finished(const Response &)), this, SLOT(openRemoteReplayFinished(const Response &)));
client->sendCommand(pend);
}
void TabReplays::openRemoteReplayFinished(const Response &r)
{
const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext);
GameReplay *replay = new GameReplay;
replay->ParseFromString(resp.replay_data());
emit openReplay(replay);
}
void TabReplays::actDownload()
{
QString filePath;
QModelIndex curLeft = sortFilter->mapToSource(localDirView->selectionModel()->currentIndex());
if (!curLeft.isValid())
filePath = localDirModel->rootPath();
else {
while (!localDirModel->isDir(curLeft))
curLeft = curLeft.parent();
filePath = localDirModel->filePath(curLeft);
}
ServerInfo_Replay const *curRight = serverDirView->getCurrentItem();
if (!curRight)
return;
filePath += QString("/game_%1.cor").arg(curRight->game_id());
Command_ReplayDownload cmd;
cmd.set_game_id(curRight->game_id());
PendingCommand *pend = client->prepareSessionCommand(cmd);
pend->setExtraData(filePath);
connect(pend, SIGNAL(finished(const Response &)), this, SLOT(downloadFinished(const Response &)));
client->sendCommand(pend);
}
void TabReplays::downloadFinished(const Response &r)
{
const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext);
PendingCommand *pend = static_cast<PendingCommand *>(sender());
QString filePath = pend->getExtraData().toString();
const std::string &data = resp.replay_data();
QFile f(filePath);
f.open(QIODevice::WriteOnly);
f.write((const char *) data.data(), data.size());
f.close();
}

View file

@ -0,0 +1,44 @@
#ifndef TAB_REPLAYS_H
#define TAB_REPLAYS_H
#include "tab.h"
#include "pb/response.pb.h"
class AbstractClient;
class QTreeView;
class QFileSystemModel;
class QSortFilterProxyModel;
class QToolBar;
class QGroupBox;
class RemoteReplayList_TreeWidget;
class GameReplay;
class TabReplays : public Tab {
Q_OBJECT
private:
AbstractClient *client;
QTreeView *localDirView;
QFileSystemModel *localDirModel;
QSortFilterProxyModel *sortFilter;
QToolBar *leftToolBar, *rightToolBar;
RemoteReplayList_TreeWidget *serverDirView;
QGroupBox *leftGroupBox, *rightGroupBox;
QAction *aOpenLocalReplay, *aOpenRemoteReplay, *aDownload;
private slots:
void actOpenLocalReplay();
void actOpenRemoteReplay();
void openRemoteReplayFinished(const Response &r);
void actDownload();
void downloadFinished(const Response &r);
signals:
void openReplay(GameReplay *replay);
public:
TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client);
void retranslateUi();
QString getTabText() const { return tr("Game replays"); }
};
#endif

View file

@ -5,6 +5,7 @@
#include "tab_room.h" #include "tab_room.h"
#include "tab_game.h" #include "tab_game.h"
#include "tab_deck_storage.h" #include "tab_deck_storage.h"
#include "tab_replays.h"
#include "tab_admin.h" #include "tab_admin.h"
#include "tab_message.h" #include "tab_message.h"
#include "tab_userlists.h" #include "tab_userlists.h"
@ -138,8 +139,14 @@ void TabSupervisor::start(AbstractClient *_client, const ServerInfo_User &_userI
if (userInfo->user_level() & ServerInfo_User::IsRegistered) { if (userInfo->user_level() & ServerInfo_User::IsRegistered) {
tabDeckStorage = new TabDeckStorage(this, client); tabDeckStorage = new TabDeckStorage(this, client);
myAddTab(tabDeckStorage); myAddTab(tabDeckStorage);
} else
tabReplays = new TabReplays(this, client);
connect(tabReplays, SIGNAL(openReplay(GameReplay *)), this, SLOT(openReplay(GameReplay *)));
myAddTab(tabReplays);
} else {
tabDeckStorage = 0; tabDeckStorage = 0;
tabReplays = 0;
}
if (userInfo->user_level() & ServerInfo_User::IsModerator) { if (userInfo->user_level() & ServerInfo_User::IsModerator) {
tabAdmin = new TabAdmin(this, client, (userInfo->user_level() & ServerInfo_User::IsAdmin)); tabAdmin = new TabAdmin(this, client, (userInfo->user_level() & ServerInfo_User::IsAdmin));
@ -155,6 +162,7 @@ void TabSupervisor::startLocal(const QList<AbstractClient *> &_clients)
{ {
tabUserLists = 0; tabUserLists = 0;
tabDeckStorage = 0; tabDeckStorage = 0;
tabReplays = 0;
tabAdmin = 0; tabAdmin = 0;
userInfo = new ServerInfo_User; userInfo = new ServerInfo_User;
localClients = _clients; localClients = _clients;
@ -183,10 +191,12 @@ void TabSupervisor::stop()
tabUserLists->deleteLater(); tabUserLists->deleteLater();
tabServer->deleteLater(); tabServer->deleteLater();
tabDeckStorage->deleteLater(); tabDeckStorage->deleteLater();
tabReplays->deleteLater();
} }
tabUserLists = 0; tabUserLists = 0;
tabServer = 0; tabServer = 0;
tabDeckStorage = 0; tabDeckStorage = 0;
tabReplays = 0;
clear(); clear();
QMapIterator<int, TabRoom *> roomIterator(roomTabs); QMapIterator<int, TabRoom *> roomIterator(roomTabs);
@ -306,6 +316,12 @@ TabMessage *TabSupervisor::addMessageTab(const QString &receiverName, bool focus
return tab; return tab;
} }
void TabSupervisor::openReplay(GameReplay *replay)
{
TabGame *replayTab = new TabGame(replay);
myAddTab(replayTab);
}
void TabSupervisor::talkLeft(TabMessage *tab) void TabSupervisor::talkLeft(TabMessage *tab)
{ {
emit setMenu(0); emit setMenu(0);

View file

@ -12,6 +12,7 @@ class TabServer;
class TabRoom; class TabRoom;
class TabGame; class TabGame;
class TabDeckStorage; class TabDeckStorage;
class TabReplays;
class TabAdmin; class TabAdmin;
class TabMessage; class TabMessage;
class TabUserLists; class TabUserLists;
@ -21,6 +22,7 @@ class Event_GameJoined;
class Event_UserMessage; class Event_UserMessage;
class ServerInfo_Room; class ServerInfo_Room;
class ServerInfo_User; class ServerInfo_User;
class GameReplay;
class CloseButton : public QAbstractButton { class CloseButton : public QAbstractButton {
Q_OBJECT Q_OBJECT
@ -44,6 +46,7 @@ private:
TabServer *tabServer; TabServer *tabServer;
TabUserLists *tabUserLists; TabUserLists *tabUserLists;
TabDeckStorage *tabDeckStorage; TabDeckStorage *tabDeckStorage;
TabReplays *tabReplays;
TabAdmin *tabAdmin; TabAdmin *tabAdmin;
QMap<int, TabRoom *> roomTabs; QMap<int, TabRoom *> roomTabs;
QMap<int, TabGame *> gameTabs; QMap<int, TabGame *> gameTabs;
@ -77,6 +80,7 @@ private slots:
void addRoomTab(const ServerInfo_Room &info, bool setCurrent); void addRoomTab(const ServerInfo_Room &info, bool setCurrent);
void roomLeft(TabRoom *tab); void roomLeft(TabRoom *tab);
TabMessage *addMessageTab(const QString &userName, bool focus); TabMessage *addMessageTab(const QString &userName, bool focus);
void openReplay(GameReplay *replay);
void processUserLeft(const QString &userName); void processUserLeft(const QString &userName);
void processUserJoined(const QString &userName); void processUserJoined(const QString &userName);
void talkLeft(TabMessage *tab); void talkLeft(TabMessage *tab);

View file

@ -12,6 +12,7 @@ SET(PROTO_FILES
command_deck_del_dir.proto command_deck_del_dir.proto
command_deck_del.proto command_deck_del.proto
command_deck_download.proto command_deck_download.proto
command_deck_list.proto
command_deck_new_dir.proto command_deck_new_dir.proto
command_deck_select.proto command_deck_select.proto
command_deck_upload.proto command_deck_upload.proto
@ -29,6 +30,8 @@ SET(PROTO_FILES
command_mulligan.proto command_mulligan.proto
command_next_turn.proto command_next_turn.proto
command_ready_start.proto command_ready_start.proto
command_replay_list.proto
command_replay_download.proto
command_reveal_cards.proto command_reveal_cards.proto
command_roll_die.proto command_roll_die.proto
command_set_active_phase.proto command_set_active_phase.proto
@ -107,6 +110,8 @@ SET(PROTO_FILES
response_join_room.proto response_join_room.proto
response_list_users.proto response_list_users.proto
response_login.proto response_login.proto
response_replay_download.proto
response_replay_list.proto
response.proto response.proto
room_commands.proto room_commands.proto
room_event.proto room_event.proto
@ -120,6 +125,7 @@ SET(PROTO_FILES
serverinfo_playerping.proto serverinfo_playerping.proto
serverinfo_playerproperties.proto serverinfo_playerproperties.proto
serverinfo_player.proto serverinfo_player.proto
serverinfo_replay.proto
serverinfo_room.proto serverinfo_room.proto
serverinfo_user.proto serverinfo_user.proto
serverinfo_zone.proto serverinfo_zone.proto

View file

@ -0,0 +1,7 @@
import "session_commands.proto";
message Command_DeckList {
extend SessionCommand {
optional Command_DeckList ext = 1008;
}
}

View file

@ -0,0 +1,8 @@
import "session_commands.proto";
message Command_ReplayDownload {
extend SessionCommand {
optional Command_ReplayDownload ext = 1101;
}
optional sint32 game_id = 1 [default = -1];
}

View file

@ -0,0 +1,7 @@
import "session_commands.proto";
message Command_ReplayList {
extend SessionCommand {
optional Command_ReplayList ext = 1100;
}
}

View file

@ -20,6 +20,7 @@ message Response {
RespWouldOverwriteOldSession = 17; RespWouldOverwriteOldSession = 17;
RespChatFlood = 18; RespChatFlood = 18;
RespUserIsBanned = 19; RespUserIsBanned = 19;
RespAccessDenied = 20;
} }
enum ResponseType { enum ResponseType {
JOIN_ROOM = 1000; JOIN_ROOM = 1000;
@ -31,6 +32,8 @@ message Response {
DECK_LIST = 1006; DECK_LIST = 1006;
DECK_DOWNLOAD = 1007; DECK_DOWNLOAD = 1007;
DECK_UPLOAD = 1008; DECK_UPLOAD = 1008;
REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101;
} }
required uint64 cmd_id = 1; required uint64 cmd_id = 1;
optional ResponseCode response_code = 2; optional ResponseCode response_code = 2;

View file

@ -0,0 +1,9 @@
import "response.proto";
message Response_ReplayDownload {
extend Response {
optional Response_ReplayDownload ext = 1101;
}
optional bytes replay_data = 1;
}

View file

@ -0,0 +1,9 @@
import "response.proto";
import "serverinfo_replay.proto";
message Response_ReplayList {
extend Response {
optional Response_ReplayList ext = 1100;
}
repeated ServerInfo_Replay replay_list = 1;
}

View file

@ -0,0 +1,9 @@
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;
}

View file

@ -18,4 +18,5 @@ message ServerInfo_User {
optional Gender gender = 5 [default = GenderUnknown]; optional Gender gender = 5 [default = GenderUnknown];
optional string country = 6; optional string country = 6;
optional bytes avatar_bmp = 7; optional bytes avatar_bmp = 7;
optional sint32 id = 8 [default = -1];
} }

View file

@ -16,6 +16,8 @@ message SessionCommand {
DECK_UPLOAD = 1013; DECK_UPLOAD = 1013;
LIST_ROOMS = 1014; LIST_ROOMS = 1014;
JOIN_ROOM = 1015; JOIN_ROOM = 1015;
REPLAY_LIST = 1100;
REPLAY_DOWNLOAD = 1101;
} }
extensions 100 to max; extensions 100 to max;
} }
@ -78,12 +80,6 @@ message Command_RemoveFromList {
optional string user_name = 2; optional string user_name = 2;
} }
message Command_DeckList {
extend SessionCommand {
optional Command_DeckList ext = 1008;
}
}
message Command_ListRooms { message Command_ListRooms {
extend SessionCommand { extend SessionCommand {
optional Command_ListRooms ext = 1014; optional Command_ListRooms ext = 1014;

View file

@ -58,7 +58,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
if ((authState == NotLoggedIn) || (authState == UserIsBanned)) if ((authState == NotLoggedIn) || (authState == UserIsBanned))
return authState; return authState;
ServerInfo_User data = getUserData(name); ServerInfo_User data = getUserData(name, true);
data.set_address(session->getAddress().toStdString()); data.set_address(session->getAddress().toStdString());
name = QString::fromStdString(data.name()); // Compensate for case indifference name = QString::fromStdString(data.name()); // Compensate for case indifference

View file

@ -48,7 +48,7 @@ public:
virtual bool isInBuddyList(const QString &whoseList, const QString &who) { return false; } virtual bool isInBuddyList(const QString &whoseList, const QString &who) { return false; }
virtual bool isInIgnoreList(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 QStringList &allPlayersEver, const GameReplay &replay) { } virtual void storeGameInformation(int secondsElapsed, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const GameReplay &replay) { }
protected: protected:
void prepareDestroy(); void prepareDestroy();
QList<Server_ProtocolHandler *> clients; QList<Server_ProtocolHandler *> clients;
@ -59,7 +59,7 @@ protected:
virtual void endSession(int sessionId) { } virtual void endSession(int sessionId) { }
virtual bool userExists(const QString &user) { return false; } virtual bool userExists(const QString &user) { return false; }
virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reason) { return UnknownUser; } virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reason) { return UnknownUser; }
virtual ServerInfo_User getUserData(const QString &name) = 0; virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0;
int getUsersCount() const; int getUsersCount() const;
int getGamesCount() const; int getGamesCount() const;
int nextGameId; int nextGameId;

View file

@ -48,11 +48,11 @@ 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), 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), secondsElapsed(0), startTime(QDateTime::currentDateTime()), gameMutex(QMutex::Recursive)
{ {
replay = new GameReplay; replay = new GameReplay;
replay->mutable_game_info()->CopyFrom(getInfo());
connect(this, SIGNAL(sigStartGameIfReady()), this, SLOT(doStartGameIfReady()), Qt::QueuedConnection); connect(this, SIGNAL(sigStartGameIfReady()), this, SLOT(doStartGameIfReady()), Qt::QueuedConnection);
addPlayer(_creator, false, false); addPlayer(_creator, false, false);
replay->mutable_game_info()->CopyFrom(getInfo());
if (room->getServer()->getGameShouldPing()) { if (room->getServer()->getGameShouldPing()) {
pingClock = new QTimer(this); pingClock = new QTimer(this);
@ -80,7 +80,7 @@ Server_Game::~Server_Game()
gameMutex.unlock(); gameMutex.unlock();
room->roomMutex.unlock(); room->roomMutex.unlock();
room->getServer()->storeGameInformation(secondsElapsed, allPlayersEver.toList(), *replay); room->getServer()->storeGameInformation(secondsElapsed, allPlayersEver, allSpectatorsEver, *replay);
delete replay; delete replay;
qDebug() << "Server_Game destructor: gameId=" << gameId; qDebug() << "Server_Game destructor: gameId=" << gameId;
@ -324,7 +324,10 @@ Server_Player *Server_Game::addPlayer(Server_ProtocolHandler *handler, bool spec
joinEvent.mutable_player_properties()->CopyFrom(newPlayer->getProperties(true)); joinEvent.mutable_player_properties()->CopyFrom(newPlayer->getProperties(true));
sendGameEventContainer(prepareGameEvent(joinEvent, -1)); sendGameEventContainer(prepareGameEvent(joinEvent, -1));
allPlayersEver.insert(QString::fromStdString(newPlayer->getUserInfo()->name())); if (spectator)
allSpectatorsEver.insert(QString::fromStdString(newPlayer->getUserInfo()->name()));
else
allPlayersEver.insert(QString::fromStdString(newPlayer->getUserInfo()->name()));
players.insert(playerId, newPlayer); players.insert(playerId, newPlayer);
if (newPlayer->getUserInfo()->name() == creatorInfo->name()) { if (newPlayer->getUserInfo()->name() == creatorInfo->name()) {
hostId = playerId; hostId = playerId;

View file

@ -45,7 +45,7 @@ private:
int hostId; int hostId;
ServerInfo_User *creatorInfo; ServerInfo_User *creatorInfo;
QMap<int, Server_Player *> players; QMap<int, Server_Player *> players;
QSet<QString> allPlayersEver; QSet<QString> allPlayersEver, allSpectatorsEver;
bool gameStarted; bool gameStarted;
int gameId; int gameId;
QString description; QString description;

View file

@ -42,11 +42,14 @@
#include "pb/command_shuffle.pb.h" #include "pb/command_shuffle.pb.h"
#include "pb/command_stop_dump_zone.pb.h" #include "pb/command_stop_dump_zone.pb.h"
#include "pb/command_undo_draw.pb.h" #include "pb/command_undo_draw.pb.h"
#include "pb/command_deck_list.pb.h"
#include "pb/command_deck_upload.pb.h" #include "pb/command_deck_upload.pb.h"
#include "pb/command_deck_download.pb.h" #include "pb/command_deck_download.pb.h"
#include "pb/command_deck_new_dir.pb.h" #include "pb/command_deck_new_dir.pb.h"
#include "pb/command_deck_del_dir.pb.h" #include "pb/command_deck_del_dir.pb.h"
#include "pb/command_deck_del.pb.h" #include "pb/command_deck_del.pb.h"
#include "pb/command_replay_list.pb.h"
#include "pb/command_replay_download.pb.h"
#include "pb/response.pb.h" #include "pb/response.pb.h"
#include "pb/response_login.pb.h" #include "pb/response_login.pb.h"
#include "pb/response_list_users.pb.h" #include "pb/response_list_users.pb.h"
@ -137,8 +140,10 @@ ServerInfo_User Server_ProtocolHandler::copyUserInfo(bool complete, bool moderat
ServerInfo_User result; ServerInfo_User result;
if (userInfo) { if (userInfo) {
result.CopyFrom(*userInfo); result.CopyFrom(*userInfo);
if (!moderatorInfo) if (!moderatorInfo) {
result.clear_address(); result.clear_address();
result.clear_id();
}
if (!complete) if (!complete)
result.clear_avatar_bmp(); result.clear_avatar_bmp();
} }
@ -234,6 +239,8 @@ Response::ResponseCode Server_ProtocolHandler::processSessionCommandContainer(co
case SessionCommand::DECK_DEL: resp = cmdDeckDel(sc.GetExtension(Command_DeckDel::ext), rc); break; case SessionCommand::DECK_DEL: resp = cmdDeckDel(sc.GetExtension(Command_DeckDel::ext), rc); break;
case SessionCommand::DECK_UPLOAD: resp = cmdDeckUpload(sc.GetExtension(Command_DeckUpload::ext), rc); break; case SessionCommand::DECK_UPLOAD: resp = cmdDeckUpload(sc.GetExtension(Command_DeckUpload::ext), rc); break;
case SessionCommand::DECK_DOWNLOAD: resp = cmdDeckDownload(sc.GetExtension(Command_DeckDownload::ext), rc); break; 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::GET_GAMES_OF_USER: resp = cmdGetGamesOfUser(sc.GetExtension(Command_GetGamesOfUser::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::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; case SessionCommand::LIST_ROOMS: resp = cmdListRooms(sc.GetExtension(Command_ListRooms::ext), rc); break;

View file

@ -39,6 +39,8 @@ class Command_DeckDelDir;
class Command_DeckDel; class Command_DeckDel;
class Command_DeckDownload; class Command_DeckDownload;
class Command_DeckUpload; class Command_DeckUpload;
class Command_ReplayList;
class Command_ReplayDownload;
class Command_ListRooms; class Command_ListRooms;
class Command_JoinRoom; class Command_JoinRoom;
class Command_LeaveRoom; class Command_LeaveRoom;
@ -119,6 +121,8 @@ private:
virtual Response::ResponseCode cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer &rc) = 0; virtual Response::ResponseCode cmdDeckDel(const Command_DeckDel &cmd, ResponseContainer &rc) = 0;
virtual Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc) = 0; virtual Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc) = 0;
virtual Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc) = 0; 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;
Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetGamesOfUser(const Command_GetGamesOfUser &cmd, ResponseContainer &rc);
Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc); Response::ResponseCode cmdGetUserInfo(const Command_GetUserInfo &cmd, ResponseContainer &rc);
Response::ResponseCode cmdListRooms(const Command_ListRooms &cmd, ResponseContainer &rc); Response::ResponseCode cmdListRooms(const Command_ListRooms &cmd, ResponseContainer &rc);

View file

@ -37,6 +37,7 @@ public:
QString getDescription() const { return description; } QString getDescription() const { return description; }
bool getAutoJoin() const { return autoJoin; } bool getAutoJoin() const { return autoJoin; }
QString getJoinMessage() const { return joinMessage; } QString getJoinMessage() const { return joinMessage; }
const QStringList &getGameTypes() const { return gameTypes; }
const QMap<int, Server_Game *> &getGames() const { return games; } const QMap<int, Server_Game *> &getGames() const { return games; }
Server *getServer() const; Server *getServer() const;
ServerInfo_Room getInfo(bool complete, bool showGameTypes = false, bool updating = false) const; ServerInfo_Room getInfo(bool complete, bool showGameTypes = false, bool updating = false) const;

View file

@ -323,29 +323,31 @@ bool Servatrice::isInIgnoreList(const QString &whoseList, const QString &who)
return query.next(); return query.next();
} }
ServerInfo_User Servatrice::evalUserQueryResult(const QSqlQuery &query, bool complete) ServerInfo_User Servatrice::evalUserQueryResult(const QSqlQuery &query, bool complete, bool withId)
{ {
ServerInfo_User result; ServerInfo_User result;
result.set_name(query.value(0).toString().toStdString()); if (withId)
result.set_id(query.value(0).toInt());
result.set_name(query.value(1).toString().toStdString());
const QString country = query.value(4).toString(); const QString country = query.value(5).toString();
if (!country.isEmpty()) if (!country.isEmpty())
result.set_country(country.toStdString()); result.set_country(country.toStdString());
if (complete) { if (complete) {
const QByteArray avatarBmp = query.value(5).toByteArray(); const QByteArray avatarBmp = query.value(6).toByteArray();
if (avatarBmp.size()) if (avatarBmp.size())
result.set_avatar_bmp(avatarBmp.data(), avatarBmp.size()); result.set_avatar_bmp(avatarBmp.data(), avatarBmp.size());
} }
const QString genderStr = query.value(3).toString(); const QString genderStr = query.value(4).toString();
if (genderStr == "m") if (genderStr == "m")
result.set_gender(ServerInfo_User::Male); result.set_gender(ServerInfo_User::Male);
else if (genderStr == "f") else if (genderStr == "f")
result.set_gender(ServerInfo_User::Female); result.set_gender(ServerInfo_User::Female);
const int is_admin = query.value(1).toInt(); const int is_admin = query.value(2).toInt();
int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered; int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered;
if (is_admin == 1) if (is_admin == 1)
userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator; userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator;
@ -353,14 +355,14 @@ ServerInfo_User Servatrice::evalUserQueryResult(const QSqlQuery &query, bool com
userLevel |= ServerInfo_User::IsModerator; userLevel |= ServerInfo_User::IsModerator;
result.set_user_level(userLevel); result.set_user_level(userLevel);
const QString realName = query.value(2).toString(); const QString realName = query.value(3).toString();
if (!realName.isEmpty()) if (!realName.isEmpty())
result.set_real_name(realName.toStdString()); result.set_real_name(realName.toStdString());
return result; return result;
} }
ServerInfo_User Servatrice::getUserData(const QString &name) ServerInfo_User Servatrice::getUserData(const QString &name, bool withId)
{ {
ServerInfo_User result; ServerInfo_User result;
result.set_name(name.toStdString()); result.set_name(name.toStdString());
@ -372,13 +374,13 @@ ServerInfo_User Servatrice::getUserData(const QString &name)
return result; return result;
QSqlQuery query; QSqlQuery query;
query.prepare("select name, admin, realname, gender, country, avatar_bmp from " + dbPrefix + "_users where name = :name and active = 1"); query.prepare("select id, name, admin, realname, gender, country, avatar_bmp from " + dbPrefix + "_users where name = :name and active = 1");
query.bindValue(":name", name); query.bindValue(":name", name);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
return result; return result;
if (query.next()) if (query.next())
return evalUserQueryResult(query, true); return evalUserQueryResult(query, true, withId);
else else
return result; return result;
} else } else
@ -447,7 +449,7 @@ QMap<QString, ServerInfo_User> Servatrice::getBuddyList(const QString &name)
checkSql(); checkSql();
QSqlQuery query; QSqlQuery query;
query.prepare("select a.name, a.admin, a.realname, a.gender, a.country from " + dbPrefix + "_users a left join " + dbPrefix + "_buddylist b on a.id = b.id_user2 left join " + dbPrefix + "_users c on b.id_user1 = c.id where c.name = :name"); query.prepare("select a.id, a.name, a.admin, a.realname, a.gender, a.country from " + dbPrefix + "_users a left join " + dbPrefix + "_buddylist b on a.id = b.id_user2 left join " + dbPrefix + "_users c on b.id_user1 = c.id where c.name = :name");
query.bindValue(":name", name); query.bindValue(":name", name);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
return result; return result;
@ -469,7 +471,7 @@ QMap<QString, ServerInfo_User> Servatrice::getIgnoreList(const QString &name)
checkSql(); checkSql();
QSqlQuery query; QSqlQuery query;
query.prepare("select a.name, a.admin, a.realname, a.gender, a.country from " + dbPrefix + "_users a left join " + dbPrefix + "_ignorelist b on a.id = b.id_user2 left join " + dbPrefix + "_users c on b.id_user1 = c.id where c.name = :name"); query.prepare("select a.id, a.name, a.admin, a.realname, a.gender, a.country from " + dbPrefix + "_users a left join " + dbPrefix + "_ignorelist b on a.id = b.id_user2 left join " + dbPrefix + "_users c on b.id_user1 = c.id where c.name = :name");
query.bindValue(":name", name); query.bindValue(":name", name);
if (!execSqlQuery(query)) if (!execSqlQuery(query))
return result; return result;
@ -536,11 +538,14 @@ void Servatrice::statusUpdate()
execSqlQuery(query); execSqlQuery(query);
} }
void Servatrice::storeGameInformation(int secondsElapsed, const QStringList &allPlayersEver, const GameReplay &replay) void Servatrice::storeGameInformation(int secondsElapsed, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const GameReplay &replay)
{ {
Server_Room *room = rooms.value(replay.game_info().room_id());
const QStringList &allGameTypes = room->getGameTypes();
QStringList gameTypes; QStringList gameTypes;
for (int i = replay.game_info().game_types_size() - 1; i >= 0; --i) for (int i = replay.game_info().game_types_size() - 1; i >= 0; --i)
gameTypes.append(QString::number(replay.game_info().game_types(i))); gameTypes.append(allGameTypes[replay.game_info().game_types(i)]);
QByteArray replayBlob; QByteArray replayBlob;
const unsigned int size = replay.ByteSize(); const unsigned int size = replay.ByteSize();
@ -552,29 +557,48 @@ void Servatrice::storeGameInformation(int secondsElapsed, const QStringList &all
return; return;
QSqlQuery query1; QSqlQuery query1;
query1.prepare("insert into " + dbPrefix + "_games (id_room, 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.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(":id_room", replay.game_info().room_id()); query1.bindValue(":room_name", room->getName());
query1.bindValue(":id_game", replay.game_info().game_id()); query1.bindValue(":id_game", replay.game_info().game_id());
query1.bindValue(":descr", QString::fromStdString(replay.game_info().description())); query1.bindValue(":descr", QString::fromStdString(replay.game_info().description()));
query1.bindValue(":creator_name", QString::fromStdString(replay.game_info().creator_info().name())); query1.bindValue(":creator_name", QString::fromStdString(replay.game_info().creator_info().name()));
query1.bindValue(":password", replay.game_info().with_password() ? 1 : 0); query1.bindValue(":password", replay.game_info().with_password() ? 1 : 0);
query1.bindValue(":game_types", gameTypes.isEmpty() ? QString("") : gameTypes.join(",")); query1.bindValue(":game_types", gameTypes.isEmpty() ? QString("") : gameTypes.join(", "));
query1.bindValue(":player_count", replay.game_info().max_players()); query1.bindValue(":player_count", replay.game_info().max_players());
query1.bindValue(":seconds", secondsElapsed); query1.bindValue(":seconds", secondsElapsed);
query1.bindValue(":replay", replayBlob); query1.bindValue(":replay", replayBlob);
if (!execSqlQuery(query1)) if (!execSqlQuery(query1))
return; return;
QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames;
QSetIterator<QString> playerIterator(allPlayersEver);
while (playerIterator.hasNext()) {
gameIds1.append(replay.game_info().game_id());
playerNames.append(playerIterator.next());
}
QSet<QString> allUsersInGame = allPlayersEver + allSpectatorsEver;
QSetIterator<QString> allUsersIterator(allUsersInGame);
while (allUsersIterator.hasNext()) {
int id = getUserIdInDB(allUsersIterator.next());
if (id == -1)
continue;
gameIds2.append(replay.game_info().game_id());
userIds.append(id);
replayNames.append(QString::fromStdString(replay.game_info().description()));
}
QSqlQuery query2; QSqlQuery query2;
query2.prepare("insert into " + dbPrefix + "_games_players (id_game, player_name) values (:id_game, :player_name)"); query2.prepare("insert into " + dbPrefix + "_games_players (id_game, player_name) values (:id_game, :player_name)");
QVariantList gameIds, playerNames; query2.bindValue(":id_game", gameIds1);
for (int i = allPlayersEver.size() - 1; i >= 0; --i) {
gameIds.append(replay.game_info().game_id());
playerNames.append(allPlayersEver[i]);
}
query2.bindValue(":id_game", gameIds);
query2.bindValue(":player_name", playerNames); query2.bindValue(":player_name", playerNames);
query2.execBatch(); query2.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);
query3.bindValue(":id_player", userIds);
query3.bindValue(":replay_name", replayNames);
query3.execBatch();
} }
void Servatrice::scheduleShutdown(const QString &reason, int minutes) void Servatrice::scheduleShutdown(const QString &reason, int minutes)

View file

@ -71,7 +71,7 @@ public:
bool getThreaded() const { return threaded; } bool getThreaded() const { return threaded; }
QString getDbPrefix() const { return dbPrefix; } QString getDbPrefix() const { return dbPrefix; }
void updateLoginMessage(); void updateLoginMessage();
ServerInfo_User getUserData(const QString &name); ServerInfo_User getUserData(const QString &name, bool withId = false);
int getUsersWithAddress(const QHostAddress &address) const; int getUsersWithAddress(const QHostAddress &address) const;
QList<ServerSocketInterface *> getUsersWithAddressAsList(const QHostAddress &address) const; QList<ServerSocketInterface *> getUsersWithAddressAsList(const QHostAddress &address) const;
QMap<QString, ServerInfo_User> getBuddyList(const QString &name); QMap<QString, ServerInfo_User> getBuddyList(const QString &name);
@ -82,7 +82,7 @@ public:
void incTxBytes(quint64 num); void incTxBytes(quint64 num);
void incRxBytes(quint64 num); void incRxBytes(quint64 num);
int getUserIdInDB(const QString &name); int getUserIdInDB(const QString &name);
void storeGameInformation(int secondsElapsed, const QStringList &allPlayersEver, const GameReplay &replay); void storeGameInformation(int secondsElapsed, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const GameReplay &replay);
protected: protected:
int startSession(const QString &userName, const QString &address); int startSession(const QString &userName, const QString &address);
void endSession(int sessionId); void endSession(int sessionId);
@ -106,7 +106,7 @@ private:
quint64 txBytes, rxBytes; quint64 txBytes, rxBytes;
int maxGameInactivityTime, maxPlayerInactivityTime; int maxGameInactivityTime, maxPlayerInactivityTime;
int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser; int maxUsersPerAddress, messageCountingInterval, maxMessageCountPerInterval, maxMessageSizePerInterval, maxGamesPerUser;
ServerInfo_User evalUserQueryResult(const QSqlQuery &query, bool complete); ServerInfo_User evalUserQueryResult(const QSqlQuery &query, bool complete, bool withId = false);
QString shutdownReason; QString shutdownReason;
int shutdownMinutes; int shutdownMinutes;

View file

@ -34,6 +34,8 @@
#include "pb/command_deck_new_dir.pb.h" #include "pb/command_deck_new_dir.pb.h"
#include "pb/command_deck_del_dir.pb.h" #include "pb/command_deck_del_dir.pb.h"
#include "pb/command_deck_del.pb.h" #include "pb/command_deck_del.pb.h"
#include "pb/command_replay_list.pb.h"
#include "pb/command_replay_download.pb.h"
#include "pb/event_connection_closed.pb.h" #include "pb/event_connection_closed.pb.h"
#include "pb/event_server_message.pb.h" #include "pb/event_server_message.pb.h"
#include "pb/event_server_identification.pb.h" #include "pb/event_server_identification.pb.h"
@ -42,6 +44,9 @@
#include "pb/response_deck_list.pb.h" #include "pb/response_deck_list.pb.h"
#include "pb/response_deck_download.pb.h" #include "pb/response_deck_download.pb.h"
#include "pb/response_deck_upload.pb.h" #include "pb/response_deck_upload.pb.h"
#include "pb/response_replay_list.pb.h"
#include "pb/response_replay_download.pb.h"
#include "pb/serverinfo_replay.pb.h"
#include "pb/serverinfo_user.pb.h" #include "pb/serverinfo_user.pb.h"
#include "pb/serverinfo_deckstorage.pb.h" #include "pb/serverinfo_deckstorage.pb.h"
@ -175,7 +180,7 @@ Response::ResponseCode ServerSocketInterface::cmdAddToList(const Command_AddToLi
if (servatrice->isInIgnoreList(QString::fromStdString(userInfo->name()), user)) if (servatrice->isInIgnoreList(QString::fromStdString(userInfo->name()), user))
return Response::RespContextError; return Response::RespContextError;
int id1 = servatrice->getUserIdInDB(QString::fromStdString(userInfo->name())); int id1 = userInfo->id();
int id2 = servatrice->getUserIdInDB(user); int id2 = servatrice->getUserIdInDB(user);
if (id2 < 0) if (id2 < 0)
return Response::RespNameNotFound; return Response::RespNameNotFound;
@ -216,7 +221,7 @@ Response::ResponseCode ServerSocketInterface::cmdRemoveFromList(const Command_Re
if (!servatrice->isInIgnoreList(QString::fromStdString(userInfo->name()), user)) if (!servatrice->isInIgnoreList(QString::fromStdString(userInfo->name()), user))
return Response::RespContextError; return Response::RespContextError;
int id1 = servatrice->getUserIdInDB(QString::fromStdString(userInfo->name())); int id1 = userInfo->id();
int id2 = servatrice->getUserIdInDB(user); int id2 = servatrice->getUserIdInDB(user);
if (id2 < 0) if (id2 < 0)
return Response::RespNameNotFound; return Response::RespNameNotFound;
@ -485,6 +490,77 @@ Response::ResponseCode ServerSocketInterface::cmdDeckDownload(const Command_Deck
return Response::RespOk; return Response::RespOk;
} }
Response::ResponseCode ServerSocketInterface::cmdReplayList(const Command_ReplayList & /*cmd*/, ResponseContainer &rc)
{
if (authState != PasswordRight)
return Response::RespFunctionNotAllowed;
Response_ReplayList *re = new Response_ReplayList;
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.bindValue(":id_player", userInfo->id());
servatrice->execSqlQuery(query1);
while (query1.next()) {
ServerInfo_Replay *replayInfo = re->add_replay_list();
const int gameId = query1.value(0).toInt();
replayInfo->set_game_id(gameId);
replayInfo->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());
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());
}
servatrice->dbMutex.unlock();
rc.setResponseExtension(re);
return Response::RespOk;
}
Response::ResponseCode ServerSocketInterface::cmdReplayDownload(const Command_ReplayDownload &cmd, ResponseContainer &rc)
{
if (authState != PasswordRight)
return Response::RespFunctionNotAllowed;
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.bindValue(":id_player", userInfo->id());
if (!servatrice->execSqlQuery(query1))
return Response::RespInternalError;
if (!query1.next())
return Response::RespAccessDenied;
QSqlQuery query2;
query2.prepare("select replay from " + servatrice->getDbPrefix() + "_games where id = :id_game");
query2.bindValue(":id_game", cmd.game_id());
if (!servatrice->execSqlQuery(query2))
return Response::RespInternalError;
if (!query2.next())
return Response::RespNameNotFound;
QByteArray data = query2.value(0).toByteArray();
Response_ReplayDownload *re = new Response_ReplayDownload;
re->set_replay_data(data.data(), data.size());
rc.setResponseExtension(re);
return Response::RespOk;
}
// MODERATOR FUNCTIONS. // MODERATOR FUNCTIONS.
// May be called by admins and moderators. Permission is checked by the calling function. // May be called by admins and moderators. Permission is checked by the calling function.
@ -499,7 +575,7 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
query.prepare("insert into " + servatrice->getDbPrefix() + "_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason)"); query.prepare("insert into " + servatrice->getDbPrefix() + "_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason)");
query.bindValue(":user_name", userName); query.bindValue(":user_name", userName);
query.bindValue(":ip_address", address); query.bindValue(":ip_address", address);
query.bindValue(":id_admin", servatrice->getUserIdInDB(QString::fromStdString(userInfo->name()))); query.bindValue(":id_admin", userInfo->id());
query.bindValue(":minutes", minutes); query.bindValue(":minutes", minutes);
query.bindValue(":reason", QString::fromStdString(cmd.reason()) + "\n"); query.bindValue(":reason", QString::fromStdString(cmd.reason()) + "\n");
query.bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason()) + "\n"); query.bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason()) + "\n");

View file

@ -61,6 +61,8 @@ private:
Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc); Response::ResponseCode cmdDeckUpload(const Command_DeckUpload &cmd, ResponseContainer &rc);
DeckList *getDeckFromDatabase(int deckId); DeckList *getDeckFromDatabase(int deckId);
Response::ResponseCode cmdDeckDownload(const Command_DeckDownload &cmd, ResponseContainer &rc); 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 cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdBanFromServer(const Command_BanFromServer &cmd, ResponseContainer &rc);
Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc); Response::ResponseCode cmdShutdownServer(const Command_ShutdownServer &cmd, ResponseContainer &rc);
Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc); Response::ResponseCode cmdUpdateServerMessage(const Command_UpdateServerMessage &cmd, ResponseContainer &rc);