Merge branch 'master' of git://github.com/mbruker/Cockatrice
This commit is contained in:
commit
347d30a84b
44 changed files with 673 additions and 371 deletions
|
@ -7,6 +7,9 @@ if (NOT WITHOUT_CLIENT)
|
||||||
add_subdirectory(cockatrice)
|
add_subdirectory(cockatrice)
|
||||||
add_subdirectory(oracle)
|
add_subdirectory(oracle)
|
||||||
endif(NOT WITHOUT_CLIENT)
|
endif(NOT WITHOUT_CLIENT)
|
||||||
|
if (WITH_TESTCLIENT)
|
||||||
|
add_subdirectory(testclient)
|
||||||
|
endif(WITH_TESTCLIENT)
|
||||||
|
|
||||||
FILE(GLOB sounds "${CMAKE_CURRENT_SOURCE_DIR}/sounds/*.raw")
|
FILE(GLOB sounds "${CMAKE_CURRENT_SOURCE_DIR}/sounds/*.raw")
|
||||||
INSTALL(FILES ${sounds} DESTINATION share/cockatrice/sounds)
|
INSTALL(FILES ${sounds} DESTINATION share/cockatrice/sounds)
|
||||||
|
|
46
README.md
Normal file
46
README.md
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# Cockatrice
|
||||||
|
|
||||||
|
Cockatrice is an open-source multiplatform software for playing card games,
|
||||||
|
such as Magic: The Gathering, over a network. It is fully client-server based
|
||||||
|
to prevent any kind of cheating, though it supports single-player games without
|
||||||
|
a network interface as well. Both client and server are written in Qt 4.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Cockatrice is free software, licensed under the GPLv2; see COPYING for details.
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
|
||||||
|
- [Qt](http://qt-project.org/)
|
||||||
|
|
||||||
|
- [protobuf](http://code.google.com/p/protobuf/)
|
||||||
|
|
||||||
|
- [CMake](http://www.cmake.org/)
|
||||||
|
|
||||||
|
The server requires an additional dependency:
|
||||||
|
|
||||||
|
- [libgcrypt](http://www.gnu.org/software/libgcrypt/)
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
The following flags can be passed to `cmake`:
|
||||||
|
|
||||||
|
- `-DWITH_SERVER=1` build the server
|
||||||
|
|
||||||
|
- `-DWITHOUT_CLIENT=1` do not build the client
|
||||||
|
|
||||||
|
# Running
|
||||||
|
|
||||||
|
`oracle` fetches card data
|
||||||
|
|
||||||
|
`cockatrice` is the game client
|
||||||
|
|
||||||
|
`servatrice` is the server
|
|
@ -197,10 +197,16 @@ if (NOT QT_QTMULTIMEDIA_FOUND)
|
||||||
FIND_PACKAGE(QtMobility REQUIRED)
|
FIND_PACKAGE(QtMobility REQUIRED)
|
||||||
endif (NOT QT_QTMULTIMEDIA_FOUND)
|
endif (NOT QT_QTMULTIMEDIA_FOUND)
|
||||||
FIND_PACKAGE(Protobuf REQUIRED)
|
FIND_PACKAGE(Protobuf REQUIRED)
|
||||||
|
FIND_PACKAGE(Threads)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0")
|
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O0")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-s -O2")
|
set(CMAKE_CXX_FLAGS_RELEASE "-s -O2")
|
||||||
|
|
||||||
|
# paths
|
||||||
|
set(ICONDIR share/icons CACHE STRING "icon dir")
|
||||||
|
set(DESKTOPDIR share/applications CACHE STRING "desktop file destination")
|
||||||
|
|
||||||
|
|
||||||
QT4_WRAP_CPP(cockatrice_HEADERS_MOC ${cockatrice_HEADERS})
|
QT4_WRAP_CPP(cockatrice_HEADERS_MOC ${cockatrice_HEADERS})
|
||||||
QT4_ADD_TRANSLATION(cockatrice_QM ${cockatrice_TS})
|
QT4_ADD_TRANSLATION(cockatrice_QM ${cockatrice_TS})
|
||||||
QT4_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
|
QT4_ADD_RESOURCES(cockatrice_RESOURCES_RCC ${cockatrice_RESOURCES})
|
||||||
|
@ -214,13 +220,17 @@ INCLUDE_DIRECTORIES(${QT_MOBILITY_INCLUDE_DIR})
|
||||||
INCLUDE_DIRECTORIES(${QT_MOBILITY_MULTIMEDIAKIT_INCLUDE_DIR})
|
INCLUDE_DIRECTORIES(${QT_MOBILITY_MULTIMEDIAKIT_INCLUDE_DIR})
|
||||||
|
|
||||||
ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_HEADERS_MOC})
|
ADD_EXECUTABLE(cockatrice WIN32 MACOSX_BUNDLE ${cockatrice_SOURCES} ${cockatrice_QM} ${cockatrice_RESOURCES_RCC} ${cockatrice_HEADERS_MOC})
|
||||||
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common ${QT_LIBRARIES} ${QT_MOBILITY_MULTIMEDIAKIT_LIBRARY})
|
TARGET_LINK_LIBRARIES(cockatrice cockatrice_common ${QT_LIBRARIES} ${QT_MOBILITY_MULTIMEDIAKIT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/cockatrice DESTINATION bin)
|
IF (NOT APPLE)
|
||||||
|
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/cockatrice DESTINATION bin)
|
||||||
|
ELSE (APPLE)
|
||||||
|
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/cockatrice.app DESTINATION bin)
|
||||||
|
ENDIF (NOT APPLE)
|
||||||
if (NOT WIN32 AND NOT APPLE)
|
if (NOT WIN32 AND NOT APPLE)
|
||||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.png DESTINATION share/icons/hicolor/48x48/apps)
|
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.png DESTINATION ${ICONDIR}/hicolor/48x48/apps)
|
||||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.svg DESTINATION share/icons/hicolor/scalable/apps)
|
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/cockatrice.svg DESTINATION ${ICONDIR}/hicolor/scalable/apps)
|
||||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION share/applications)
|
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cockatrice.desktop DESTINATION ${DESKTOPDIR})
|
||||||
INSTALL(FILES ${cockatrice_QM} DESTINATION share/cockatrice/translations)
|
INSTALL(FILES ${cockatrice_QM} DESTINATION share/cockatrice/translations)
|
||||||
ENDIF(NOT WIN32 AND NOT APPLE)
|
ENDIF(NOT WIN32 AND NOT APPLE)
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@
|
||||||
<file>resources/countries/es.svg</file>
|
<file>resources/countries/es.svg</file>
|
||||||
<file>resources/countries/fi.svg</file>
|
<file>resources/countries/fi.svg</file>
|
||||||
<file>resources/countries/fr.svg</file>
|
<file>resources/countries/fr.svg</file>
|
||||||
|
<file>resources/countries/ge.svg</file>
|
||||||
<file>resources/countries/gr.svg</file>
|
<file>resources/countries/gr.svg</file>
|
||||||
<file>resources/countries/gt.svg</file>
|
<file>resources/countries/gt.svg</file>
|
||||||
<file>resources/countries/hr.svg</file>
|
<file>resources/countries/hr.svg</file>
|
||||||
|
|
12
cockatrice/resources/countries/ge.svg
Normal file
12
cockatrice/resources/countries/ge.svg
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="900" height="600" viewBox="0 0 300 200">
|
||||||
|
<defs>
|
||||||
|
<g id="smallcross"><clipPath id="vclip"><path d="M-109,104 a104,104 0 0,0 0,-208 H109 a104,104 0 0,0 0,208 z"/></clipPath><path id="varm" d="M-55,74 a55,55 0 0,1 110,0 V-74 a55,55 0 0,1 -110,0 z" clip-path="url(#vclip)"/>
|
||||||
|
<use xlink:href="#varm" transform="rotate(90)"/></g>
|
||||||
|
</defs>
|
||||||
|
<rect width="300" height="200" style="fill:#fff"/>
|
||||||
|
<path d="m 130,0 0,80 -130,0 L 0,120 l 130,0 0,80 40,0 0,-80 130,0 0,-40 -130,0 L 170,0 130,0 z" style="fill:#ff0000" />
|
||||||
|
<use xlink:href="#smallcross" transform="translate(64.45,39.45)" fill="#f00"/>
|
||||||
|
<use xlink:href="#smallcross" transform="translate(235.55,160.55)" fill="#f00"/>
|
||||||
|
<use xlink:href="#smallcross" transform="translate(235.55,39.45)" fill="#f00"/>
|
||||||
|
<use xlink:href="#smallcross" transform="translate(64.45,160.55)" fill="#f00"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 970 B |
|
@ -117,7 +117,7 @@ bool CardDatabaseDisplayModel::filterAcceptsRow(int sourceRow, const QModelIndex
|
||||||
{
|
{
|
||||||
CardInfo const *info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
|
CardInfo const *info = static_cast<CardDatabaseModel *>(sourceModel())->getCard(sourceRow);
|
||||||
|
|
||||||
if (((isToken == ShowTrue) && !info->getIsToken()) || (isToken == ShowFalse) && info->getIsToken())
|
if (((isToken == ShowTrue) && !info->getIsToken()) || ((isToken == ShowFalse) && info->getIsToken()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!cardNameBeginning.isEmpty())
|
if (!cardNameBeginning.isEmpty())
|
||||||
|
|
|
@ -54,6 +54,35 @@ void ChatView::appendHtml(const QString &html)
|
||||||
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
verticalScrollBar()->setValue(verticalScrollBar()->maximum());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ChatView::appendCardTag(QTextCursor &cursor, const QString &cardName)
|
||||||
|
{
|
||||||
|
QTextCharFormat oldFormat = cursor.charFormat();
|
||||||
|
QTextCharFormat anchorFormat = oldFormat;
|
||||||
|
anchorFormat.setForeground(Qt::blue);
|
||||||
|
anchorFormat.setAnchor(true);
|
||||||
|
anchorFormat.setAnchorHref("card://" + cardName);
|
||||||
|
|
||||||
|
cursor.setCharFormat(anchorFormat);
|
||||||
|
cursor.insertText(cardName);
|
||||||
|
cursor.setCharFormat(oldFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatView::appendUrlTag(QTextCursor &cursor, QString url)
|
||||||
|
{
|
||||||
|
if (!url.contains("://"))
|
||||||
|
url.prepend("http://");
|
||||||
|
|
||||||
|
QTextCharFormat oldFormat = cursor.charFormat();
|
||||||
|
QTextCharFormat anchorFormat = oldFormat;
|
||||||
|
anchorFormat.setForeground(Qt::blue);
|
||||||
|
anchorFormat.setAnchor(true);
|
||||||
|
anchorFormat.setAnchorHref(url);
|
||||||
|
|
||||||
|
cursor.setCharFormat(anchorFormat);
|
||||||
|
cursor.insertText(url);
|
||||||
|
cursor.setCharFormat(oldFormat);
|
||||||
|
}
|
||||||
|
|
||||||
void ChatView::appendMessage(QString message, QString sender, UserLevelFlags userLevel, bool playerBold)
|
void ChatView::appendMessage(QString message, QString sender, UserLevelFlags userLevel, bool playerBold)
|
||||||
{
|
{
|
||||||
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
|
bool atBottom = verticalScrollBar()->value() >= verticalScrollBar()->maximum();
|
||||||
|
@ -113,14 +142,17 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use
|
||||||
else
|
else
|
||||||
message = message.mid(closeTagIndex + 7);
|
message = message.mid(closeTagIndex + 7);
|
||||||
|
|
||||||
QTextCharFormat tempFormat = messageFormat;
|
appendCardTag(cursor, cardName);
|
||||||
tempFormat.setForeground(Qt::blue);
|
} else if (message.startsWith("[[")) {
|
||||||
tempFormat.setAnchor(true);
|
message = message.mid(2);
|
||||||
tempFormat.setAnchorHref("card://" + cardName);
|
int closeTagIndex = message.indexOf("]]");
|
||||||
|
QString cardName = message.left(closeTagIndex);
|
||||||
|
if (closeTagIndex == -1)
|
||||||
|
message.clear();
|
||||||
|
else
|
||||||
|
message = message.mid(closeTagIndex + 2);
|
||||||
|
|
||||||
cursor.setCharFormat(tempFormat);
|
appendCardTag(cursor, cardName);
|
||||||
cursor.insertText(cardName);
|
|
||||||
cursor.setCharFormat(messageFormat);
|
|
||||||
} else if (message.startsWith("[url]")) {
|
} else if (message.startsWith("[url]")) {
|
||||||
message = message.mid(5);
|
message = message.mid(5);
|
||||||
int closeTagIndex = message.indexOf("[/url]");
|
int closeTagIndex = message.indexOf("[/url]");
|
||||||
|
@ -130,17 +162,7 @@ void ChatView::appendMessage(QString message, QString sender, UserLevelFlags use
|
||||||
else
|
else
|
||||||
message = message.mid(closeTagIndex + 6);
|
message = message.mid(closeTagIndex + 6);
|
||||||
|
|
||||||
if (!url.contains("://"))
|
appendUrlTag(cursor, url);
|
||||||
url.prepend("http://");
|
|
||||||
|
|
||||||
QTextCharFormat tempFormat = messageFormat;
|
|
||||||
tempFormat.setForeground(Qt::blue);
|
|
||||||
tempFormat.setAnchor(true);
|
|
||||||
tempFormat.setAnchorHref(url);
|
|
||||||
|
|
||||||
cursor.setCharFormat(tempFormat);
|
|
||||||
cursor.insertText(url);
|
|
||||||
cursor.setCharFormat(messageFormat);
|
|
||||||
} else
|
} else
|
||||||
from = 1;
|
from = 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ private:
|
||||||
QString hoveredContent;
|
QString hoveredContent;
|
||||||
QTextFragment getFragmentUnderMouse(const QPoint &pos) const;
|
QTextFragment getFragmentUnderMouse(const QPoint &pos) const;
|
||||||
QTextCursor prepareBlock(bool same = false);
|
QTextCursor prepareBlock(bool same = false);
|
||||||
|
void appendCardTag(QTextCursor &cursor, const QString &cardName);
|
||||||
|
void appendUrlTag(QTextCursor &cursor, QString url);
|
||||||
private slots:
|
private slots:
|
||||||
void openLink(const QUrl &link);
|
void openLink(const QUrl &link);
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -14,6 +14,7 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &allGameTypes, QWidget *
|
||||||
: QDialog(parent)
|
: QDialog(parent)
|
||||||
{
|
{
|
||||||
unavailableGamesVisibleCheckBox = new QCheckBox(tr("Show &unavailable games"));
|
unavailableGamesVisibleCheckBox = new QCheckBox(tr("Show &unavailable games"));
|
||||||
|
passwordProtectedGamesVisibleCheckBox = new QCheckBox(tr("Show &password protected games"));
|
||||||
|
|
||||||
QLabel *gameNameFilterLabel = new QLabel(tr("Game &description:"));
|
QLabel *gameNameFilterLabel = new QLabel(tr("Game &description:"));
|
||||||
gameNameFilterEdit = new QLineEdit;
|
gameNameFilterEdit = new QLineEdit;
|
||||||
|
@ -68,6 +69,7 @@ DlgFilterGames::DlgFilterGames(const QMap<int, QString> &allGameTypes, QWidget *
|
||||||
leftGrid->addWidget(creatorNameFilterEdit, 1, 1);
|
leftGrid->addWidget(creatorNameFilterEdit, 1, 1);
|
||||||
leftGrid->addWidget(maxPlayersGroupBox, 2, 0, 1, 2);
|
leftGrid->addWidget(maxPlayersGroupBox, 2, 0, 1, 2);
|
||||||
leftGrid->addWidget(unavailableGamesVisibleCheckBox, 3, 0, 1, 2);
|
leftGrid->addWidget(unavailableGamesVisibleCheckBox, 3, 0, 1, 2);
|
||||||
|
leftGrid->addWidget(passwordProtectedGamesVisibleCheckBox, 4, 0, 1, 2);
|
||||||
|
|
||||||
QVBoxLayout *leftColumn = new QVBoxLayout;
|
QVBoxLayout *leftColumn = new QVBoxLayout;
|
||||||
leftColumn->addLayout(leftGrid);
|
leftColumn->addLayout(leftGrid);
|
||||||
|
@ -102,6 +104,16 @@ void DlgFilterGames::setUnavailableGamesVisible(bool _unavailableGamesVisible)
|
||||||
unavailableGamesVisibleCheckBox->setChecked(_unavailableGamesVisible);
|
unavailableGamesVisibleCheckBox->setChecked(_unavailableGamesVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DlgFilterGames::getPasswordProtectedGamesVisible() const
|
||||||
|
{
|
||||||
|
return passwordProtectedGamesVisibleCheckBox->isChecked();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DlgFilterGames::setPasswordProtectedGamesVisible(bool _passwordProtectedGamesVisible)
|
||||||
|
{
|
||||||
|
passwordProtectedGamesVisibleCheckBox->setChecked(_passwordProtectedGamesVisible);
|
||||||
|
}
|
||||||
|
|
||||||
QString DlgFilterGames::getGameNameFilter() const
|
QString DlgFilterGames::getGameNameFilter() const
|
||||||
{
|
{
|
||||||
return gameNameFilterEdit->text();
|
return gameNameFilterEdit->text();
|
||||||
|
|
|
@ -13,6 +13,7 @@ class DlgFilterGames : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
QCheckBox *unavailableGamesVisibleCheckBox;
|
QCheckBox *unavailableGamesVisibleCheckBox;
|
||||||
|
QCheckBox *passwordProtectedGamesVisibleCheckBox;
|
||||||
QLineEdit *gameNameFilterEdit;
|
QLineEdit *gameNameFilterEdit;
|
||||||
QLineEdit *creatorNameFilterEdit;
|
QLineEdit *creatorNameFilterEdit;
|
||||||
QMap<int, QCheckBox *> gameTypeFilterCheckBoxes;
|
QMap<int, QCheckBox *> gameTypeFilterCheckBoxes;
|
||||||
|
@ -23,6 +24,8 @@ public:
|
||||||
|
|
||||||
bool getUnavailableGamesVisible() const;
|
bool getUnavailableGamesVisible() const;
|
||||||
void setUnavailableGamesVisible(bool _unavailableGamesVisible);
|
void setUnavailableGamesVisible(bool _unavailableGamesVisible);
|
||||||
|
bool getPasswordProtectedGamesVisible() const;
|
||||||
|
void setPasswordProtectedGamesVisible(bool _passwordProtectedGamesVisible);
|
||||||
QString getGameNameFilter() const;
|
QString getGameNameFilter() const;
|
||||||
void setGameNameFilter(const QString &_gameNameFilter);
|
void setGameNameFilter(const QString &_gameNameFilter);
|
||||||
QString getCreatorNameFilter() const;
|
QString getCreatorNameFilter() const;
|
||||||
|
|
|
@ -82,6 +82,7 @@ void GameSelector::actSetFilter()
|
||||||
gameTypeMap = gameListModel->getGameTypes().value(room->getRoomId());
|
gameTypeMap = gameListModel->getGameTypes().value(room->getRoomId());
|
||||||
DlgFilterGames dlg(gameTypeMap, this);
|
DlgFilterGames dlg(gameTypeMap, this);
|
||||||
dlg.setUnavailableGamesVisible(gameListProxyModel->getUnavailableGamesVisible());
|
dlg.setUnavailableGamesVisible(gameListProxyModel->getUnavailableGamesVisible());
|
||||||
|
dlg.setPasswordProtectedGamesVisible(gameListProxyModel->getPasswordProtectedGamesVisible());
|
||||||
dlg.setGameNameFilter(gameListProxyModel->getGameNameFilter());
|
dlg.setGameNameFilter(gameListProxyModel->getGameNameFilter());
|
||||||
dlg.setCreatorNameFilter(gameListProxyModel->getCreatorNameFilter());
|
dlg.setCreatorNameFilter(gameListProxyModel->getCreatorNameFilter());
|
||||||
dlg.setGameTypeFilter(gameListProxyModel->getGameTypeFilter());
|
dlg.setGameTypeFilter(gameListProxyModel->getGameTypeFilter());
|
||||||
|
@ -93,6 +94,7 @@ void GameSelector::actSetFilter()
|
||||||
clearFilterButton->setEnabled(true);
|
clearFilterButton->setEnabled(true);
|
||||||
|
|
||||||
gameListProxyModel->setUnavailableGamesVisible(dlg.getUnavailableGamesVisible());
|
gameListProxyModel->setUnavailableGamesVisible(dlg.getUnavailableGamesVisible());
|
||||||
|
gameListProxyModel->setPasswordProtectedGamesVisible(dlg.getPasswordProtectedGamesVisible());
|
||||||
gameListProxyModel->setGameNameFilter(dlg.getGameNameFilter());
|
gameListProxyModel->setGameNameFilter(dlg.getGameNameFilter());
|
||||||
gameListProxyModel->setCreatorNameFilter(dlg.getCreatorNameFilter());
|
gameListProxyModel->setCreatorNameFilter(dlg.getCreatorNameFilter());
|
||||||
gameListProxyModel->setGameTypeFilter(dlg.getGameTypeFilter());
|
gameListProxyModel->setGameTypeFilter(dlg.getGameTypeFilter());
|
||||||
|
|
|
@ -105,6 +105,12 @@ void GamesProxyModel::setUnavailableGamesVisible(bool _unavailableGamesVisible)
|
||||||
invalidateFilter();
|
invalidateFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GamesProxyModel::setPasswordProtectedGamesVisible(bool _passwordProtectedGamesVisible)
|
||||||
|
{
|
||||||
|
passwordProtectedGamesVisible = _passwordProtectedGamesVisible;
|
||||||
|
invalidateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
void GamesProxyModel::setGameNameFilter(const QString &_gameNameFilter)
|
void GamesProxyModel::setGameNameFilter(const QString &_gameNameFilter)
|
||||||
{
|
{
|
||||||
gameNameFilter = _gameNameFilter;
|
gameNameFilter = _gameNameFilter;
|
||||||
|
@ -133,6 +139,7 @@ void GamesProxyModel::setMaxPlayersFilter(int _maxPlayersFilterMin, int _maxPlay
|
||||||
void GamesProxyModel::resetFilterParameters()
|
void GamesProxyModel::resetFilterParameters()
|
||||||
{
|
{
|
||||||
unavailableGamesVisible = false;
|
unavailableGamesVisible = false;
|
||||||
|
passwordProtectedGamesVisible = false;
|
||||||
gameNameFilter = QString();
|
gameNameFilter = QString();
|
||||||
creatorNameFilter = QString();
|
creatorNameFilter = QString();
|
||||||
gameTypeFilter.clear();
|
gameTypeFilter.clear();
|
||||||
|
@ -158,6 +165,8 @@ bool GamesProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &/*sourc
|
||||||
if (game.only_registered())
|
if (game.only_registered())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!passwordProtectedGamesVisible && game.with_password())
|
||||||
|
return false;
|
||||||
if (!gameNameFilter.isEmpty())
|
if (!gameNameFilter.isEmpty())
|
||||||
if (!QString::fromStdString(game.description()).contains(gameNameFilter, Qt::CaseInsensitive))
|
if (!QString::fromStdString(game.description()).contains(gameNameFilter, Qt::CaseInsensitive))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -33,6 +33,7 @@ class GamesProxyModel : public QSortFilterProxyModel {
|
||||||
private:
|
private:
|
||||||
ServerInfo_User *ownUser;
|
ServerInfo_User *ownUser;
|
||||||
bool unavailableGamesVisible;
|
bool unavailableGamesVisible;
|
||||||
|
bool passwordProtectedGamesVisible;
|
||||||
QString gameNameFilter, creatorNameFilter;
|
QString gameNameFilter, creatorNameFilter;
|
||||||
QSet<int> gameTypeFilter;
|
QSet<int> gameTypeFilter;
|
||||||
int maxPlayersFilterMin, maxPlayersFilterMax;
|
int maxPlayersFilterMin, maxPlayersFilterMax;
|
||||||
|
@ -41,6 +42,8 @@ public:
|
||||||
|
|
||||||
bool getUnavailableGamesVisible() const { return unavailableGamesVisible; }
|
bool getUnavailableGamesVisible() const { return unavailableGamesVisible; }
|
||||||
void setUnavailableGamesVisible(bool _unavailableGamesVisible);
|
void setUnavailableGamesVisible(bool _unavailableGamesVisible);
|
||||||
|
bool getPasswordProtectedGamesVisible() const { return passwordProtectedGamesVisible; }
|
||||||
|
void setPasswordProtectedGamesVisible(bool _passwordProtectedGamesVisible);
|
||||||
QString getGameNameFilter() const { return gameNameFilter; }
|
QString getGameNameFilter() const { return gameNameFilter; }
|
||||||
void setGameNameFilter(const QString &_gameNameFilter);
|
void setGameNameFilter(const QString &_gameNameFilter);
|
||||||
QString getCreatorNameFilter() const { return creatorNameFilter; }
|
QString getCreatorNameFilter() const { return creatorNameFilter; }
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QGroupBox>
|
#include <QGroupBox>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
|
@ -24,24 +25,16 @@ ShutdownDialog::ShutdownDialog(QWidget *parent)
|
||||||
minutesEdit->setMinimum(0);
|
minutesEdit->setMinimum(0);
|
||||||
minutesEdit->setValue(5);
|
minutesEdit->setValue(5);
|
||||||
|
|
||||||
QPushButton *okButton = new QPushButton(tr("&OK"));
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||||
okButton->setAutoDefault(true);
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||||
okButton->setDefault(true);
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||||
connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));
|
|
||||||
QPushButton *cancelButton = new QPushButton(tr("&Cancel"));
|
|
||||||
connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
|
|
||||||
|
|
||||||
QHBoxLayout *buttonLayout = new QHBoxLayout;
|
|
||||||
buttonLayout->addStretch();
|
|
||||||
buttonLayout->addWidget(okButton);
|
|
||||||
buttonLayout->addWidget(cancelButton);
|
|
||||||
|
|
||||||
QGridLayout *mainLayout = new QGridLayout;
|
QGridLayout *mainLayout = new QGridLayout;
|
||||||
mainLayout->addWidget(reasonLabel, 0, 0);
|
mainLayout->addWidget(reasonLabel, 0, 0);
|
||||||
mainLayout->addWidget(reasonEdit, 0, 1);
|
mainLayout->addWidget(reasonEdit, 0, 1);
|
||||||
mainLayout->addWidget(minutesLabel, 1, 0);
|
mainLayout->addWidget(minutesLabel, 1, 0);
|
||||||
mainLayout->addWidget(minutesEdit, 1, 1);
|
mainLayout->addWidget(minutesEdit, 1, 1);
|
||||||
mainLayout->addLayout(buttonLayout, 2, 0, 1, 2);
|
mainLayout->addWidget(buttonBox, 2, 0, 1, 2);
|
||||||
|
|
||||||
setLayout(mainLayout);
|
setLayout(mainLayout);
|
||||||
setWindowTitle(tr("Shut down server"));
|
setWindowTitle(tr("Shut down server"));
|
||||||
|
|
|
@ -306,6 +306,8 @@ void TabDeckStorage::actDeleteRemoteDeck()
|
||||||
QString path = dir->getPath();
|
QString path = dir->getPath();
|
||||||
if (path.isEmpty())
|
if (path.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
if (QMessageBox::warning(this, tr("Delete remote folder"), tr("Are you sure you want to delete \"%1\"?").arg(path), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
Command_DeckDelDir cmd;
|
Command_DeckDelDir cmd;
|
||||||
cmd.set_path(path.toStdString());
|
cmd.set_path(path.toStdString());
|
||||||
pend = client->prepareSessionCommand(cmd);
|
pend = client->prepareSessionCommand(cmd);
|
||||||
|
|
|
@ -76,8 +76,10 @@ void RoomSelector::processListRoomsEvent(const Event_ListRooms &event)
|
||||||
twi->setData(0, Qt::DisplayRole, QString::fromStdString(room.name()));
|
twi->setData(0, Qt::DisplayRole, QString::fromStdString(room.name()));
|
||||||
if (room.has_description())
|
if (room.has_description())
|
||||||
twi->setData(1, Qt::DisplayRole, QString::fromStdString(room.description()));
|
twi->setData(1, Qt::DisplayRole, QString::fromStdString(room.description()));
|
||||||
twi->setData(2, Qt::DisplayRole, room.player_count());
|
if (room.has_player_count())
|
||||||
twi->setData(3, Qt::DisplayRole, room.game_count());
|
twi->setData(2, Qt::DisplayRole, room.player_count());
|
||||||
|
if (room.has_game_count())
|
||||||
|
twi->setData(3, Qt::DisplayRole, room.game_count());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +93,7 @@ void RoomSelector::processListRoomsEvent(const Event_ListRooms &event)
|
||||||
twi->setData(3, Qt::DisplayRole, room.game_count());
|
twi->setData(3, Qt::DisplayRole, room.game_count());
|
||||||
twi->setTextAlignment(2, Qt::AlignRight);
|
twi->setTextAlignment(2, Qt::AlignRight);
|
||||||
twi->setTextAlignment(3, Qt::AlignRight);
|
twi->setTextAlignment(3, Qt::AlignRight);
|
||||||
|
|
||||||
roomList->addTopLevelItem(twi);
|
roomList->addTopLevelItem(twi);
|
||||||
if (room.has_auto_join())
|
if (room.has_auto_join())
|
||||||
if (room.auto_join())
|
if (room.auto_join())
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
Server::Server(bool _threaded, QObject *parent)
|
Server::Server(bool _threaded, QObject *parent)
|
||||||
: QObject(parent), threaded(_threaded), clientsLock(QReadWriteLock::Recursive), nextLocalGameId(0)
|
: QObject(parent), threaded(_threaded), nextLocalGameId(0)
|
||||||
{
|
{
|
||||||
qRegisterMetaType<ServerInfo_Game>("ServerInfo_Game");
|
qRegisterMetaType<ServerInfo_Game>("ServerInfo_Game");
|
||||||
qRegisterMetaType<ServerInfo_Room>("ServerInfo_Room");
|
qRegisterMetaType<ServerInfo_Room>("ServerInfo_Room");
|
||||||
|
@ -79,10 +79,9 @@ void Server::prepareDestroy()
|
||||||
clientsLock.unlock();
|
clientsLock.unlock();
|
||||||
} while (!done);
|
} while (!done);
|
||||||
} else {
|
} else {
|
||||||
clientsLock.lockForWrite();
|
// no locking is needed in unthreaded mode
|
||||||
while (!clients.isEmpty())
|
while (!clients.isEmpty())
|
||||||
clients.first()->prepareDestroy();
|
clients.first()->prepareDestroy();
|
||||||
clientsLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
roomsLock.lockForWrite();
|
roomsLock.lockForWrite();
|
||||||
|
@ -141,7 +140,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
|
||||||
}
|
}
|
||||||
|
|
||||||
users.insert(name, session);
|
users.insert(name, session);
|
||||||
qDebug() << "Server::loginUser: name=" << name;
|
qDebug() << "Server::loginUser:" << session << "name=" << name;
|
||||||
|
|
||||||
data.set_session_id(databaseInterface->startSession(name, session->getAddress()));
|
data.set_session_id(databaseInterface->startSession(name, session->getAddress()));
|
||||||
databaseInterface->unlockSessionTables();
|
databaseInterface->unlockSessionTables();
|
||||||
|
@ -159,7 +158,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
|
||||||
clients[i]->sendProtocolItem(*se);
|
clients[i]->sendProtocolItem(*se);
|
||||||
delete se;
|
delete se;
|
||||||
|
|
||||||
event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true));
|
event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true));
|
||||||
locker.unlock();
|
locker.unlock();
|
||||||
|
|
||||||
se = Server_ProtocolHandler::prepareSessionEvent(event);
|
se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||||
|
@ -187,6 +186,17 @@ QList<PlayerReference> Server::getPersistentPlayerReferences(const QString &user
|
||||||
return persistentPlayers.values(userName);
|
return persistentPlayers.values(userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Server_AbstractUserInterface *Server::findUser(const QString &userName) const
|
||||||
|
{
|
||||||
|
// Call this only with clientsLock set.
|
||||||
|
|
||||||
|
Server_AbstractUserInterface *userHandler = users.value(userName);
|
||||||
|
if (userHandler)
|
||||||
|
return userHandler;
|
||||||
|
else
|
||||||
|
return externalUsers.value(userName);
|
||||||
|
}
|
||||||
|
|
||||||
void Server::addClient(Server_ProtocolHandler *client)
|
void Server::addClient(Server_ProtocolHandler *client)
|
||||||
{
|
{
|
||||||
QWriteLocker locker(&clientsLock);
|
QWriteLocker locker(&clientsLock);
|
||||||
|
@ -218,13 +228,13 @@ void Server::removeClient(Server_ProtocolHandler *client)
|
||||||
qDebug() << "closed session id:" << sessionId;
|
qDebug() << "closed session id:" << sessionId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qDebug() << "Server::removeClient:" << clients.size() << "clients; " << users.size() << "users left";
|
qDebug() << "Server::removeClient: removed" << (void *) client << ";" << clients.size() << "clients; " << users.size() << "users left";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::externalUserJoined(const ServerInfo_User &userInfo)
|
void Server::externalUserJoined(const ServerInfo_User &userInfo)
|
||||||
{
|
{
|
||||||
// This function is always called from the main thread via signal/slot.
|
// This function is always called from the main thread via signal/slot.
|
||||||
QWriteLocker locker(&clientsLock);
|
clientsLock.lockForWrite();
|
||||||
|
|
||||||
Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo));
|
Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo));
|
||||||
externalUsers.insert(QString::fromStdString(userInfo.name()), newUser);
|
externalUsers.insert(QString::fromStdString(userInfo.name()), newUser);
|
||||||
|
@ -263,7 +273,7 @@ void Server::externalUserLeft(const QString &userName)
|
||||||
if (!room)
|
if (!room)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QMutexLocker roomGamesLocker(&room->gamesMutex);
|
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||||
Server_Game *game = room->getGames().value(userGamesIterator.key());
|
Server_Game *game = room->getGames().value(userGamesIterator.key());
|
||||||
if (!game)
|
if (!game)
|
||||||
continue;
|
continue;
|
||||||
|
@ -389,7 +399,7 @@ void Server::externalGameCommandContainerReceived(const CommandContainer &cont,
|
||||||
throw Response::RespNotInRoom;
|
throw Response::RespNotInRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker roomGamesLocker(&room->gamesMutex);
|
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||||
Server_Game *game = room->getGames().value(cont.game_id());
|
Server_Game *game = room->getGames().value(cont.game_id());
|
||||||
if (!game) {
|
if (!game) {
|
||||||
qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found";
|
qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found";
|
||||||
|
@ -499,7 +509,7 @@ int Server::getGamesCount() const
|
||||||
QMapIterator<int, Server_Room *> roomIterator(rooms);
|
QMapIterator<int, Server_Room *> roomIterator(rooms);
|
||||||
while (roomIterator.hasNext()) {
|
while (roomIterator.hasNext()) {
|
||||||
Server_Room *room = roomIterator.next().value();
|
Server_Room *room = roomIterator.next().value();
|
||||||
QMutexLocker roomLocker(&room->gamesMutex);
|
QReadLocker roomLocker(&room->gamesLock);
|
||||||
result += room->getGames().size();
|
result += room->getGames().size();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -42,9 +42,11 @@ public:
|
||||||
mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock
|
mutable QReadWriteLock clientsLock, roomsLock; // locking order: roomsLock before clientsLock
|
||||||
Server(bool _threaded, QObject *parent = 0);
|
Server(bool _threaded, QObject *parent = 0);
|
||||||
~Server();
|
~Server();
|
||||||
|
void setThreaded(bool _threaded) { threaded = _threaded; }
|
||||||
AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft);
|
AuthenticationResult loginUser(Server_ProtocolHandler *session, QString &name, const QString &password, QString &reason, int &secondsLeft);
|
||||||
const QMap<int, Server_Room *> &getRooms() { return rooms; }
|
const QMap<int, Server_Room *> &getRooms() { return rooms; }
|
||||||
|
|
||||||
|
Server_AbstractUserInterface *findUser(const QString &userName) const;
|
||||||
const QMap<QString, Server_ProtocolHandler *> &getUsers() const { return users; }
|
const QMap<QString, Server_ProtocolHandler *> &getUsers() const { return users; }
|
||||||
const QMap<qint64, Server_ProtocolHandler *> &getUsersBySessionId() const { return usersBySessionId; }
|
const QMap<qint64, Server_ProtocolHandler *> &getUsersBySessionId() const { return usersBySessionId; }
|
||||||
void addClient(Server_ProtocolHandler *player);
|
void addClient(Server_ProtocolHandler *player);
|
||||||
|
@ -62,7 +64,6 @@ public:
|
||||||
|
|
||||||
Server_DatabaseInterface *getDatabaseInterface() const;
|
Server_DatabaseInterface *getDatabaseInterface() const;
|
||||||
int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; }
|
int getNextLocalGameId() { QMutexLocker locker(&nextLocalGameIdMutex); return ++nextLocalGameId; }
|
||||||
virtual void storeGameInformation(int secondsElapsed, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replays) { }
|
|
||||||
|
|
||||||
void sendIsl_Response(const Response &item, int serverId = -1, qint64 sessionId = -1);
|
void sendIsl_Response(const Response &item, int serverId = -1, qint64 sessionId = -1);
|
||||||
void sendIsl_SessionEvent(const SessionEvent &item, int serverId = -1, qint64 sessionId = -1);
|
void sendIsl_SessionEvent(const SessionEvent &item, int serverId = -1, qint64 sessionId = -1);
|
||||||
|
|
|
@ -78,7 +78,7 @@ void Server_AbstractUserInterface::joinPersistentGames(ResponseContainer &rc)
|
||||||
Server_Room *room = server->getRooms().value(pr.getRoomId());
|
Server_Room *room = server->getRooms().value(pr.getRoomId());
|
||||||
if (!room)
|
if (!room)
|
||||||
continue;
|
continue;
|
||||||
QMutexLocker roomGamesLocker(&room->gamesMutex);
|
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||||
|
|
||||||
Server_Game *game = room->getGames().value(pr.getGameId());
|
Server_Game *game = room->getGames().value(pr.getGameId());
|
||||||
if (!game)
|
if (!game)
|
||||||
|
|
|
@ -51,28 +51,78 @@ void Server_CardZone::shuffle()
|
||||||
playersWithWritePermission.clear();
|
playersWithWritePermission.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Server_CardZone::removeCardFromCoordMap(Server_Card *card, int oldX, int oldY)
|
||||||
|
{
|
||||||
|
if (oldX < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const int baseX = (oldX / 3) * 3;
|
||||||
|
QMap<int, Server_Card *> &coordMap = coordinateMap[oldY];
|
||||||
|
|
||||||
|
if (coordMap.contains(baseX) && coordMap.contains(baseX + 1) && coordMap.contains(baseX + 2))
|
||||||
|
// If the removal of this card has opened up a previously full pile...
|
||||||
|
freePilesMap[oldY].insert(coordMap.value(baseX)->getName(), baseX);
|
||||||
|
|
||||||
|
coordMap.remove(oldX);
|
||||||
|
|
||||||
|
if (!(coordMap.contains(baseX) && coordMap.value(baseX)->getName() == card->getName()) && !(coordMap.contains(baseX + 1) && coordMap.value(baseX + 1)->getName() == card->getName()) && !(coordMap.contains(baseX + 2) && coordMap.value(baseX + 2)->getName() == card->getName()))
|
||||||
|
// If this card was the last one with this name...
|
||||||
|
freePilesMap[oldY].remove(card->getName(), baseX);
|
||||||
|
|
||||||
|
if (!coordMap.contains(baseX) && !coordMap.contains(baseX + 1) && !coordMap.contains(baseX + 2)) {
|
||||||
|
// If the removal of this card has freed a whole pile, i.e. it was the last card in it...
|
||||||
|
if (baseX < freeSpaceMap[oldY])
|
||||||
|
freeSpaceMap[oldY] = baseX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server_CardZone::insertCardIntoCoordMap(Server_Card *card, int x, int y)
|
||||||
|
{
|
||||||
|
if (x < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
coordinateMap[y].insert(x, card);
|
||||||
|
if (!(x % 3)) {
|
||||||
|
if (!freePilesMap[y].contains(card->getName(), x) && card->getAttachedCards().isEmpty())
|
||||||
|
freePilesMap[y].insert(card->getName(), x);
|
||||||
|
if (freeSpaceMap[y] == x) {
|
||||||
|
int nextFreeX = x;
|
||||||
|
do {
|
||||||
|
nextFreeX += 3;
|
||||||
|
} while (coordinateMap[y].contains(nextFreeX) || coordinateMap[y].contains(nextFreeX + 1) || coordinateMap[y].contains(nextFreeX + 2));
|
||||||
|
freeSpaceMap[y] = nextFreeX;
|
||||||
|
}
|
||||||
|
} else if (!((x - 2) % 3)) {
|
||||||
|
const int baseX = (x / 3) * 3;
|
||||||
|
freePilesMap[y].remove(coordinateMap[y].value(baseX)->getName(), baseX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int Server_CardZone::removeCard(Server_Card *card)
|
int Server_CardZone::removeCard(Server_Card *card)
|
||||||
{
|
{
|
||||||
int index = cards.indexOf(card);
|
int index = cards.indexOf(card);
|
||||||
cards.removeAt(index);
|
cards.removeAt(index);
|
||||||
|
if (has_coords)
|
||||||
|
removeCardFromCoordMap(card, card->getX(), card->getY());
|
||||||
card->setZone(0);
|
card->setZone(0);
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
Server_Card *Server_CardZone::getCard(int id, int *position)
|
Server_Card *Server_CardZone::getCard(int id, int *position, bool remove)
|
||||||
{
|
{
|
||||||
if (type != ServerInfo_Zone::HiddenZone) {
|
if (type != ServerInfo_Zone::HiddenZone) {
|
||||||
QListIterator<Server_Card *> CardIterator(cards);
|
for (int i = 0; i < cards.size(); ++i) {
|
||||||
int i = 0;
|
Server_Card *tmp = cards[i];
|
||||||
while (CardIterator.hasNext()) {
|
|
||||||
Server_Card *tmp = CardIterator.next();
|
|
||||||
if (tmp->getId() == id) {
|
if (tmp->getId() == id) {
|
||||||
if (position)
|
if (position)
|
||||||
*position = i;
|
*position = i;
|
||||||
|
if (remove) {
|
||||||
|
cards.removeAt(i);
|
||||||
|
tmp->setZone(0);
|
||||||
|
}
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,30 +131,29 @@ Server_Card *Server_CardZone::getCard(int id, int *position)
|
||||||
Server_Card *tmp = cards[id];
|
Server_Card *tmp = cards[id];
|
||||||
if (position)
|
if (position)
|
||||||
*position = id;
|
*position = id;
|
||||||
|
if (remove) {
|
||||||
|
cards.removeAt(id);
|
||||||
|
tmp->setZone(0);
|
||||||
|
}
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Server_CardZone::getFreeGridColumn(int x, int y, const QString &cardName) const
|
int Server_CardZone::getFreeGridColumn(int x, int y, const QString &cardName) const
|
||||||
{
|
{
|
||||||
QMap<int, Server_Card *> coordMap;
|
const QMap<int, Server_Card *> &coordMap = coordinateMap.value(y);
|
||||||
for (int i = 0; i < cards.size(); ++i)
|
|
||||||
if (cards[i]->getY() == y)
|
|
||||||
coordMap.insert(cards[i]->getX(), cards[i]);
|
|
||||||
|
|
||||||
int resultX = 0;
|
|
||||||
if (x == -1) {
|
if (x == -1) {
|
||||||
for (int i = 0; i < cards.size(); ++i)
|
if (freePilesMap[y].contains(cardName)) {
|
||||||
if ((cards[i]->getName() == cardName) && !(cards[i]->getX() % 3) && (cards[i]->getY() == y)) {
|
x = (freePilesMap[y].value(cardName) / 3) * 3;
|
||||||
if (!cards[i]->getAttachedCards().isEmpty())
|
if (!coordMap.contains(x))
|
||||||
continue;
|
return x;
|
||||||
if (!coordMap.value(cards[i]->getX() + 1))
|
else if (!coordMap.contains(x + 1))
|
||||||
return cards[i]->getX() + 1;
|
return x + 1;
|
||||||
if (!coordMap.value(cards[i]->getX() + 2))
|
else
|
||||||
return cards[i]->getX() + 2;
|
return x + 2;
|
||||||
}
|
}
|
||||||
} else if (x == -2) {
|
} else if (x >= 0) {
|
||||||
} else {
|
int resultX = 0;
|
||||||
x = (x / 3) * 3;
|
x = (x / 3) * 3;
|
||||||
if (!coordMap.contains(x))
|
if (!coordMap.contains(x))
|
||||||
resultX = x;
|
resultX = x;
|
||||||
|
@ -119,13 +168,14 @@ int Server_CardZone::getFreeGridColumn(int x, int y, const QString &cardName) co
|
||||||
resultX = x;
|
resultX = x;
|
||||||
x = -1;
|
x = -1;
|
||||||
}
|
}
|
||||||
|
if (x < 0)
|
||||||
|
while (coordMap.contains(resultX))
|
||||||
|
resultX += 3;
|
||||||
|
|
||||||
|
return resultX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x < 0)
|
return freeSpaceMap[y];
|
||||||
while (coordMap.value(resultX))
|
|
||||||
resultX += 3;
|
|
||||||
|
|
||||||
return resultX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server_CardZone::isColumnStacked(int x, int y) const
|
bool Server_CardZone::isColumnStacked(int x, int y) const
|
||||||
|
@ -133,12 +183,7 @@ bool Server_CardZone::isColumnStacked(int x, int y) const
|
||||||
if (!has_coords)
|
if (!has_coords)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
QMap<int, Server_Card *> coordMap;
|
return coordinateMap[y].contains((x / 3) * 3 + 1);
|
||||||
for (int i = 0; i < cards.size(); ++i)
|
|
||||||
if (cards[i]->getY() == y)
|
|
||||||
coordMap.insert(cards[i]->getX(), cards[i]);
|
|
||||||
|
|
||||||
return coordMap.contains((x / 3) * 3 + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Server_CardZone::isColumnEmpty(int x, int y) const
|
bool Server_CardZone::isColumnEmpty(int x, int y) const
|
||||||
|
@ -146,63 +191,68 @@ bool Server_CardZone::isColumnEmpty(int x, int y) const
|
||||||
if (!has_coords)
|
if (!has_coords)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
QMap<int, Server_Card *> coordMap;
|
return !coordinateMap[y].contains((x / 3) * 3);
|
||||||
for (int i = 0; i < cards.size(); ++i)
|
|
||||||
if (cards[i]->getY() == y)
|
|
||||||
coordMap.insert(cards[i]->getX(), cards[i]);
|
|
||||||
|
|
||||||
return !coordMap.contains((x / 3) * 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server_CardZone::moveCard(GameEventStorage &ges, QMap<int, Server_Card *> &coordMap, Server_Card *card, int x, int y)
|
void Server_CardZone::moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y)
|
||||||
{
|
{
|
||||||
coordMap.remove(card->getY() * 10000 + card->getX());
|
|
||||||
|
|
||||||
CardToMove *cardToMove = new CardToMove;
|
CardToMove *cardToMove = new CardToMove;
|
||||||
cardToMove->set_card_id(card->getId());
|
cardToMove->set_card_id(card->getId());
|
||||||
player->moveCard(ges, this, QList<const CardToMove *>() << cardToMove, this, x, y, false, false);
|
player->moveCard(ges, this, QList<const CardToMove *>() << cardToMove, this, x, y, false, false);
|
||||||
delete cardToMove;
|
delete cardToMove;
|
||||||
|
|
||||||
coordMap.insert(y * 10000 + x, card);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server_CardZone::fixFreeSpaces(GameEventStorage &ges)
|
void Server_CardZone::fixFreeSpaces(GameEventStorage &ges)
|
||||||
{
|
{
|
||||||
QMap<int, Server_Card *> coordMap;
|
if (!has_coords)
|
||||||
QSet<int> placesToLook;
|
return;
|
||||||
for (int i = 0; i < cards.size(); ++i) {
|
|
||||||
coordMap.insert(cards[i]->getY() * 10000 + cards[i]->getX(), cards[i]);
|
|
||||||
placesToLook.insert(cards[i]->getY() * 10000 + (cards[i]->getX() / 3) * 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSetIterator<int> placeIterator(placesToLook);
|
QSet<QPair<int, int> > placesToLook;
|
||||||
|
for (int i = 0; i < cards.size(); ++i)
|
||||||
|
placesToLook.insert(QPair<int, int>((cards[i]->getX() / 3) * 3, cards[i]->getY()));
|
||||||
|
|
||||||
|
QSetIterator<QPair<int, int> > placeIterator(placesToLook);
|
||||||
while (placeIterator.hasNext()) {
|
while (placeIterator.hasNext()) {
|
||||||
int foo = placeIterator.next();
|
const QPair<int, int> &foo = placeIterator.next();
|
||||||
int y = foo / 10000;
|
int baseX = foo.first;
|
||||||
int baseX = foo - y * 10000;
|
int y = foo.second;
|
||||||
|
|
||||||
if (!coordMap.contains(y * 10000 + baseX)) {
|
if (!coordinateMap[y].contains(baseX)) {
|
||||||
if (coordMap.contains(y * 10000 + baseX + 1))
|
if (coordinateMap[y].contains(baseX + 1))
|
||||||
moveCard(ges, coordMap, coordMap.value(y * 10000 + baseX + 1), baseX, y);
|
moveCardInRow(ges, coordinateMap[y].value(baseX + 1), baseX, y);
|
||||||
else if (coordMap.contains(y * 10000 + baseX + 2)) {
|
else if (coordinateMap[y].contains(baseX + 2)) {
|
||||||
moveCard(ges, coordMap, coordMap.value(y * 10000 + baseX + 2), baseX, y);
|
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX, y);
|
||||||
continue;
|
continue;
|
||||||
} else
|
} else
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!coordMap.contains(y * 10000 + baseX + 1) && coordMap.contains(y * 10000 + baseX + 2))
|
if (!coordinateMap[y].contains(baseX + 1) && coordinateMap[y].contains(baseX + 2))
|
||||||
moveCard(ges, coordMap, coordMap.value(y * 10000 + baseX + 2), baseX + 1, y);
|
moveCardInRow(ges, coordinateMap[y].value(baseX + 2), baseX + 1, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Server_CardZone::updateCardCoordinates(Server_Card *card, int oldX, int oldY)
|
||||||
|
{
|
||||||
|
if (!has_coords)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (oldX != -1)
|
||||||
|
removeCardFromCoordMap(card, oldX, oldY);
|
||||||
|
insertCardIntoCoordMap(card, card->getX(), card->getY());
|
||||||
|
}
|
||||||
|
|
||||||
void Server_CardZone::insertCard(Server_Card *card, int x, int y)
|
void Server_CardZone::insertCard(Server_Card *card, int x, int y)
|
||||||
{
|
{
|
||||||
if (hasCoords()) {
|
if (hasCoords()) {
|
||||||
card->setCoords(x, y);
|
card->setCoords(x, y);
|
||||||
cards.append(card);
|
cards.append(card);
|
||||||
|
insertCardIntoCoordMap(card, x, y);
|
||||||
} else {
|
} else {
|
||||||
card->setCoords(0, 0);
|
card->setCoords(0, 0);
|
||||||
cards.insert(x, card);
|
if (x == -1)
|
||||||
|
cards.append(card);
|
||||||
|
else
|
||||||
|
cards.insert(x, card);
|
||||||
}
|
}
|
||||||
card->setZone(this);
|
card->setZone(this);
|
||||||
}
|
}
|
||||||
|
@ -212,6 +262,9 @@ void Server_CardZone::clear()
|
||||||
for (int i = 0; i < cards.size(); i++)
|
for (int i = 0; i < cards.size(); i++)
|
||||||
delete cards.at(i);
|
delete cards.at(i);
|
||||||
cards.clear();
|
cards.clear();
|
||||||
|
coordinateMap.clear();
|
||||||
|
freePilesMap.clear();
|
||||||
|
freeSpaceMap.clear();
|
||||||
playersWithWritePermission.clear();
|
playersWithWritePermission.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,12 +40,19 @@ private:
|
||||||
int cardsBeingLookedAt;
|
int cardsBeingLookedAt;
|
||||||
QSet<int> playersWithWritePermission;
|
QSet<int> playersWithWritePermission;
|
||||||
bool alwaysRevealTopCard;
|
bool alwaysRevealTopCard;
|
||||||
|
QList<Server_Card *> cards;
|
||||||
|
QMap<int, QMap<int, Server_Card *> > coordinateMap; // y -> (x -> card)
|
||||||
|
QMap<int, QMultiMap<QString, int> > freePilesMap; // y -> (cardName -> x)
|
||||||
|
QMap<int, int> freeSpaceMap; // y -> x
|
||||||
|
void removeCardFromCoordMap(Server_Card *card, int oldX, int oldY);
|
||||||
|
void insertCardIntoCoordMap(Server_Card *card, int x, int y);
|
||||||
public:
|
public:
|
||||||
Server_CardZone(Server_Player *_player, const QString &_name, bool _has_coords, ServerInfo_Zone::ZoneType _type);
|
Server_CardZone(Server_Player *_player, const QString &_name, bool _has_coords, ServerInfo_Zone::ZoneType _type);
|
||||||
~Server_CardZone();
|
~Server_CardZone();
|
||||||
|
|
||||||
|
const QList<Server_Card *> &getCards() const { return cards; }
|
||||||
int removeCard(Server_Card *card);
|
int removeCard(Server_Card *card);
|
||||||
Server_Card *getCard(int id, int *position = NULL);
|
Server_Card *getCard(int id, int *position = NULL, bool remove = false);
|
||||||
|
|
||||||
int getCardsBeingLookedAt() const { return cardsBeingLookedAt; }
|
int getCardsBeingLookedAt() const { return cardsBeingLookedAt; }
|
||||||
void setCardsBeingLookedAt(int _cardsBeingLookedAt) { cardsBeingLookedAt = _cardsBeingLookedAt; }
|
void setCardsBeingLookedAt(int _cardsBeingLookedAt) { cardsBeingLookedAt = _cardsBeingLookedAt; }
|
||||||
|
@ -59,9 +66,9 @@ public:
|
||||||
bool isColumnEmpty(int x, int y) const;
|
bool isColumnEmpty(int x, int y) const;
|
||||||
bool isColumnStacked(int x, int y) const;
|
bool isColumnStacked(int x, int y) const;
|
||||||
void fixFreeSpaces(GameEventStorage &ges);
|
void fixFreeSpaces(GameEventStorage &ges);
|
||||||
void moveCard(GameEventStorage &ges, QMap<int, Server_Card *> &coordMap, Server_Card *card, int x, int y);
|
void moveCardInRow(GameEventStorage &ges, Server_Card *card, int x, int y);
|
||||||
QList<Server_Card *> cards;
|
|
||||||
void insertCard(Server_Card *card, int x, int y);
|
void insertCard(Server_Card *card, int x, int y);
|
||||||
|
void updateCardCoordinates(Server_Card *card, int oldX, int oldY);
|
||||||
void shuffle();
|
void shuffle();
|
||||||
void clear();
|
void clear();
|
||||||
void addWritePermission(int playerId);
|
void addWritePermission(int playerId);
|
||||||
|
|
|
@ -19,7 +19,7 @@ public:
|
||||||
virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; }
|
virtual bool isInIgnoreList(const QString &whoseList, const QString &who) { return false; }
|
||||||
virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0;
|
virtual ServerInfo_User getUserData(const QString &name, bool withId = false) = 0;
|
||||||
virtual void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replayList) { }
|
virtual void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replayList) { }
|
||||||
virtual DeckList *getDeckFromDatabase(int deckId, const QString &userName) { return 0; }
|
virtual DeckList *getDeckFromDatabase(int deckId, int userId) { return 0; }
|
||||||
|
|
||||||
virtual qint64 startSession(const QString &userName, const QString &address) { return 0; }
|
virtual qint64 startSession(const QString &userName, const QString &address) { return 0; }
|
||||||
public slots:
|
public slots:
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "pb/event_set_active_phase.pb.h"
|
#include "pb/event_set_active_phase.pb.h"
|
||||||
#include "pb/serverinfo_playerping.pb.h"
|
#include "pb/serverinfo_playerping.pb.h"
|
||||||
#include "pb/game_replay.pb.h"
|
#include "pb/game_replay.pb.h"
|
||||||
|
#include "pb/event_replay_added.pb.h"
|
||||||
#include <google/protobuf/descriptor.h>
|
#include <google/protobuf/descriptor.h>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
@ -90,7 +91,7 @@ Server_Game::Server_Game(const ServerInfo_User &_creatorInfo, int _gameId, const
|
||||||
|
|
||||||
Server_Game::~Server_Game()
|
Server_Game::~Server_Game()
|
||||||
{
|
{
|
||||||
room->gamesMutex.lock();
|
room->gamesLock.lockForWrite();
|
||||||
gameMutex.lock();
|
gameMutex.lock();
|
||||||
|
|
||||||
gameClosed = true;
|
gameClosed = true;
|
||||||
|
@ -106,11 +107,11 @@ Server_Game::~Server_Game()
|
||||||
creatorInfo = 0;
|
creatorInfo = 0;
|
||||||
|
|
||||||
gameMutex.unlock();
|
gameMutex.unlock();
|
||||||
room->gamesMutex.unlock();
|
room->gamesLock.unlock();
|
||||||
|
|
||||||
currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame);
|
currentReplay->set_duration_seconds(secondsElapsed - startTimeOfThisGame);
|
||||||
replayList.append(currentReplay);
|
replayList.append(currentReplay);
|
||||||
room->getServer()->storeGameInformation(secondsElapsed, allPlayersEver, allSpectatorsEver, replayList);
|
storeGameInformation();
|
||||||
|
|
||||||
for (int i = 0; i < replayList.size(); ++i)
|
for (int i = 0; i < replayList.size(); ++i)
|
||||||
delete replayList[i];
|
delete replayList[i];
|
||||||
|
@ -118,6 +119,51 @@ Server_Game::~Server_Game()
|
||||||
qDebug() << "Server_Game destructor: gameId=" << gameId;
|
qDebug() << "Server_Game destructor: gameId=" << gameId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Server_Game::storeGameInformation()
|
||||||
|
{
|
||||||
|
const ServerInfo_Game &gameInfo = replayList.first()->game_info();
|
||||||
|
|
||||||
|
Event_ReplayAdded replayEvent;
|
||||||
|
ServerInfo_ReplayMatch *replayMatchInfo = replayEvent.mutable_match_info();
|
||||||
|
replayMatchInfo->set_game_id(gameInfo.game_id());
|
||||||
|
replayMatchInfo->set_room_name(room->getName().toStdString());
|
||||||
|
replayMatchInfo->set_time_started(QDateTime::currentDateTime().addSecs(-secondsElapsed).toTime_t());
|
||||||
|
replayMatchInfo->set_length(secondsElapsed);
|
||||||
|
replayMatchInfo->set_game_name(gameInfo.description());
|
||||||
|
|
||||||
|
const QStringList &allGameTypes = room->getGameTypes();
|
||||||
|
QStringList gameTypes;
|
||||||
|
for (int i = gameInfo.game_types_size() - 1; i >= 0; --i)
|
||||||
|
gameTypes.append(allGameTypes[gameInfo.game_types(i)]);
|
||||||
|
|
||||||
|
QSetIterator<QString> playerIterator(allPlayersEver);
|
||||||
|
while (playerIterator.hasNext())
|
||||||
|
replayMatchInfo->add_player_names(playerIterator.next().toStdString());
|
||||||
|
|
||||||
|
for (int i = 0; i < replayList.size(); ++i) {
|
||||||
|
ServerInfo_Replay *replayInfo = replayMatchInfo->add_replay_list();
|
||||||
|
replayInfo->set_replay_id(replayList[i]->replay_id());
|
||||||
|
replayInfo->set_replay_name(gameInfo.description());
|
||||||
|
replayInfo->set_duration(replayList[i]->duration_seconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
QSet<QString> allUsersInGame = allPlayersEver + allSpectatorsEver;
|
||||||
|
QSetIterator<QString> allUsersIterator(allUsersInGame);
|
||||||
|
|
||||||
|
SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent);
|
||||||
|
Server *server = room->getServer();
|
||||||
|
server->clientsLock.lockForRead();
|
||||||
|
while (allUsersIterator.hasNext()) {
|
||||||
|
Server_AbstractUserInterface *userHandler = server->findUser(allUsersIterator.next());
|
||||||
|
if (userHandler)
|
||||||
|
userHandler->sendProtocolItem(*sessionEvent);
|
||||||
|
}
|
||||||
|
server->clientsLock.unlock();
|
||||||
|
delete sessionEvent;
|
||||||
|
|
||||||
|
server->getDatabaseInterface()->storeGameInformation(room->getName(), gameTypes, gameInfo, allPlayersEver, allSpectatorsEver, replayList);
|
||||||
|
}
|
||||||
|
|
||||||
void Server_Game::pingClockTimeout()
|
void Server_Game::pingClockTimeout()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&gameMutex);
|
QMutexLocker locker(&gameMutex);
|
||||||
|
@ -359,7 +405,7 @@ Response::ResponseCode Server_Game::checkJoin(ServerInfo_User *user, const QStri
|
||||||
return Response::RespWrongPassword;
|
return Response::RespWrongPassword;
|
||||||
if (!(user->user_level() & ServerInfo_User::IsRegistered) && onlyRegistered)
|
if (!(user->user_level() & ServerInfo_User::IsRegistered) && onlyRegistered)
|
||||||
return Response::RespUserLevelTooLow;
|
return Response::RespUserLevelTooLow;
|
||||||
if (onlyBuddies)
|
if (onlyBuddies && (user->name() != creatorInfo->name()))
|
||||||
if (!databaseInterface->isInBuddyList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name())))
|
if (!databaseInterface->isInBuddyList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name())))
|
||||||
return Response::RespOnlyBuddies;
|
return Response::RespOnlyBuddies;
|
||||||
if (databaseInterface->isInIgnoreList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name())))
|
if (databaseInterface->isInIgnoreList(QString::fromStdString(creatorInfo->name()), QString::fromStdString(user->name())))
|
||||||
|
@ -390,7 +436,7 @@ void Server_Game::addPlayer(Server_AbstractUserInterface *userInterface, Respons
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&gameMutex);
|
QMutexLocker locker(&gameMutex);
|
||||||
|
|
||||||
Server_Player *newPlayer = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true), spectator, userInterface);
|
Server_Player *newPlayer = new Server_Player(this, nextPlayerId++, userInterface->copyUserInfo(true, true), spectator, userInterface);
|
||||||
newPlayer->moveToThread(thread());
|
newPlayer->moveToThread(thread());
|
||||||
|
|
||||||
Event_Join joinEvent;
|
Event_Join joinEvent;
|
||||||
|
@ -516,8 +562,8 @@ void Server_Game::unattachCards(GameEventStorage &ges, Server_Player *player)
|
||||||
QMapIterator<QString, Server_CardZone *> zoneIterator(player->getZones());
|
QMapIterator<QString, Server_CardZone *> zoneIterator(player->getZones());
|
||||||
while (zoneIterator.hasNext()) {
|
while (zoneIterator.hasNext()) {
|
||||||
Server_CardZone *zone = zoneIterator.next().value();
|
Server_CardZone *zone = zoneIterator.next().value();
|
||||||
for (int i = 0; i < zone->cards.size(); ++i) {
|
for (int i = 0; i < zone->getCards().size(); ++i) {
|
||||||
Server_Card *card = zone->cards.at(i);
|
Server_Card *card = zone->getCards().at(i);
|
||||||
|
|
||||||
// Make a copy of the list because the original one gets modified during the loop
|
// Make a copy of the list because the original one gets modified during the loop
|
||||||
QList<Server_Card *> attachedCards = card->getAttachedCards();
|
QList<Server_Card *> attachedCards = card->getAttachedCards();
|
||||||
|
|
|
@ -73,6 +73,7 @@ private:
|
||||||
|
|
||||||
void createGameStateChangedEvent(Event_GameStateChanged *event, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo);
|
void createGameStateChangedEvent(Event_GameStateChanged *event, Server_Player *playerWhosAsking, bool omniscient, bool withUserInfo);
|
||||||
void sendGameStateToPlayers();
|
void sendGameStateToPlayers();
|
||||||
|
void storeGameInformation();
|
||||||
signals:
|
signals:
|
||||||
void sigStartGameIfReady();
|
void sigStartGameIfReady();
|
||||||
void gameInfoChanged(ServerInfo_Game gameInfo);
|
void gameInfoChanged(ServerInfo_Game gameInfo);
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
Server_Player::Server_Player(Server_Game *_game, int _playerId, const ServerInfo_User &_userInfo, bool _spectator, Server_AbstractUserInterface *_userInterface)
|
Server_Player::Server_Player(Server_Game *_game, int _playerId, const ServerInfo_User &_userInfo, bool _spectator, Server_AbstractUserInterface *_userInterface)
|
||||||
: game(_game), userInterface(_userInterface), userInfo(new ServerInfo_User(_userInfo)), deck(0), pingTime(0), playerId(_playerId), spectator(_spectator), nextCardId(0), readyStart(false), conceded(false), sideboardLocked(true)
|
: ServerInfo_User_Container(_userInfo), game(_game), userInterface(_userInterface), deck(0), pingTime(0), playerId(_playerId), spectator(_spectator), nextCardId(0), readyStart(false), conceded(false), sideboardLocked(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,9 +101,6 @@ void Server_Player::prepareDestroy()
|
||||||
userInterface->playerRemovedFromGame(game);
|
userInterface->playerRemovedFromGame(game);
|
||||||
playerMutex.unlock();
|
playerMutex.unlock();
|
||||||
|
|
||||||
delete userInfo;
|
|
||||||
userInfo = 0;
|
|
||||||
|
|
||||||
clearZones();
|
clearZones();
|
||||||
|
|
||||||
deleteLater();
|
deleteLater();
|
||||||
|
@ -185,7 +182,7 @@ void Server_Player::setupZones()
|
||||||
if (!currentCard)
|
if (!currentCard)
|
||||||
continue;
|
continue;
|
||||||
for (int k = 0; k < currentCard->getNumber(); ++k)
|
for (int k = 0; k < currentCard->getNumber(); ++k)
|
||||||
z->cards.append(new Server_Card(currentCard->getName(), nextCardId++, 0, 0, z));
|
z->insertCard(new Server_Card(currentCard->getName(), nextCardId++, 0, 0, z), -1, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,11 +206,10 @@ void Server_Player::setupZones()
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (int j = 0; j < start->cards.size(); ++j)
|
for (int j = 0; j < start->getCards().size(); ++j)
|
||||||
if (start->cards[j]->getName() == QString::fromStdString(m.card_name())) {
|
if (start->getCards()[j]->getName() == QString::fromStdString(m.card_name())) {
|
||||||
Server_Card *card = start->cards[j];
|
Server_Card *card = start->getCard(j, NULL, true);
|
||||||
start->cards.removeAt(j);
|
target->insertCard(card, -1, 0);
|
||||||
target->cards.append(card);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +241,7 @@ void Server_Player::getProperties(ServerInfo_PlayerProperties &result, bool with
|
||||||
{
|
{
|
||||||
result.set_player_id(playerId);
|
result.set_player_id(playerId);
|
||||||
if (withUserInfo)
|
if (withUserInfo)
|
||||||
result.mutable_user_info()->CopyFrom(*userInfo);
|
copyUserInfo(*(result.mutable_user_info()), true);
|
||||||
result.set_spectator(spectator);
|
result.set_spectator(spectator);
|
||||||
if (!spectator) {
|
if (!spectator) {
|
||||||
result.set_conceded(conceded);
|
result.set_conceded(conceded);
|
||||||
|
@ -286,16 +282,16 @@ Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int numbe
|
||||||
{
|
{
|
||||||
Server_CardZone *deckZone = zones.value("deck");
|
Server_CardZone *deckZone = zones.value("deck");
|
||||||
Server_CardZone *handZone = zones.value("hand");
|
Server_CardZone *handZone = zones.value("hand");
|
||||||
if (deckZone->cards.size() < number)
|
if (deckZone->getCards().size() < number)
|
||||||
number = deckZone->cards.size();
|
number = deckZone->getCards().size();
|
||||||
|
|
||||||
Event_DrawCards eventOthers;
|
Event_DrawCards eventOthers;
|
||||||
eventOthers.set_number(number);
|
eventOthers.set_number(number);
|
||||||
Event_DrawCards eventPrivate(eventOthers);
|
Event_DrawCards eventPrivate(eventOthers);
|
||||||
|
|
||||||
for (int i = 0; i < number; ++i) {
|
for (int i = 0; i < number; ++i) {
|
||||||
Server_Card *card = deckZone->cards.takeFirst();
|
Server_Card *card = deckZone->getCard(0, NULL, true);
|
||||||
handZone->cards.append(card);
|
handZone->insertCard(card, -1, 0);
|
||||||
lastDrawList.append(card->getId());
|
lastDrawList.append(card->getId());
|
||||||
|
|
||||||
ServerInfo_Card *cardInfo = eventPrivate.add_cards();
|
ServerInfo_Card *cardInfo = eventPrivate.add_cards();
|
||||||
|
@ -306,11 +302,11 @@ Response::ResponseCode Server_Player::drawCards(GameEventStorage &ges, int numbe
|
||||||
ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
|
ges.enqueueGameEvent(eventPrivate, playerId, GameEventStorageItem::SendToPrivate, playerId);
|
||||||
ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
|
ges.enqueueGameEvent(eventOthers, playerId, GameEventStorageItem::SendToOthers);
|
||||||
|
|
||||||
if (deckZone->getAlwaysRevealTopCard() && !deckZone->cards.isEmpty()) {
|
if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) {
|
||||||
Event_RevealCards revealEvent;
|
Event_RevealCards revealEvent;
|
||||||
revealEvent.set_zone_name(deckZone->getName().toStdString());
|
revealEvent.set_zone_name(deckZone->getName().toStdString());
|
||||||
revealEvent.set_card_id(0);
|
revealEvent.set_card_id(0);
|
||||||
deckZone->cards.first()->getInfo(revealEvent.add_cards());
|
deckZone->getCards().first()->getInfo(revealEvent.add_cards());
|
||||||
|
|
||||||
ges.enqueueGameEvent(revealEvent, playerId);
|
ges.enqueueGameEvent(revealEvent, playerId);
|
||||||
}
|
}
|
||||||
|
@ -346,7 +342,7 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_Car
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
|
|
||||||
if (!targetzone->hasCoords() && (x == -1))
|
if (!targetzone->hasCoords() && (x == -1))
|
||||||
x = targetzone->cards.size();
|
x = targetzone->getCards().size();
|
||||||
|
|
||||||
QList<QPair<Server_Card *, int> > cardsToMove;
|
QList<QPair<Server_Card *, int> > cardsToMove;
|
||||||
QMap<Server_Card *, const CardToMove *> cardProperties;
|
QMap<Server_Card *, const CardToMove *> cardProperties;
|
||||||
|
@ -357,15 +353,21 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_Car
|
||||||
continue;
|
continue;
|
||||||
cardIdsToMove.insert(_cards[i]->card_id());
|
cardIdsToMove.insert(_cards[i]->card_id());
|
||||||
|
|
||||||
|
// Consistency checks. In case the command contains illegal moves, try to resolve the legal ones still.
|
||||||
int position;
|
int position;
|
||||||
Server_Card *card = startzone->getCard(_cards[i]->card_id(), &position);
|
Server_Card *card = startzone->getCard(_cards[i]->card_id(), &position);
|
||||||
if (!card)
|
if (!card)
|
||||||
return Response::RespNameNotFound;
|
return Response::RespNameNotFound;
|
||||||
|
if (card->getParentCard())
|
||||||
|
continue;
|
||||||
if (!card->getAttachedCards().isEmpty() && !targetzone->isColumnEmpty(x, y))
|
if (!card->getAttachedCards().isEmpty() && !targetzone->isColumnEmpty(x, y))
|
||||||
return Response::RespContextError;
|
continue;
|
||||||
cardsToMove.append(QPair<Server_Card *, int>(card, position));
|
cardsToMove.append(QPair<Server_Card *, int>(card, position));
|
||||||
cardProperties.insert(card, _cards[i]);
|
cardProperties.insert(card, _cards[i]);
|
||||||
}
|
}
|
||||||
|
// In case all moves were filtered out, abort.
|
||||||
|
if (cardsToMove.isEmpty())
|
||||||
|
return Response::RespContextError;
|
||||||
|
|
||||||
MoveCardCompareFunctor cmp(startzone == targetzone ? -1 : x);
|
MoveCardCompareFunctor cmp(startzone == targetzone ? -1 : x);
|
||||||
qSort(cardsToMove.begin(), cardsToMove.end(), cmp);
|
qSort(cardsToMove.begin(), cardsToMove.end(), cmp);
|
||||||
|
@ -525,19 +527,19 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_Car
|
||||||
if (!ptString.isEmpty() && !faceDown)
|
if (!ptString.isEmpty() && !faceDown)
|
||||||
setCardAttrHelper(ges, targetzone->getName(), card->getId(), AttrPT, ptString);
|
setCardAttrHelper(ges, targetzone->getName(), card->getId(), AttrPT, ptString);
|
||||||
}
|
}
|
||||||
if (startzone->getAlwaysRevealTopCard() && !startzone->cards.isEmpty() && (originalPosition == 0)) {
|
if (startzone->getAlwaysRevealTopCard() && !startzone->getCards().isEmpty() && (originalPosition == 0)) {
|
||||||
Event_RevealCards revealEvent;
|
Event_RevealCards revealEvent;
|
||||||
revealEvent.set_zone_name(startzone->getName().toStdString());
|
revealEvent.set_zone_name(startzone->getName().toStdString());
|
||||||
revealEvent.set_card_id(0);
|
revealEvent.set_card_id(0);
|
||||||
startzone->cards.first()->getInfo(revealEvent.add_cards());
|
startzone->getCards().first()->getInfo(revealEvent.add_cards());
|
||||||
|
|
||||||
ges.enqueueGameEvent(revealEvent, playerId);
|
ges.enqueueGameEvent(revealEvent, playerId);
|
||||||
}
|
}
|
||||||
if (targetzone->getAlwaysRevealTopCard() && !targetzone->cards.isEmpty() && (newX == 0)) {
|
if (targetzone->getAlwaysRevealTopCard() && !targetzone->getCards().isEmpty() && (newX == 0)) {
|
||||||
Event_RevealCards revealEvent;
|
Event_RevealCards revealEvent;
|
||||||
revealEvent.set_zone_name(targetzone->getName().toStdString());
|
revealEvent.set_zone_name(targetzone->getName().toStdString());
|
||||||
revealEvent.set_card_id(0);
|
revealEvent.set_card_id(0);
|
||||||
targetzone->cards.first()->getInfo(revealEvent.add_cards());
|
targetzone->getCards().first()->getInfo(revealEvent.add_cards());
|
||||||
|
|
||||||
ges.enqueueGameEvent(revealEvent, playerId);
|
ges.enqueueGameEvent(revealEvent, playerId);
|
||||||
}
|
}
|
||||||
|
@ -556,7 +558,7 @@ Response::ResponseCode Server_Player::moveCard(GameEventStorage &ges, Server_Car
|
||||||
void Server_Player::unattachCard(GameEventStorage &ges, Server_Card *card)
|
void Server_Player::unattachCard(GameEventStorage &ges, Server_Card *card)
|
||||||
{
|
{
|
||||||
Server_CardZone *zone = card->getZone();
|
Server_CardZone *zone = card->getZone();
|
||||||
|
Server_Card *parentCard = card->getParentCard();
|
||||||
card->setParentCard(0);
|
card->setParentCard(0);
|
||||||
|
|
||||||
Event_AttachCard event;
|
Event_AttachCard event;
|
||||||
|
@ -568,6 +570,9 @@ void Server_Player::unattachCard(GameEventStorage &ges, Server_Card *card)
|
||||||
cardToMove->set_card_id(card->getId());
|
cardToMove->set_card_id(card->getId());
|
||||||
moveCard(ges, zone, QList<const CardToMove *>() << cardToMove, zone, -1, card->getY(), card->getFaceDown());
|
moveCard(ges, zone, QList<const CardToMove *>() << cardToMove, zone, -1, card->getY(), card->getFaceDown());
|
||||||
delete cardToMove;
|
delete cardToMove;
|
||||||
|
|
||||||
|
if (parentCard->getZone())
|
||||||
|
parentCard->getZone()->updateCardCoordinates(parentCard, parentCard->getX(), parentCard->getY());
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::ResponseCode Server_Player::setCardAttrHelper(GameEventStorage &ges, const QString &zoneName, int cardId, CardAttribute attribute, const QString &attrValue)
|
Response::ResponseCode Server_Player::setCardAttrHelper(GameEventStorage &ges, const QString &zoneName, int cardId, CardAttribute attribute, const QString &attrValue)
|
||||||
|
@ -580,7 +585,7 @@ Response::ResponseCode Server_Player::setCardAttrHelper(GameEventStorage &ges, c
|
||||||
|
|
||||||
QString result;
|
QString result;
|
||||||
if (cardId == -1) {
|
if (cardId == -1) {
|
||||||
QListIterator<Server_Card *> CardIterator(zone->cards);
|
QListIterator<Server_Card *> CardIterator(zone->getCards());
|
||||||
while (CardIterator.hasNext()) {
|
while (CardIterator.hasNext()) {
|
||||||
result = CardIterator.next()->setAttribute(attribute, attrValue, true);
|
result = CardIterator.next()->setAttribute(attribute, attrValue, true);
|
||||||
if (result.isNull())
|
if (result.isNull())
|
||||||
|
@ -631,7 +636,7 @@ Response::ResponseCode Server_Player::cmdDeckSelect(const Command_DeckSelect &cm
|
||||||
DeckList *newDeck;
|
DeckList *newDeck;
|
||||||
if (cmd.has_deck_id()) {
|
if (cmd.has_deck_id()) {
|
||||||
try {
|
try {
|
||||||
newDeck = game->getRoom()->getServer()->getDatabaseInterface()->getDeckFromDatabase(cmd.deck_id(), QString::fromStdString(userInfo->name()));
|
newDeck = game->getRoom()->getServer()->getDatabaseInterface()->getDeckFromDatabase(cmd.deck_id(), userInfo->id());
|
||||||
} catch(Response::ResponseCode r) {
|
} catch(Response::ResponseCode r) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -782,11 +787,11 @@ Response::ResponseCode Server_Player::cmdShuffle(const Command_Shuffle & /*cmd*/
|
||||||
event.set_zone_name("deck");
|
event.set_zone_name("deck");
|
||||||
ges.enqueueGameEvent(event, playerId);
|
ges.enqueueGameEvent(event, playerId);
|
||||||
|
|
||||||
if (deckZone->getAlwaysRevealTopCard() && !deckZone->cards.isEmpty()) {
|
if (deckZone->getAlwaysRevealTopCard() && !deckZone->getCards().isEmpty()) {
|
||||||
Event_RevealCards revealEvent;
|
Event_RevealCards revealEvent;
|
||||||
revealEvent.set_zone_name(deckZone->getName().toStdString());
|
revealEvent.set_zone_name(deckZone->getName().toStdString());
|
||||||
revealEvent.set_card_id(0);
|
revealEvent.set_card_id(0);
|
||||||
deckZone->cards.first()->getInfo(revealEvent.add_cards());
|
deckZone->getCards().first()->getInfo(revealEvent.add_cards());
|
||||||
|
|
||||||
ges.enqueueGameEvent(revealEvent, playerId);
|
ges.enqueueGameEvent(revealEvent, playerId);
|
||||||
}
|
}
|
||||||
|
@ -805,12 +810,12 @@ Response::ResponseCode Server_Player::cmdMulligan(const Command_Mulligan & /*cmd
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
|
|
||||||
Server_CardZone *hand = zones.value("hand");
|
Server_CardZone *hand = zones.value("hand");
|
||||||
int number = (hand->cards.size() <= 1) ? initialCards : hand->cards.size() - 1;
|
int number = (hand->getCards().size() <= 1) ? initialCards : hand->getCards().size() - 1;
|
||||||
|
|
||||||
Server_CardZone *deck = zones.value("deck");
|
Server_CardZone *deck = zones.value("deck");
|
||||||
while (!hand->cards.isEmpty()) {
|
while (!hand->getCards().isEmpty()) {
|
||||||
CardToMove *cardToMove = new CardToMove;
|
CardToMove *cardToMove = new CardToMove;
|
||||||
cardToMove->set_card_id(hand->cards.first()->getId());
|
cardToMove->set_card_id(hand->getCards().first()->getId());
|
||||||
moveCard(ges, hand, QList<const CardToMove *>() << cardToMove, deck, 0, 0, false);
|
moveCard(ges, hand, QList<const CardToMove *>() << cardToMove, deck, 0, 0, false);
|
||||||
delete cardToMove;
|
delete cardToMove;
|
||||||
}
|
}
|
||||||
|
@ -991,9 +996,11 @@ Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cm
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
if (cmd.has_target_card_id())
|
if (cmd.has_target_card_id())
|
||||||
targetCard = targetzone->getCard(cmd.target_card_id());
|
targetCard = targetzone->getCard(cmd.target_card_id());
|
||||||
if (targetCard)
|
if (targetCard) {
|
||||||
if (targetCard->getParentCard())
|
if (targetCard->getParentCard())
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
|
} else
|
||||||
|
return Response::RespNameNotFound;
|
||||||
}
|
}
|
||||||
if (!startzone->hasCoords())
|
if (!startzone->hasCoords())
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
|
@ -1025,6 +1032,11 @@ Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cm
|
||||||
for (int i = 0; i < attachedList.size(); ++i)
|
for (int i = 0; i < attachedList.size(); ++i)
|
||||||
attachedList[i]->getZone()->getPlayer()->unattachCard(ges, attachedList[i]);
|
attachedList[i]->getZone()->getPlayer()->unattachCard(ges, attachedList[i]);
|
||||||
|
|
||||||
|
card->setParentCard(targetCard);
|
||||||
|
const int oldX = card->getX();
|
||||||
|
card->setCoords(-1, card->getY());
|
||||||
|
startzone->updateCardCoordinates(card, oldX, card->getY());
|
||||||
|
|
||||||
if (targetzone->isColumnStacked(targetCard->getX(), targetCard->getY())) {
|
if (targetzone->isColumnStacked(targetCard->getX(), targetCard->getY())) {
|
||||||
CardToMove *cardToMove = new CardToMove;
|
CardToMove *cardToMove = new CardToMove;
|
||||||
cardToMove->set_card_id(targetCard->getId());
|
cardToMove->set_card_id(targetCard->getId());
|
||||||
|
@ -1032,9 +1044,6 @@ Response::ResponseCode Server_Player::cmdAttachCard(const Command_AttachCard &cm
|
||||||
delete cardToMove;
|
delete cardToMove;
|
||||||
}
|
}
|
||||||
|
|
||||||
card->setParentCard(targetCard);
|
|
||||||
card->setCoords(-1, card->getY());
|
|
||||||
|
|
||||||
Event_AttachCard event;
|
Event_AttachCard event;
|
||||||
event.set_start_zone(startzone->getName().toStdString());
|
event.set_start_zone(startzone->getName().toStdString());
|
||||||
event.set_card_id(card->getId());
|
event.set_card_id(card->getId());
|
||||||
|
@ -1407,16 +1416,17 @@ Response::ResponseCode Server_Player::cmdDumpZone(const Command_DumpZone &cmd, R
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
|
|
||||||
int numberCards = cmd.number_cards();
|
int numberCards = cmd.number_cards();
|
||||||
|
const QList<Server_Card *> &cards = zone->getCards();
|
||||||
|
|
||||||
Response_DumpZone *re = new Response_DumpZone;
|
Response_DumpZone *re = new Response_DumpZone;
|
||||||
ServerInfo_Zone *zoneInfo = re->mutable_zone_info();
|
ServerInfo_Zone *zoneInfo = re->mutable_zone_info();
|
||||||
zoneInfo->set_name(zone->getName().toStdString());
|
zoneInfo->set_name(zone->getName().toStdString());
|
||||||
zoneInfo->set_type(zone->getType());
|
zoneInfo->set_type(zone->getType());
|
||||||
zoneInfo->set_with_coords(zone->hasCoords());
|
zoneInfo->set_with_coords(zone->hasCoords());
|
||||||
zoneInfo->set_card_count(numberCards < zone->cards.size() ? zone->cards.size() : numberCards);
|
zoneInfo->set_card_count(numberCards < cards.size() ? cards.size() : numberCards);
|
||||||
|
|
||||||
for (int i = 0; (i < zone->cards.size()) && (i < numberCards || numberCards == -1); ++i) {
|
for (int i = 0; (i < cards.size()) && (i < numberCards || numberCards == -1); ++i) {
|
||||||
Server_Card *card = zone->cards[i];
|
Server_Card *card = cards[i];
|
||||||
QString displayedName = card->getFaceDown() ? QString() : card->getName();
|
QString displayedName = card->getFaceDown() ? QString() : card->getName();
|
||||||
ServerInfo_Card *cardInfo = zoneInfo->add_card_list();
|
ServerInfo_Card *cardInfo = zoneInfo->add_card_list();
|
||||||
cardInfo->set_name(displayedName.toStdString());
|
cardInfo->set_name(displayedName.toStdString());
|
||||||
|
@ -1510,11 +1520,11 @@ Response::ResponseCode Server_Player::cmdRevealCards(const Command_RevealCards &
|
||||||
|
|
||||||
QList<Server_Card *> cardsToReveal;
|
QList<Server_Card *> cardsToReveal;
|
||||||
if (!cmd.has_card_id())
|
if (!cmd.has_card_id())
|
||||||
cardsToReveal = zone->cards;
|
cardsToReveal = zone->getCards();
|
||||||
else if (cmd.card_id() == -2) {
|
else if (cmd.card_id() == -2) {
|
||||||
if (zone->cards.isEmpty())
|
if (zone->getCards().isEmpty())
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
cardsToReveal.append(zone->cards.at(rng->getNumber(0, zone->cards.size() - 1)));
|
cardsToReveal.append(zone->getCards().at(rng->getNumber(0, zone->getCards().size() - 1)));
|
||||||
} else {
|
} else {
|
||||||
Server_Card *card = zone->getCard(cmd.card_id());
|
Server_Card *card = zone->getCard(cmd.card_id());
|
||||||
if (!card)
|
if (!card)
|
||||||
|
@ -1600,11 +1610,11 @@ Response::ResponseCode Server_Player::cmdChangeZoneProperties(const Command_Chan
|
||||||
|
|
||||||
ges.enqueueGameEvent(event, playerId);
|
ges.enqueueGameEvent(event, playerId);
|
||||||
|
|
||||||
if (!zone->cards.isEmpty() && cmd.always_reveal_top_card()) {
|
if (!zone->getCards().isEmpty() && cmd.always_reveal_top_card()) {
|
||||||
Event_RevealCards revealEvent;
|
Event_RevealCards revealEvent;
|
||||||
revealEvent.set_zone_name(zone->getName().toStdString());
|
revealEvent.set_zone_name(zone->getName().toStdString());
|
||||||
revealEvent.set_card_id(0);
|
revealEvent.set_card_id(0);
|
||||||
zone->cards.first()->getInfo(revealEvent.add_cards());
|
zone->getCards().first()->getInfo(revealEvent.add_cards());
|
||||||
|
|
||||||
ges.enqueueGameEvent(revealEvent, playerId);
|
ges.enqueueGameEvent(revealEvent, playerId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define PLAYER_H
|
#define PLAYER_H
|
||||||
|
|
||||||
#include "server_arrowtarget.h"
|
#include "server_arrowtarget.h"
|
||||||
|
#include "serverinfo_user_container.h"
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
@ -60,13 +61,12 @@ class Command_DeckSelect;
|
||||||
class Command_SetSideboardLock;
|
class Command_SetSideboardLock;
|
||||||
class Command_ChangeZoneProperties;
|
class Command_ChangeZoneProperties;
|
||||||
|
|
||||||
class Server_Player : public Server_ArrowTarget {
|
class Server_Player : public Server_ArrowTarget, public ServerInfo_User_Container {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
private:
|
private:
|
||||||
class MoveCardCompareFunctor;
|
class MoveCardCompareFunctor;
|
||||||
Server_Game *game;
|
Server_Game *game;
|
||||||
Server_AbstractUserInterface *userInterface;
|
Server_AbstractUserInterface *userInterface;
|
||||||
ServerInfo_User *userInfo;
|
|
||||||
DeckList *deck;
|
DeckList *deck;
|
||||||
QMap<QString, Server_CardZone *> zones;
|
QMap<QString, Server_CardZone *> zones;
|
||||||
QMap<int, Server_Counter *> counters;
|
QMap<int, Server_Counter *> counters;
|
||||||
|
@ -96,7 +96,6 @@ public:
|
||||||
bool getSpectator() const { return spectator; }
|
bool getSpectator() const { return spectator; }
|
||||||
bool getConceded() const { return conceded; }
|
bool getConceded() const { return conceded; }
|
||||||
void setConceded(bool _conceded) { conceded = _conceded; }
|
void setConceded(bool _conceded) { conceded = _conceded; }
|
||||||
ServerInfo_User *getUserInfo() const { return userInfo; }
|
|
||||||
DeckList *getDeck() const { return deck; }
|
DeckList *getDeck() const { return deck; }
|
||||||
Server_Game *getGame() const { return game; }
|
Server_Game *getGame() const { return game; }
|
||||||
const QMap<QString, Server_CardZone *> &getZones() const { return zones; }
|
const QMap<QString, Server_CardZone *> &getZones() const { return zones; }
|
||||||
|
|
|
@ -38,6 +38,8 @@ Server_ProtocolHandler::~Server_ProtocolHandler()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function must only be called from the thread this object lives in.
|
||||||
|
// The thread must not hold any server locks when calling this (e.g. clientsLock, roomsLock).
|
||||||
void Server_ProtocolHandler::prepareDestroy()
|
void Server_ProtocolHandler::prepareDestroy()
|
||||||
{
|
{
|
||||||
if (deleted)
|
if (deleted)
|
||||||
|
@ -58,24 +60,24 @@ void Server_ProtocolHandler::prepareDestroy()
|
||||||
Server_Room *r = server->getRooms().value(gameIterator.value().first);
|
Server_Room *r = server->getRooms().value(gameIterator.value().first);
|
||||||
if (!r)
|
if (!r)
|
||||||
continue;
|
continue;
|
||||||
r->gamesMutex.lock();
|
r->gamesLock.lockForRead();
|
||||||
Server_Game *g = r->getGames().value(gameIterator.key());
|
Server_Game *g = r->getGames().value(gameIterator.key());
|
||||||
if (!g) {
|
if (!g) {
|
||||||
r->gamesMutex.unlock();
|
r->gamesLock.unlock();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
g->gameMutex.lock();
|
g->gameMutex.lock();
|
||||||
Server_Player *p = g->getPlayers().value(gameIterator.value().second);
|
Server_Player *p = g->getPlayers().value(gameIterator.value().second);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
g->gameMutex.unlock();
|
g->gameMutex.unlock();
|
||||||
r->gamesMutex.unlock();
|
r->gamesLock.unlock();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
p->disconnectClient();
|
p->disconnectClient();
|
||||||
|
|
||||||
g->gameMutex.unlock();
|
g->gameMutex.unlock();
|
||||||
r->gamesMutex.unlock();
|
r->gamesLock.unlock();
|
||||||
}
|
}
|
||||||
server->roomsLock.unlock();
|
server->roomsLock.unlock();
|
||||||
|
|
||||||
|
@ -195,7 +197,7 @@ Response::ResponseCode Server_ProtocolHandler::processGameCommandContainer(const
|
||||||
if (!room)
|
if (!room)
|
||||||
return Response::RespNotInRoom;
|
return Response::RespNotInRoom;
|
||||||
|
|
||||||
QMutexLocker roomGamesLocker(&room->gamesMutex);
|
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||||
Server_Game *game = room->getGames().value(cont.game_id());
|
Server_Game *game = room->getGames().value(cont.game_id());
|
||||||
if (!game) {
|
if (!game) {
|
||||||
if (room->getExternalGames().contains(cont.game_id())) {
|
if (room->getExternalGames().contains(cont.game_id())) {
|
||||||
|
@ -275,6 +277,10 @@ Response::ResponseCode Server_ProtocolHandler::processAdminCommandContainer(cons
|
||||||
|
|
||||||
void Server_ProtocolHandler::processCommandContainer(const CommandContainer &cont)
|
void Server_ProtocolHandler::processCommandContainer(const CommandContainer &cont)
|
||||||
{
|
{
|
||||||
|
// Command processing must be disabled after prepareDestroy() has been called.
|
||||||
|
if (deleted)
|
||||||
|
return;
|
||||||
|
|
||||||
lastDataReceived = timeRunning;
|
lastDataReceived = timeRunning;
|
||||||
|
|
||||||
ResponseContainer responseContainer(cont.has_cmd_id() ? cont.cmd_id() : -1);
|
ResponseContainer responseContainer(cont.has_cmd_id() ? cont.cmd_id() : -1);
|
||||||
|
@ -374,12 +380,9 @@ Response::ResponseCode Server_ProtocolHandler::cmdMessage(const Command_Message
|
||||||
QReadLocker locker(&server->clientsLock);
|
QReadLocker locker(&server->clientsLock);
|
||||||
|
|
||||||
QString receiver = QString::fromStdString(cmd.user_name());
|
QString receiver = QString::fromStdString(cmd.user_name());
|
||||||
Server_AbstractUserInterface *userInterface = server->getUsers().value(receiver);
|
Server_AbstractUserInterface *userInterface = server->findUser(receiver);
|
||||||
if (!userInterface) {
|
if (!userInterface)
|
||||||
userInterface = server->getExternalUsers().value(receiver);
|
return Response::RespNameNotFound;
|
||||||
if (!userInterface)
|
|
||||||
return Response::RespNameNotFound;
|
|
||||||
}
|
|
||||||
if (databaseInterface->isInIgnoreList(receiver, QString::fromStdString(userInfo->name())))
|
if (databaseInterface->isInIgnoreList(receiver, QString::fromStdString(userInfo->name())))
|
||||||
return Response::RespInIgnoreList;
|
return Response::RespInIgnoreList;
|
||||||
|
|
||||||
|
@ -400,22 +403,20 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetGamesOfUser(const Command_G
|
||||||
if (authState == NotLoggedIn)
|
if (authState == NotLoggedIn)
|
||||||
return Response::RespLoginNeeded;
|
return Response::RespLoginNeeded;
|
||||||
|
|
||||||
server->clientsLock.lockForRead();
|
// We don't need to check whether the user is logged in; persistent games should also work.
|
||||||
if (!server->getUsers().contains(QString::fromStdString(cmd.user_name())))
|
// The client needs to deal with an empty result list.
|
||||||
return Response::RespNameNotFound;
|
|
||||||
server->clientsLock.unlock();
|
|
||||||
|
|
||||||
Response_GetGamesOfUser *re = new Response_GetGamesOfUser;
|
Response_GetGamesOfUser *re = new Response_GetGamesOfUser;
|
||||||
server->roomsLock.lockForRead();
|
server->roomsLock.lockForRead();
|
||||||
QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
|
QMapIterator<int, Server_Room *> roomIterator(server->getRooms());
|
||||||
while (roomIterator.hasNext()) {
|
while (roomIterator.hasNext()) {
|
||||||
Server_Room *room = roomIterator.next().value();
|
Server_Room *room = roomIterator.next().value();
|
||||||
room->gamesMutex.lock();
|
room->gamesLock.lockForRead();
|
||||||
room->getInfo(*re->add_room_list(), false, true);
|
room->getInfo(*re->add_room_list(), false, true);
|
||||||
QListIterator<ServerInfo_Game> gameIterator(room->getGamesOfUser(QString::fromStdString(cmd.user_name())));
|
QListIterator<ServerInfo_Game> gameIterator(room->getGamesOfUser(QString::fromStdString(cmd.user_name())));
|
||||||
while (gameIterator.hasNext())
|
while (gameIterator.hasNext())
|
||||||
re->add_game_list()->CopyFrom(gameIterator.next());
|
re->add_game_list()->CopyFrom(gameIterator.next());
|
||||||
room->gamesMutex.unlock();
|
room->gamesLock.unlock();
|
||||||
}
|
}
|
||||||
server->roomsLock.unlock();
|
server->roomsLock.unlock();
|
||||||
|
|
||||||
|
@ -436,15 +437,11 @@ Response::ResponseCode Server_ProtocolHandler::cmdGetUserInfo(const Command_GetU
|
||||||
|
|
||||||
QReadLocker locker(&server->clientsLock);
|
QReadLocker locker(&server->clientsLock);
|
||||||
|
|
||||||
ServerInfo_User_Container *infoSource;
|
ServerInfo_User_Container *infoSource = server->findUser(userName);
|
||||||
if (server->getUsers().contains(userName))
|
if (!infoSource)
|
||||||
infoSource = server->getUsers().value(userName);
|
|
||||||
else if (server->getExternalUsers().contains(userName))
|
|
||||||
infoSource = server->getExternalUsers().value(userName);
|
|
||||||
else
|
|
||||||
return Response::RespNameNotFound;
|
return Response::RespNameNotFound;
|
||||||
|
|
||||||
re->mutable_user_info()->CopyFrom(infoSource->copyUserInfo(true, userInfo->user_level() & ServerInfo_User::IsModerator));
|
re->mutable_user_info()->CopyFrom(infoSource->copyUserInfo(true, false, userInfo->user_level() & ServerInfo_User::IsModerator));
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.setResponseExtension(re);
|
rc.setResponseExtension(re);
|
||||||
|
@ -556,8 +553,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdCreateGame(const Command_Creat
|
||||||
if (gameId == -1)
|
if (gameId == -1)
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
|
|
||||||
QMutexLocker roomLocker(&room->gamesMutex);
|
|
||||||
|
|
||||||
if (server->getMaxGamesPerUser() > 0)
|
if (server->getMaxGamesPerUser() > 0)
|
||||||
if (room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= server->getMaxGamesPerUser())
|
if (room->getGamesCreatedByUser(QString::fromStdString(userInfo->name())) >= server->getMaxGamesPerUser())
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <google/protobuf/descriptor.h>
|
#include <google/protobuf/descriptor.h>
|
||||||
|
|
||||||
Server_Room::Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent)
|
Server_Room::Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent)
|
||||||
: QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), gamesMutex(QMutex::Recursive)
|
: QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes), gamesLock(QReadWriteLock::Recursive)
|
||||||
{
|
{
|
||||||
connect(this, SIGNAL(gameListChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)), Qt::QueuedConnection);
|
connect(this, SIGNAL(gameListChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)), Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
@ -22,15 +22,15 @@ Server_Room::~Server_Room()
|
||||||
{
|
{
|
||||||
qDebug("Server_Room destructor");
|
qDebug("Server_Room destructor");
|
||||||
|
|
||||||
gamesMutex.lock();
|
gamesLock.lockForWrite();
|
||||||
const QList<Server_Game *> gameList = games.values();
|
const QList<Server_Game *> gameList = games.values();
|
||||||
for (int i = 0; i < gameList.size(); ++i)
|
for (int i = 0; i < gameList.size(); ++i)
|
||||||
delete gameList[i];
|
delete gameList[i];
|
||||||
games.clear();
|
games.clear();
|
||||||
gamesMutex.unlock();
|
gamesLock.unlock();
|
||||||
|
|
||||||
usersLock.lockForWrite();
|
usersLock.lockForWrite();
|
||||||
userList.clear();
|
users.clear();
|
||||||
usersLock.unlock();
|
usersLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,17 +39,15 @@ Server *Server_Room::getServer() const
|
||||||
return static_cast<Server *>(parent());
|
return static_cast<Server *>(parent());
|
||||||
}
|
}
|
||||||
|
|
||||||
const ServerInfo_Room &Server_Room::getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes, bool updating, bool includeExternalData) const
|
const ServerInfo_Room &Server_Room::getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes, bool includeExternalData) const
|
||||||
{
|
{
|
||||||
result.set_room_id(id);
|
result.set_room_id(id);
|
||||||
|
|
||||||
if (!updating) {
|
result.set_name(name.toStdString());
|
||||||
result.set_name(name.toStdString());
|
result.set_description(description.toStdString());
|
||||||
result.set_description(description.toStdString());
|
result.set_auto_join(autoJoin);
|
||||||
result.set_auto_join(autoJoin);
|
|
||||||
}
|
|
||||||
|
|
||||||
gamesMutex.lock();
|
gamesLock.lockForRead();
|
||||||
result.set_game_count(games.size() + externalGames.size());
|
result.set_game_count(games.size() + externalGames.size());
|
||||||
if (complete) {
|
if (complete) {
|
||||||
QMapIterator<int, Server_Game *> gameIterator(games);
|
QMapIterator<int, Server_Game *> gameIterator(games);
|
||||||
|
@ -61,13 +59,14 @@ const ServerInfo_Room &Server_Room::getInfo(ServerInfo_Room &result, bool comple
|
||||||
result.add_game_list()->CopyFrom(externalGameIterator.next().value());
|
result.add_game_list()->CopyFrom(externalGameIterator.next().value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gamesMutex.unlock();
|
gamesLock.unlock();
|
||||||
|
|
||||||
usersLock.lockForRead();
|
usersLock.lockForRead();
|
||||||
result.set_player_count(userList.size() + externalUsers.size());
|
result.set_player_count(users.size() + externalUsers.size());
|
||||||
if (complete) {
|
if (complete) {
|
||||||
for (int i = 0; i < userList.size(); ++i)
|
QMapIterator<QString, Server_ProtocolHandler *> userIterator(users);
|
||||||
result.add_user_list()->CopyFrom(userList[i]->copyUserInfo(false));
|
while (userIterator.hasNext())
|
||||||
|
result.add_user_list()->CopyFrom(userIterator.next().value()->copyUserInfo(false));
|
||||||
if (includeExternalData) {
|
if (includeExternalData) {
|
||||||
QMapIterator<QString, ServerInfo_User_Container> externalUserIterator(externalUsers);
|
QMapIterator<QString, ServerInfo_User_Container> externalUserIterator(externalUsers);
|
||||||
while (externalUserIterator.hasNext())
|
while (externalUserIterator.hasNext())
|
||||||
|
@ -100,26 +99,44 @@ void Server_Room::addClient(Server_ProtocolHandler *client)
|
||||||
event.mutable_user_info()->CopyFrom(client->copyUserInfo(false));
|
event.mutable_user_info()->CopyFrom(client->copyUserInfo(false));
|
||||||
sendRoomEvent(prepareRoomEvent(event));
|
sendRoomEvent(prepareRoomEvent(event));
|
||||||
|
|
||||||
|
ServerInfo_Room roomInfo;
|
||||||
|
roomInfo.set_room_id(id);
|
||||||
|
|
||||||
usersLock.lockForWrite();
|
usersLock.lockForWrite();
|
||||||
userList.append(client);
|
users.insert(QString::fromStdString(client->getUserInfo()->name()), client);
|
||||||
|
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||||
usersLock.unlock();
|
usersLock.unlock();
|
||||||
|
|
||||||
ServerInfo_Room roomInfo;
|
// XXX This can be removed during the next client update.
|
||||||
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
|
gamesLock.lockForRead();
|
||||||
|
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||||
|
gamesLock.unlock();
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
emit roomInfoChanged(roomInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server_Room::removeClient(Server_ProtocolHandler *client)
|
void Server_Room::removeClient(Server_ProtocolHandler *client)
|
||||||
{
|
{
|
||||||
usersLock.lockForWrite();
|
usersLock.lockForWrite();
|
||||||
userList.removeAt(userList.indexOf(client));
|
users.remove(QString::fromStdString(client->getUserInfo()->name()));
|
||||||
|
|
||||||
|
ServerInfo_Room roomInfo;
|
||||||
|
roomInfo.set_room_id(id);
|
||||||
|
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||||
usersLock.unlock();
|
usersLock.unlock();
|
||||||
|
|
||||||
Event_LeaveRoom event;
|
Event_LeaveRoom event;
|
||||||
event.set_name(client->getUserInfo()->name());
|
event.set_name(client->getUserInfo()->name());
|
||||||
sendRoomEvent(prepareRoomEvent(event));
|
sendRoomEvent(prepareRoomEvent(event));
|
||||||
|
|
||||||
ServerInfo_Room roomInfo;
|
// XXX This can be removed during the next client update.
|
||||||
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
|
gamesLock.lockForRead();
|
||||||
|
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||||
|
gamesLock.unlock();
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
emit roomInfoChanged(roomInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server_Room::addExternalUser(const ServerInfo_User &userInfo)
|
void Server_Room::addExternalUser(const ServerInfo_User &userInfo)
|
||||||
|
@ -130,43 +147,52 @@ void Server_Room::addExternalUser(const ServerInfo_User &userInfo)
|
||||||
event.mutable_user_info()->CopyFrom(userInfoContainer.copyUserInfo(false));
|
event.mutable_user_info()->CopyFrom(userInfoContainer.copyUserInfo(false));
|
||||||
sendRoomEvent(prepareRoomEvent(event), false);
|
sendRoomEvent(prepareRoomEvent(event), false);
|
||||||
|
|
||||||
|
ServerInfo_Room roomInfo;
|
||||||
|
roomInfo.set_room_id(id);
|
||||||
|
|
||||||
usersLock.lockForWrite();
|
usersLock.lockForWrite();
|
||||||
externalUsers.insert(QString::fromStdString(userInfo.name()), userInfoContainer);
|
externalUsers.insert(QString::fromStdString(userInfo.name()), userInfoContainer);
|
||||||
|
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||||
usersLock.unlock();
|
usersLock.unlock();
|
||||||
|
|
||||||
ServerInfo_Room roomInfo;
|
emit roomInfoChanged(roomInfo);
|
||||||
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server_Room::removeExternalUser(const QString &name)
|
void Server_Room::removeExternalUser(const QString &name)
|
||||||
{
|
{
|
||||||
// This function is always called from the Server thread with server->roomsMutex locked.
|
// This function is always called from the Server thread with server->roomsMutex locked.
|
||||||
|
ServerInfo_Room roomInfo;
|
||||||
|
roomInfo.set_room_id(id);
|
||||||
|
|
||||||
usersLock.lockForWrite();
|
usersLock.lockForWrite();
|
||||||
if (externalUsers.contains(name))
|
if (externalUsers.contains(name))
|
||||||
externalUsers.remove(name);
|
externalUsers.remove(name);
|
||||||
|
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||||
usersLock.unlock();
|
usersLock.unlock();
|
||||||
|
|
||||||
Event_LeaveRoom event;
|
Event_LeaveRoom event;
|
||||||
event.set_name(name.toStdString());
|
event.set_name(name.toStdString());
|
||||||
sendRoomEvent(prepareRoomEvent(event), false);
|
sendRoomEvent(prepareRoomEvent(event), false);
|
||||||
|
|
||||||
ServerInfo_Room roomInfo;
|
emit roomInfoChanged(roomInfo);
|
||||||
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server_Room::updateExternalGameList(const ServerInfo_Game &gameInfo)
|
void Server_Room::updateExternalGameList(const ServerInfo_Game &gameInfo)
|
||||||
{
|
{
|
||||||
// This function is always called from the Server thread with server->roomsMutex locked.
|
// This function is always called from the Server thread with server->roomsMutex locked.
|
||||||
gamesMutex.lock();
|
ServerInfo_Room roomInfo;
|
||||||
|
roomInfo.set_room_id(id);
|
||||||
|
|
||||||
|
gamesLock.lockForWrite();
|
||||||
if (!gameInfo.has_player_count() && externalGames.contains(gameInfo.game_id()))
|
if (!gameInfo.has_player_count() && externalGames.contains(gameInfo.game_id()))
|
||||||
externalGames.remove(gameInfo.game_id());
|
externalGames.remove(gameInfo.game_id());
|
||||||
else
|
else
|
||||||
externalGames.insert(gameInfo.game_id(), gameInfo);
|
externalGames.insert(gameInfo.game_id(), gameInfo);
|
||||||
gamesMutex.unlock();
|
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||||
|
gamesLock.unlock();
|
||||||
|
|
||||||
broadcastGameListUpdate(gameInfo, false);
|
broadcastGameListUpdate(gameInfo, false);
|
||||||
ServerInfo_Room roomInfo;
|
emit roomInfoChanged(roomInfo);
|
||||||
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGame &cmd, ResponseContainer &rc, Server_AbstractUserInterface *userInterface)
|
Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGame &cmd, ResponseContainer &rc, Server_AbstractUserInterface *userInterface)
|
||||||
|
@ -174,7 +200,7 @@ Response::ResponseCode Server_Room::processJoinGameCommand(const Command_JoinGam
|
||||||
// This function is called from the Server thread and from the S_PH thread.
|
// This function is called from the Server thread and from the S_PH thread.
|
||||||
// server->roomsMutex is always locked.
|
// server->roomsMutex is always locked.
|
||||||
|
|
||||||
QMutexLocker roomGamesLocker(&gamesMutex);
|
QReadLocker roomGamesLocker(&gamesLock);
|
||||||
Server_Game *g = games.value(cmd.game_id());
|
Server_Game *g = games.value(cmd.game_id());
|
||||||
if (!g) {
|
if (!g) {
|
||||||
if (externalGames.contains(cmd.game_id())) {
|
if (externalGames.contains(cmd.game_id())) {
|
||||||
|
@ -210,8 +236,11 @@ void Server_Room::say(const QString &userName, const QString &s, bool sendToIsl)
|
||||||
void Server_Room::sendRoomEvent(RoomEvent *event, bool sendToIsl)
|
void Server_Room::sendRoomEvent(RoomEvent *event, bool sendToIsl)
|
||||||
{
|
{
|
||||||
usersLock.lockForRead();
|
usersLock.lockForRead();
|
||||||
for (int i = 0; i < userList.size(); ++i)
|
{
|
||||||
userList[i]->sendProtocolItem(*event);
|
QMapIterator<QString, Server_ProtocolHandler *> userIterator(users);
|
||||||
|
while (userIterator.hasNext())
|
||||||
|
userIterator.next().value()->sendProtocolItem(*event);
|
||||||
|
}
|
||||||
usersLock.unlock();
|
usersLock.unlock();
|
||||||
|
|
||||||
if (sendToIsl)
|
if (sendToIsl)
|
||||||
|
@ -229,24 +258,33 @@ void Server_Room::broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool
|
||||||
|
|
||||||
void Server_Room::addGame(Server_Game *game)
|
void Server_Room::addGame(Server_Game *game)
|
||||||
{
|
{
|
||||||
// Lock gamesMutex before calling this
|
ServerInfo_Room roomInfo;
|
||||||
|
roomInfo.set_room_id(id);
|
||||||
|
|
||||||
|
gamesLock.lockForWrite();
|
||||||
connect(game, SIGNAL(gameInfoChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)));
|
connect(game, SIGNAL(gameInfoChanged(ServerInfo_Game)), this, SLOT(broadcastGameListUpdate(ServerInfo_Game)));
|
||||||
|
|
||||||
game->gameMutex.lock();
|
game->gameMutex.lock();
|
||||||
games.insert(game->getGameId(), game);
|
games.insert(game->getGameId(), game);
|
||||||
ServerInfo_Game gameInfo;
|
ServerInfo_Game gameInfo;
|
||||||
game->getInfo(gameInfo);
|
game->getInfo(gameInfo);
|
||||||
|
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||||
game->gameMutex.unlock();
|
game->gameMutex.unlock();
|
||||||
|
gamesLock.unlock();
|
||||||
|
|
||||||
|
// XXX This can be removed during the next client update.
|
||||||
|
usersLock.lockForRead();
|
||||||
|
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||||
|
usersLock.unlock();
|
||||||
|
// -----------
|
||||||
|
|
||||||
emit gameListChanged(gameInfo);
|
emit gameListChanged(gameInfo);
|
||||||
ServerInfo_Room roomInfo;
|
emit roomInfoChanged(roomInfo);
|
||||||
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server_Room::removeGame(Server_Game *game)
|
void Server_Room::removeGame(Server_Game *game)
|
||||||
{
|
{
|
||||||
// No need to lock gamesMutex or gameMutex. This method is only
|
// No need to lock gamesLock or gameMutex. This method is only
|
||||||
// called from ~Server_Game, which locks both mutexes anyway beforehand.
|
// called from ~Server_Game, which locks both mutexes anyway beforehand.
|
||||||
|
|
||||||
disconnect(game, 0, this, 0);
|
disconnect(game, 0, this, 0);
|
||||||
|
@ -258,12 +296,21 @@ void Server_Room::removeGame(Server_Game *game)
|
||||||
games.remove(game->getGameId());
|
games.remove(game->getGameId());
|
||||||
|
|
||||||
ServerInfo_Room roomInfo;
|
ServerInfo_Room roomInfo;
|
||||||
emit roomInfoChanged(getInfo(roomInfo, false, false, true));
|
roomInfo.set_room_id(id);
|
||||||
|
roomInfo.set_game_count(games.size() + externalGames.size());
|
||||||
|
|
||||||
|
// XXX This can be removed during the next client update.
|
||||||
|
usersLock.lockForRead();
|
||||||
|
roomInfo.set_player_count(users.size() + externalUsers.size());
|
||||||
|
usersLock.unlock();
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
emit roomInfoChanged(roomInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Server_Room::getGamesCreatedByUser(const QString &userName) const
|
int Server_Room::getGamesCreatedByUser(const QString &userName) const
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&gamesMutex);
|
QReadLocker locker(&gamesLock);
|
||||||
|
|
||||||
QMapIterator<int, Server_Game *> gamesIterator(games);
|
QMapIterator<int, Server_Game *> gamesIterator(games);
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
@ -275,7 +322,7 @@ int Server_Room::getGamesCreatedByUser(const QString &userName) const
|
||||||
|
|
||||||
QList<ServerInfo_Game> Server_Room::getGamesOfUser(const QString &userName) const
|
QList<ServerInfo_Game> Server_Room::getGamesOfUser(const QString &userName) const
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&gamesMutex);
|
QReadLocker locker(&gamesLock);
|
||||||
|
|
||||||
QList<ServerInfo_Game> result;
|
QList<ServerInfo_Game> result;
|
||||||
QMapIterator<int, Server_Game *> gamesIterator(games);
|
QMapIterator<int, Server_Game *> gamesIterator(games);
|
||||||
|
|
|
@ -37,13 +37,13 @@ private:
|
||||||
QStringList gameTypes;
|
QStringList gameTypes;
|
||||||
QMap<int, Server_Game *> games;
|
QMap<int, Server_Game *> games;
|
||||||
QMap<int, ServerInfo_Game> externalGames;
|
QMap<int, ServerInfo_Game> externalGames;
|
||||||
QList<Server_ProtocolHandler *> userList;
|
QMap<QString, Server_ProtocolHandler *> users;
|
||||||
QMap<QString, ServerInfo_User_Container> externalUsers;
|
QMap<QString, ServerInfo_User_Container> externalUsers;
|
||||||
private slots:
|
private slots:
|
||||||
void broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl = true);
|
void broadcastGameListUpdate(const ServerInfo_Game &gameInfo, bool sendToIsl = true);
|
||||||
public:
|
public:
|
||||||
mutable QReadWriteLock usersLock;
|
mutable QReadWriteLock usersLock;
|
||||||
mutable QMutex gamesMutex;
|
mutable QReadWriteLock gamesLock;
|
||||||
Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent);
|
Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent);
|
||||||
~Server_Room();
|
~Server_Room();
|
||||||
int getId() const { return id; }
|
int getId() const { return id; }
|
||||||
|
@ -55,7 +55,7 @@ public:
|
||||||
const QMap<int, Server_Game *> &getGames() const { return games; }
|
const QMap<int, Server_Game *> &getGames() const { return games; }
|
||||||
const QMap<int, ServerInfo_Game> &getExternalGames() const { return externalGames; }
|
const QMap<int, ServerInfo_Game> &getExternalGames() const { return externalGames; }
|
||||||
Server *getServer() const;
|
Server *getServer() const;
|
||||||
const ServerInfo_Room &getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes = false, bool updating = false, bool includeExternalData = true) const;
|
const ServerInfo_Room &getInfo(ServerInfo_Room &result, bool complete, bool showGameTypes = false, bool includeExternalData = true) const;
|
||||||
int getGamesCreatedByUser(const QString &name) const;
|
int getGamesCreatedByUser(const QString &name) const;
|
||||||
QList<ServerInfo_Game> getGamesOfUser(const QString &name) const;
|
QList<ServerInfo_Game> getGamesOfUser(const QString &name) const;
|
||||||
|
|
||||||
|
|
|
@ -26,22 +26,27 @@ ServerInfo_User_Container::~ServerInfo_User_Container()
|
||||||
|
|
||||||
void ServerInfo_User_Container::setUserInfo(const ServerInfo_User &_userInfo)
|
void ServerInfo_User_Container::setUserInfo(const ServerInfo_User &_userInfo)
|
||||||
{
|
{
|
||||||
userInfo = new ServerInfo_User;
|
userInfo = new ServerInfo_User(_userInfo);
|
||||||
userInfo->CopyFrom(_userInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerInfo_User ServerInfo_User_Container::copyUserInfo(bool complete, bool moderatorInfo) const
|
ServerInfo_User &ServerInfo_User_Container::copyUserInfo(ServerInfo_User &result, bool complete, bool internalInfo, bool sessionInfo) const
|
||||||
{
|
{
|
||||||
ServerInfo_User result;
|
|
||||||
if (userInfo) {
|
if (userInfo) {
|
||||||
result.CopyFrom(*userInfo);
|
result.CopyFrom(*userInfo);
|
||||||
if (!moderatorInfo) {
|
if (!sessionInfo) {
|
||||||
result.clear_session_id();
|
result.clear_session_id();
|
||||||
result.clear_address();
|
result.clear_address();
|
||||||
result.clear_id();
|
|
||||||
}
|
}
|
||||||
|
if (!internalInfo)
|
||||||
|
result.clear_id();
|
||||||
if (!complete)
|
if (!complete)
|
||||||
result.clear_avatar_bmp();
|
result.clear_avatar_bmp();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServerInfo_User ServerInfo_User_Container::copyUserInfo(bool complete, bool internalInfo, bool sessionInfo) const
|
||||||
|
{
|
||||||
|
ServerInfo_User result;
|
||||||
|
return copyUserInfo(result, complete, internalInfo, sessionInfo);
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ public:
|
||||||
virtual ~ServerInfo_User_Container();
|
virtual ~ServerInfo_User_Container();
|
||||||
ServerInfo_User *getUserInfo() const { return userInfo; }
|
ServerInfo_User *getUserInfo() const { return userInfo; }
|
||||||
void setUserInfo(const ServerInfo_User &_userInfo);
|
void setUserInfo(const ServerInfo_User &_userInfo);
|
||||||
ServerInfo_User copyUserInfo(bool complete, bool moderatorInfo = false) const;
|
ServerInfo_User ©UserInfo(ServerInfo_User &result, bool complete, bool internalInfo = false, bool sessionInfo = false) const;
|
||||||
|
ServerInfo_User copyUserInfo(bool complete, bool internalInfo = false, bool sessionInfo = false) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||||
PROJECT(oracle)
|
PROJECT(oracle)
|
||||||
|
|
||||||
|
# paths
|
||||||
|
set(DESKTOPDIR share/applications CACHE STRING "path to .desktop files")
|
||||||
|
|
||||||
SET(oracle_SOURCES src/main.cpp src/oracleimporter.cpp src/window_main.cpp ../cockatrice/src/carddatabase.cpp ../cockatrice/src/settingscache.cpp)
|
SET(oracle_SOURCES src/main.cpp src/oracleimporter.cpp src/window_main.cpp ../cockatrice/src/carddatabase.cpp ../cockatrice/src/settingscache.cpp)
|
||||||
SET(oracle_HEADERS src/oracleimporter.h src/window_main.h ../cockatrice/src/carddatabase.h ../cockatrice/src/settingscache.h)
|
SET(oracle_HEADERS src/oracleimporter.h src/window_main.h ../cockatrice/src/carddatabase.h ../cockatrice/src/settingscache.h)
|
||||||
|
|
||||||
|
@ -19,7 +22,11 @@ INCLUDE_DIRECTORIES(../cockatrice/src)
|
||||||
ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_HEADERS_MOC})
|
ADD_EXECUTABLE(oracle WIN32 MACOSX_BUNDLE ${oracle_SOURCES} ${oracle_HEADERS_MOC})
|
||||||
TARGET_LINK_LIBRARIES(oracle ${QT_LIBRARIES})
|
TARGET_LINK_LIBRARIES(oracle ${QT_LIBRARIES})
|
||||||
|
|
||||||
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/oracle DESTINATION bin)
|
IF (NOT APPLE)
|
||||||
|
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/oracle DESTINATION bin)
|
||||||
|
ELSE (APPLE)
|
||||||
|
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/oracle.app DESTINATION bin)
|
||||||
|
ENDIF (NOT APPLE)
|
||||||
IF (NOT WIN32 AND NOT APPLE)
|
IF (NOT WIN32 AND NOT APPLE)
|
||||||
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/oracle.desktop DESTINATION share/applications)
|
INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/oracle.desktop DESTINATION ${DESKTOPDIR})
|
||||||
ENDIF (NOT WIN32 AND NOT APPLE)
|
ENDIF (NOT WIN32 AND NOT APPLE)
|
||||||
|
|
|
@ -2,6 +2,9 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||||
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
PROJECT(servatrice)
|
PROJECT(servatrice)
|
||||||
|
|
||||||
|
# cmake modules
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
SET(servatrice_SOURCES
|
SET(servatrice_SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/passwordhasher.cpp
|
src/passwordhasher.cpp
|
||||||
|
@ -28,6 +31,7 @@ SET(QT_USE_QTSQL TRUE)
|
||||||
FIND_PACKAGE(Qt4 REQUIRED)
|
FIND_PACKAGE(Qt4 REQUIRED)
|
||||||
FIND_PACKAGE(Protobuf REQUIRED)
|
FIND_PACKAGE(Protobuf REQUIRED)
|
||||||
FIND_PACKAGE(Libgcrypt REQUIRED)
|
FIND_PACKAGE(Libgcrypt REQUIRED)
|
||||||
|
FIND_PACKAGE(Threads)
|
||||||
|
|
||||||
#set(CMAKE_BUILD_TYPE Release)
|
#set(CMAKE_BUILD_TYPE Release)
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O2")
|
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb -O2")
|
||||||
|
@ -43,7 +47,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}/../common)
|
||||||
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
|
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
ADD_EXECUTABLE(servatrice ${servatrice_SOURCES} ${servatrice_HEADERS_MOC})
|
ADD_EXECUTABLE(servatrice ${servatrice_SOURCES} ${servatrice_HEADERS_MOC})
|
||||||
TARGET_LINK_LIBRARIES(servatrice cockatrice_common ${QT_LIBRARIES} ${LIBGCRYPT_LIBRARY})
|
TARGET_LINK_LIBRARIES(servatrice cockatrice_common ${QT_LIBRARIES} ${LIBGCRYPT_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||||
|
|
||||||
#add_custom_target(versionheader ALL DEPENDS version_header)
|
#add_custom_target(versionheader ALL DEPENDS version_header)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
@ -51,3 +55,5 @@ add_custom_command(
|
||||||
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/../common/getversion.cmake
|
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/../common/getversion.cmake
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# install rules
|
||||||
|
INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/servatrice DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
|
@ -28,12 +28,12 @@ SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
|
||||||
CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` (
|
CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` (
|
||||||
`id` int(7) unsigned zerofill NOT NULL auto_increment,
|
`id` int(7) unsigned zerofill NOT NULL auto_increment,
|
||||||
`id_folder` int(7) unsigned zerofill NOT NULL,
|
`id_folder` int(7) unsigned zerofill NOT NULL,
|
||||||
`user` varchar(35) NOT NULL,
|
`id_user` int(7) unsigned NULL,
|
||||||
`name` varchar(50) NOT NULL,
|
`name` varchar(50) NOT NULL,
|
||||||
`upload_time` datetime NOT NULL,
|
`upload_time` datetime NOT NULL,
|
||||||
`content` text NOT NULL,
|
`content` text NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `FolderPlusUser` (`id_folder`,`user`)
|
KEY `FolderPlusUser` (`id_folder`,`id_user`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
@ -45,10 +45,10 @@ CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` (
|
||||||
CREATE TABLE IF NOT EXISTS `cockatrice_decklist_folders` (
|
CREATE TABLE IF NOT EXISTS `cockatrice_decklist_folders` (
|
||||||
`id` int(7) unsigned zerofill NOT NULL auto_increment,
|
`id` int(7) unsigned zerofill NOT NULL auto_increment,
|
||||||
`id_parent` int(7) unsigned zerofill NOT NULL,
|
`id_parent` int(7) unsigned zerofill NOT NULL,
|
||||||
`user` varchar(35) NOT NULL,
|
`id_user` int(7) unsigned NULL,
|
||||||
`name` varchar(30) NOT NULL,
|
`name` varchar(30) NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `ParentPlusUser` (`id_parent`,`user`)
|
KEY `ParentPlusUser` (`id_parent`,`id_user`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
@ -115,9 +115,11 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` (
|
||||||
`avatar_bmp` blob NOT NULL,
|
`avatar_bmp` blob NOT NULL,
|
||||||
`registrationDate` datetime NOT NULL,
|
`registrationDate` datetime NOT NULL,
|
||||||
`active` tinyint(1) NOT NULL,
|
`active` tinyint(1) NOT NULL,
|
||||||
`token` char(32) NOT NULL,
|
`token` binary(16) NOT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `name` (`name`)
|
UNIQUE KEY `name` (`name`),
|
||||||
|
KEY `token` (`token`),
|
||||||
|
KEY `email` (`email`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
CREATE TABLE `cockatrice_uptime` (
|
CREATE TABLE `cockatrice_uptime` (
|
||||||
|
@ -163,7 +165,8 @@ CREATE TABLE `cockatrice_bans` (
|
||||||
`reason` text NOT NULL,
|
`reason` text NOT NULL,
|
||||||
`visible_reason` text NOT NULL,
|
`visible_reason` text NOT NULL,
|
||||||
PRIMARY KEY (`user_name`,`time_from`),
|
PRIMARY KEY (`user_name`,`time_from`),
|
||||||
KEY `time_from` (`time_from`,`ip_address`)
|
KEY `time_from` (`time_from`,`ip_address`),
|
||||||
|
KEY `ip_address` (`ip_address`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
CREATE TABLE `cockatrice_sessions` (
|
CREATE TABLE `cockatrice_sessions` (
|
||||||
|
@ -201,6 +204,7 @@ CREATE TABLE `cockatrice_replays_access` (
|
||||||
`id_player` int(7) NOT NULL,
|
`id_player` int(7) NOT NULL,
|
||||||
`replay_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
|
`replay_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
|
||||||
`do_not_hide` tinyint(1) NOT NULL,
|
`do_not_hide` tinyint(1) NOT NULL,
|
||||||
KEY `id_player` (`id_player`)
|
KEY `id_player` (`id_player`),
|
||||||
|
KEY `id_game` (`id_game`)
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
|
|
@ -128,8 +128,8 @@ void IslInterface::initServer()
|
||||||
while (roomIterator.hasNext()) {
|
while (roomIterator.hasNext()) {
|
||||||
Server_Room *room = roomIterator.next().value();
|
Server_Room *room = roomIterator.next().value();
|
||||||
room->usersLock.lockForRead();
|
room->usersLock.lockForRead();
|
||||||
room->gamesMutex.lock();
|
room->gamesLock.lockForRead();
|
||||||
room->getInfo(*event.add_room_list(), true, true, false, false);
|
room->getInfo(*event.add_room_list(), true, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
IslMessage message;
|
IslMessage message;
|
||||||
|
@ -150,7 +150,7 @@ void IslInterface::initServer()
|
||||||
roomIterator.toFront();
|
roomIterator.toFront();
|
||||||
while (roomIterator.hasNext()) {
|
while (roomIterator.hasNext()) {
|
||||||
roomIterator.next();
|
roomIterator.next();
|
||||||
roomIterator.value()->gamesMutex.unlock();
|
roomIterator.value()->gamesLock.unlock();
|
||||||
roomIterator.value()->usersLock.unlock();
|
roomIterator.value()->usersLock.unlock();
|
||||||
}
|
}
|
||||||
server->roomsLock.unlock();
|
server->roomsLock.unlock();
|
||||||
|
|
|
@ -127,6 +127,7 @@ int main(int argc, char *argv[])
|
||||||
QSettings *settings = new QSettings("servatrice.ini", QSettings::IniFormat);
|
QSettings *settings = new QSettings("servatrice.ini", QSettings::IniFormat);
|
||||||
|
|
||||||
loggerThread = new QThread;
|
loggerThread = new QThread;
|
||||||
|
loggerThread->setObjectName("logger");
|
||||||
logger = new ServerLogger(logToConsole);
|
logger = new ServerLogger(logToConsole);
|
||||||
logger->moveToThread(loggerThread);
|
logger->moveToThread(loggerThread);
|
||||||
|
|
||||||
|
@ -151,12 +152,16 @@ int main(int argc, char *argv[])
|
||||||
sigemptyset(&segv.sa_mask);
|
sigemptyset(&segv.sa_mask);
|
||||||
sigaction(SIGSEGV, &segv, 0);
|
sigaction(SIGSEGV, &segv, 0);
|
||||||
sigaction(SIGABRT, &segv, 0);
|
sigaction(SIGABRT, &segv, 0);
|
||||||
|
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
#endif
|
#endif
|
||||||
rng = new RNG_SFMT;
|
rng = new RNG_SFMT;
|
||||||
|
|
||||||
std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl;
|
std::cerr << "Servatrice " << VERSION_STRING << " starting." << std::endl;
|
||||||
std::cerr << "-------------------------" << std::endl;
|
std::cerr << "-------------------------" << std::endl;
|
||||||
|
|
||||||
|
PasswordHasher::initialize();
|
||||||
|
|
||||||
if (testRandom)
|
if (testRandom)
|
||||||
testRNG();
|
testRNG();
|
||||||
if (testHashFunction)
|
if (testHashFunction)
|
||||||
|
|
|
@ -3,6 +3,14 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <gcrypt.h>
|
#include <gcrypt.h>
|
||||||
|
|
||||||
|
void PasswordHasher::initialize()
|
||||||
|
{
|
||||||
|
// These calls are required by libgcrypt before we use any of its functions.
|
||||||
|
gcry_check_version(0);
|
||||||
|
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
|
||||||
|
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||||
|
}
|
||||||
|
|
||||||
QString PasswordHasher::computeHash(const QString &password, const QString &salt)
|
QString PasswordHasher::computeHash(const QString &password, const QString &salt)
|
||||||
{
|
{
|
||||||
const int algo = GCRY_MD_SHA512;
|
const int algo = GCRY_MD_SHA512;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
class PasswordHasher {
|
class PasswordHasher {
|
||||||
public:
|
public:
|
||||||
|
static void initialize();
|
||||||
static QString computeHash(const QString &password, const QString &salt);
|
static QString computeHash(const QString &password, const QString &salt);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,6 @@
|
||||||
#include "server_logger.h"
|
#include "server_logger.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "decklist.h"
|
#include "decklist.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_message.pb.h"
|
||||||
#include "pb/event_server_shutdown.pb.h"
|
#include "pb/event_server_shutdown.pb.h"
|
||||||
#include "pb/event_connection_closed.pb.h"
|
#include "pb/event_connection_closed.pb.h"
|
||||||
|
@ -44,6 +42,7 @@ Servatrice_GameServer::Servatrice_GameServer(Servatrice *_server, int _numberPoo
|
||||||
server(_server)
|
server(_server)
|
||||||
{
|
{
|
||||||
if (_numberPools == 0) {
|
if (_numberPools == 0) {
|
||||||
|
server->setThreaded(false);
|
||||||
Servatrice_DatabaseInterface *newDatabaseInterface = new Servatrice_DatabaseInterface(0, server);
|
Servatrice_DatabaseInterface *newDatabaseInterface = new Servatrice_DatabaseInterface(0, server);
|
||||||
Servatrice_ConnectionPool *newPool = new Servatrice_ConnectionPool(newDatabaseInterface);
|
Servatrice_ConnectionPool *newPool = new Servatrice_ConnectionPool(newDatabaseInterface);
|
||||||
|
|
||||||
|
@ -57,6 +56,7 @@ Servatrice_GameServer::Servatrice_GameServer(Servatrice *_server, int _numberPoo
|
||||||
Servatrice_ConnectionPool *newPool = new Servatrice_ConnectionPool(newDatabaseInterface);
|
Servatrice_ConnectionPool *newPool = new Servatrice_ConnectionPool(newDatabaseInterface);
|
||||||
|
|
||||||
QThread *newThread = new QThread;
|
QThread *newThread = new QThread;
|
||||||
|
newThread->setObjectName("pool_" + QString::number(i));
|
||||||
newPool->moveToThread(newThread);
|
newPool->moveToThread(newThread);
|
||||||
newDatabaseInterface->moveToThread(newThread);
|
newDatabaseInterface->moveToThread(newThread);
|
||||||
server->addDatabaseInterface(newThread, newDatabaseInterface);
|
server->addDatabaseInterface(newThread, newDatabaseInterface);
|
||||||
|
@ -253,6 +253,7 @@ bool Servatrice::initServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
QThread *thread = new QThread;
|
QThread *thread = new QThread;
|
||||||
|
thread->setObjectName("isl_" + QString::number(prop.id));
|
||||||
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
|
||||||
|
|
||||||
IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this);
|
IslInterface *interface = new IslInterface(prop.id, prop.hostname, prop.address.toString(), prop.controlPort, prop.cert, cert, key, this);
|
||||||
|
@ -290,6 +291,7 @@ bool Servatrice::initServer()
|
||||||
|
|
||||||
const int numberPools = settings->value("server/number_pools", 1).toInt();
|
const int numberPools = settings->value("server/number_pools", 1).toInt();
|
||||||
gameServer = new Servatrice_GameServer(this, numberPools, servatriceDatabaseInterface->getDatabase(), this);
|
gameServer = new Servatrice_GameServer(this, numberPools, servatriceDatabaseInterface->getDatabase(), this);
|
||||||
|
gameServer->setMaxPendingConnections(1000);
|
||||||
const int gamePort = settings->value("server/port", 4747).toInt();
|
const int gamePort = settings->value("server/port", 4747).toInt();
|
||||||
qDebug() << "Starting server on port" << gamePort;
|
qDebug() << "Starting server on port" << gamePort;
|
||||||
if (gameServer->listen(QHostAddress::Any, gamePort))
|
if (gameServer->listen(QHostAddress::Any, gamePort))
|
||||||
|
@ -354,55 +356,6 @@ QList<ServerSocketInterface *> Servatrice::getUsersWithAddressAsList(const QHost
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Servatrice::storeGameInformation(int secondsElapsed, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replayList)
|
|
||||||
{
|
|
||||||
const ServerInfo_Game &gameInfo = replayList.first()->game_info();
|
|
||||||
|
|
||||||
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)
|
|
||||||
gameTypes.append(allGameTypes[gameInfo.game_types(i)]);
|
|
||||||
|
|
||||||
QSetIterator<QString> playerIterator(allPlayersEver);
|
|
||||||
while (playerIterator.hasNext())
|
|
||||||
replayMatchInfo->add_player_names(playerIterator.next().toStdString());
|
|
||||||
|
|
||||||
for (int i = 0; i < replayList.size(); ++i) {
|
|
||||||
ServerInfo_Replay *replayInfo = replayMatchInfo->add_replay_list();
|
|
||||||
replayInfo->set_replay_id(replayList[i]->replay_id());
|
|
||||||
replayInfo->set_replay_name(gameInfo.description());
|
|
||||||
replayInfo->set_duration(replayList[i]->duration_seconds());
|
|
||||||
}
|
|
||||||
|
|
||||||
QSet<QString> allUsersInGame = allPlayersEver + allSpectatorsEver;
|
|
||||||
QSetIterator<QString> allUsersIterator(allUsersInGame);
|
|
||||||
|
|
||||||
SessionEvent *sessionEvent = Server_ProtocolHandler::prepareSessionEvent(replayEvent);
|
|
||||||
clientsLock.lockForRead();
|
|
||||||
while (allUsersIterator.hasNext()) {
|
|
||||||
const QString userName = allUsersIterator.next();
|
|
||||||
Server_AbstractUserInterface *userHandler = users.value(userName);
|
|
||||||
if (!userHandler)
|
|
||||||
userHandler = externalUsers.value(userName);
|
|
||||||
if (userHandler)
|
|
||||||
userHandler->sendProtocolItem(*sessionEvent);
|
|
||||||
}
|
|
||||||
clientsLock.unlock();
|
|
||||||
delete sessionEvent;
|
|
||||||
|
|
||||||
getDatabaseInterface()->storeGameInformation(room->getName(), gameTypes, gameInfo, allPlayersEver, allSpectatorsEver, replayList);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Servatrice::updateLoginMessage()
|
void Servatrice::updateLoginMessage()
|
||||||
{
|
{
|
||||||
if (!servatriceDatabaseInterface->checkSql())
|
if (!servatriceDatabaseInterface->checkSql())
|
||||||
|
|
|
@ -122,6 +122,7 @@ private:
|
||||||
QMap<int, IslInterface *> islInterfaces;
|
QMap<int, IslInterface *> islInterfaces;
|
||||||
public slots:
|
public slots:
|
||||||
void scheduleShutdown(const QString &reason, int minutes);
|
void scheduleShutdown(const QString &reason, int minutes);
|
||||||
|
void updateLoginMessage();
|
||||||
public:
|
public:
|
||||||
Servatrice(QSettings *_settings, QObject *parent = 0);
|
Servatrice(QSettings *_settings, QObject *parent = 0);
|
||||||
~Servatrice();
|
~Servatrice();
|
||||||
|
@ -139,12 +140,10 @@ public:
|
||||||
AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; }
|
AuthenticationMethod getAuthenticationMethod() const { return authenticationMethod; }
|
||||||
QString getDbPrefix() const { return dbPrefix; }
|
QString getDbPrefix() const { return dbPrefix; }
|
||||||
int getServerId() const { return serverId; }
|
int getServerId() const { return serverId; }
|
||||||
void updateLoginMessage();
|
|
||||||
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;
|
||||||
void incTxBytes(quint64 num);
|
void incTxBytes(quint64 num);
|
||||||
void incRxBytes(quint64 num);
|
void incRxBytes(quint64 num);
|
||||||
void storeGameInformation(int secondsElapsed, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replays);
|
|
||||||
void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface);
|
void addDatabaseInterface(QThread *thread, Servatrice_DatabaseInterface *databaseInterface);
|
||||||
|
|
||||||
bool islConnectionExists(int serverId) const;
|
bool islConnectionExists(int serverId) const;
|
||||||
|
|
|
@ -507,15 +507,15 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, const QString &userName)
|
DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int userId)
|
||||||
{
|
{
|
||||||
checkSql();
|
checkSql();
|
||||||
|
|
||||||
QSqlQuery query(sqlDatabase);
|
QSqlQuery query(sqlDatabase);
|
||||||
|
|
||||||
query.prepare("select content from " + server->getDbPrefix() + "_decklist_files where id = :id and user = :user");
|
query.prepare("select content from " + server->getDbPrefix() + "_decklist_files where id = :id and id_user = :id_user");
|
||||||
query.bindValue(":id", deckId);
|
query.bindValue(":id", deckId);
|
||||||
query.bindValue(":user", userName);
|
query.bindValue(":id_user", userId);
|
||||||
execSqlQuery(query);
|
execSqlQuery(query);
|
||||||
if (!query.next())
|
if (!query.next())
|
||||||
throw Response::RespNameNotFound;
|
throw Response::RespNameNotFound;
|
||||||
|
|
|
@ -38,7 +38,7 @@ public:
|
||||||
bool isInIgnoreList(const QString &whoseList, const QString &who);
|
bool isInIgnoreList(const QString &whoseList, const QString &who);
|
||||||
ServerInfo_User getUserData(const QString &name, bool withId = false);
|
ServerInfo_User getUserData(const QString &name, bool withId = false);
|
||||||
void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replayList);
|
void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo, const QSet<QString> &allPlayersEver, const QSet<QString> &allSpectatorsEver, const QList<GameReplay *> &replayList);
|
||||||
DeckList *getDeckFromDatabase(int deckId, const QString &userName);
|
DeckList *getDeckFromDatabase(int deckId, int userId);
|
||||||
|
|
||||||
int getNextGameId();
|
int getNextGameId();
|
||||||
int getNextReplayId();
|
int getNextReplayId();
|
||||||
|
|
|
@ -72,18 +72,25 @@ ServerSocketInterface::ServerSocketInterface(Servatrice *_server, Servatrice_Dat
|
||||||
socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
|
socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
|
||||||
connect(socket, SIGNAL(readyRead()), this, SLOT(readClient()));
|
connect(socket, SIGNAL(readyRead()), this, SLOT(readClient()));
|
||||||
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError)));
|
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(catchSocketError(QAbstractSocket::SocketError)));
|
||||||
connect(this, SIGNAL(outputBufferChanged()), this, SLOT(flushOutputBuffer()), Qt::QueuedConnection);
|
|
||||||
|
// Never call flushOutputQueue directly from outputQueueChanged. In case of a socket error,
|
||||||
|
// it could lead to this object being destroyed while another function is still on the call stack. -> mutex deadlocks etc.
|
||||||
|
connect(this, SIGNAL(outputQueueChanged()), this, SLOT(flushOutputQueue()), Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerSocketInterface::~ServerSocketInterface()
|
ServerSocketInterface::~ServerSocketInterface()
|
||||||
{
|
{
|
||||||
logger->logMessage("ServerSocketInterface destructor", this);
|
logger->logMessage("ServerSocketInterface destructor", this);
|
||||||
|
|
||||||
flushOutputBuffer();
|
flushOutputQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerSocketInterface::initConnection(int socketDescriptor)
|
void ServerSocketInterface::initConnection(int socketDescriptor)
|
||||||
{
|
{
|
||||||
|
// Add this object to the server's list of connections before it can receive socket events.
|
||||||
|
// Otherwise, in case a of a socket error, it could be removed from the list before it is added.
|
||||||
|
server->addClient(this);
|
||||||
|
|
||||||
socket->setSocketDescriptor(socketDescriptor);
|
socket->setSocketDescriptor(socketDescriptor);
|
||||||
logger->logMessage(QString("Incoming connection: %1").arg(socket->peerAddress().toString()), this);
|
logger->logMessage(QString("Incoming connection: %1").arg(socket->peerAddress().toString()), this);
|
||||||
initSessionDeprecated();
|
initSessionDeprecated();
|
||||||
|
@ -93,11 +100,10 @@ void ServerSocketInterface::initSessionDeprecated()
|
||||||
{
|
{
|
||||||
// dirty hack to make v13 client display the correct error message
|
// dirty hack to make v13 client display the correct error message
|
||||||
|
|
||||||
outputBufferMutex.lock();
|
QByteArray buf;
|
||||||
outputBuffer = "<?xml version=\"1.0\"?><cockatrice_server_stream version=\"14\">";
|
buf.append("<?xml version=\"1.0\"?><cockatrice_server_stream version=\"14\">");
|
||||||
outputBufferMutex.unlock();
|
socket->write(buf);
|
||||||
|
socket->flush();
|
||||||
emit outputBufferChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServerSocketInterface::initSession()
|
bool ServerSocketInterface::initSession()
|
||||||
|
@ -121,21 +127,9 @@ bool ServerSocketInterface::initSession()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
server->addClient(this);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerSocketInterface::flushOutputBuffer()
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&outputBufferMutex);
|
|
||||||
if (outputBuffer.isEmpty())
|
|
||||||
return;
|
|
||||||
servatrice->incTxBytes(outputBuffer.size());
|
|
||||||
socket->write(outputBuffer);
|
|
||||||
socket->flush();
|
|
||||||
outputBuffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerSocketInterface::readClient()
|
void ServerSocketInterface::readClient()
|
||||||
{
|
{
|
||||||
QByteArray data = socket->readAll();
|
QByteArray data = socket->readAll();
|
||||||
|
@ -183,18 +177,42 @@ void ServerSocketInterface::catchSocketError(QAbstractSocket::SocketError socket
|
||||||
|
|
||||||
void ServerSocketInterface::transmitProtocolItem(const ServerMessage &item)
|
void ServerSocketInterface::transmitProtocolItem(const ServerMessage &item)
|
||||||
{
|
{
|
||||||
QByteArray buf;
|
outputQueueMutex.lock();
|
||||||
unsigned int size = item.ByteSize();
|
outputQueue.append(item);
|
||||||
buf.resize(size + 4);
|
outputQueueMutex.unlock();
|
||||||
item.SerializeToArray(buf.data() + 4, size);
|
|
||||||
buf.data()[3] = (unsigned char) size;
|
|
||||||
buf.data()[2] = (unsigned char) (size >> 8);
|
|
||||||
buf.data()[1] = (unsigned char) (size >> 16);
|
|
||||||
buf.data()[0] = (unsigned char) (size >> 24);
|
|
||||||
|
|
||||||
QMutexLocker locker(&outputBufferMutex);
|
emit outputQueueChanged();
|
||||||
outputBuffer.append(buf);
|
}
|
||||||
emit outputBufferChanged();
|
|
||||||
|
void ServerSocketInterface::flushOutputQueue()
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&outputQueueMutex);
|
||||||
|
if (outputQueue.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int totalBytes = 0;
|
||||||
|
while (!outputQueue.isEmpty()) {
|
||||||
|
ServerMessage item = outputQueue.takeFirst();
|
||||||
|
locker.unlock();
|
||||||
|
|
||||||
|
QByteArray buf;
|
||||||
|
unsigned int size = item.ByteSize();
|
||||||
|
buf.resize(size + 4);
|
||||||
|
item.SerializeToArray(buf.data() + 4, size);
|
||||||
|
buf.data()[3] = (unsigned char) size;
|
||||||
|
buf.data()[2] = (unsigned char) (size >> 8);
|
||||||
|
buf.data()[1] = (unsigned char) (size >> 16);
|
||||||
|
buf.data()[0] = (unsigned char) (size >> 24);
|
||||||
|
// In case socket->write() calls catchSocketError(), the mutex must not be locked during this call.
|
||||||
|
socket->write(buf);
|
||||||
|
|
||||||
|
totalBytes += size + 4;
|
||||||
|
locker.relock();
|
||||||
|
}
|
||||||
|
locker.unlock();
|
||||||
|
servatrice->incTxBytes(totalBytes);
|
||||||
|
// see above wrt mutex
|
||||||
|
socket->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerSocketInterface::logDebugMessage(const QString &message)
|
void ServerSocketInterface::logDebugMessage(const QString &message)
|
||||||
|
@ -324,10 +342,10 @@ int ServerSocketInterface::getDeckPathId(int basePathId, QStringList path)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
QSqlQuery query(sqlInterface->getDatabase());
|
QSqlQuery query(sqlInterface->getDatabase());
|
||||||
query.prepare("select id from " + servatrice->getDbPrefix() + "_decklist_folders where id_parent = :id_parent and name = :name and user = :user");
|
query.prepare("select id from " + servatrice->getDbPrefix() + "_decklist_folders where id_parent = :id_parent and name = :name and id_user = :id_user");
|
||||||
query.bindValue(":id_parent", basePathId);
|
query.bindValue(":id_parent", basePathId);
|
||||||
query.bindValue(":name", path.takeFirst());
|
query.bindValue(":name", path.takeFirst());
|
||||||
query.bindValue(":user", QString::fromStdString(userInfo->name()));
|
query.bindValue(":id_user", userInfo->id());
|
||||||
if (!sqlInterface->execSqlQuery(query))
|
if (!sqlInterface->execSqlQuery(query))
|
||||||
return -1;
|
return -1;
|
||||||
if (!query.next())
|
if (!query.next())
|
||||||
|
@ -347,9 +365,9 @@ int ServerSocketInterface::getDeckPathId(const QString &path)
|
||||||
bool ServerSocketInterface::deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder)
|
bool ServerSocketInterface::deckListHelper(int folderId, ServerInfo_DeckStorage_Folder *folder)
|
||||||
{
|
{
|
||||||
QSqlQuery query(sqlInterface->getDatabase());
|
QSqlQuery query(sqlInterface->getDatabase());
|
||||||
query.prepare("select id, name from " + servatrice->getDbPrefix() + "_decklist_folders where id_parent = :id_parent and user = :user");
|
query.prepare("select id, name from " + servatrice->getDbPrefix() + "_decklist_folders where id_parent = :id_parent and id_user = :id_user");
|
||||||
query.bindValue(":id_parent", folderId);
|
query.bindValue(":id_parent", folderId);
|
||||||
query.bindValue(":user", QString::fromStdString(userInfo->name()));
|
query.bindValue(":id_user", userInfo->id());
|
||||||
if (!sqlInterface->execSqlQuery(query))
|
if (!sqlInterface->execSqlQuery(query))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -362,9 +380,9 @@ bool ServerSocketInterface::deckListHelper(int folderId, ServerInfo_DeckStorage_
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.prepare("select id, name, upload_time from " + servatrice->getDbPrefix() + "_decklist_files where id_folder = :id_folder and user = :user");
|
query.prepare("select id, name, upload_time from " + servatrice->getDbPrefix() + "_decklist_files where id_folder = :id_folder and id_user = :id_user");
|
||||||
query.bindValue(":id_folder", folderId);
|
query.bindValue(":id_folder", folderId);
|
||||||
query.bindValue(":user", QString::fromStdString(userInfo->name()));
|
query.bindValue(":id_user", userInfo->id());
|
||||||
if (!sqlInterface->execSqlQuery(query))
|
if (!sqlInterface->execSqlQuery(query))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -412,9 +430,9 @@ Response::ResponseCode ServerSocketInterface::cmdDeckNewDir(const Command_DeckNe
|
||||||
return Response::RespNameNotFound;
|
return Response::RespNameNotFound;
|
||||||
|
|
||||||
QSqlQuery query(sqlInterface->getDatabase());
|
QSqlQuery query(sqlInterface->getDatabase());
|
||||||
query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_folders (id_parent, user, name) values(:id_parent, :user, :name)");
|
query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_folders (id_parent, id_user, name) values(:id_parent, :id_user, :name)");
|
||||||
query.bindValue(":id_parent", folderId);
|
query.bindValue(":id_parent", folderId);
|
||||||
query.bindValue(":user", QString::fromStdString(userInfo->name()));
|
query.bindValue(":id_user", userInfo->id());
|
||||||
query.bindValue(":name", QString::fromStdString(cmd.dir_name()));
|
query.bindValue(":name", QString::fromStdString(cmd.dir_name()));
|
||||||
if (!sqlInterface->execSqlQuery(query))
|
if (!sqlInterface->execSqlQuery(query))
|
||||||
return Response::RespContextError;
|
return Response::RespContextError;
|
||||||
|
@ -463,9 +481,9 @@ Response::ResponseCode ServerSocketInterface::cmdDeckDel(const Command_DeckDel &
|
||||||
sqlInterface->checkSql();
|
sqlInterface->checkSql();
|
||||||
QSqlQuery query(sqlInterface->getDatabase());
|
QSqlQuery query(sqlInterface->getDatabase());
|
||||||
|
|
||||||
query.prepare("select id from " + servatrice->getDbPrefix() + "_decklist_files where id = :id and user = :user");
|
query.prepare("select id from " + servatrice->getDbPrefix() + "_decklist_files where id = :id and id_user = :id_user");
|
||||||
query.bindValue(":id", cmd.deck_id());
|
query.bindValue(":id", cmd.deck_id());
|
||||||
query.bindValue(":user", QString::fromStdString(userInfo->name()));
|
query.bindValue(":id_user", userInfo->id());
|
||||||
sqlInterface->execSqlQuery(query);
|
sqlInterface->execSqlQuery(query);
|
||||||
if (!query.next())
|
if (!query.next())
|
||||||
return Response::RespNameNotFound;
|
return Response::RespNameNotFound;
|
||||||
|
@ -500,9 +518,9 @@ Response::ResponseCode ServerSocketInterface::cmdDeckUpload(const Command_DeckUp
|
||||||
return Response::RespNameNotFound;
|
return Response::RespNameNotFound;
|
||||||
|
|
||||||
QSqlQuery query(sqlInterface->getDatabase());
|
QSqlQuery query(sqlInterface->getDatabase());
|
||||||
query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_files (id_folder, user, name, upload_time, content) values(:id_folder, :user, :name, NOW(), :content)");
|
query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_files (id_folder, id_user, name, upload_time, content) values(:id_folder, :id_user, :name, NOW(), :content)");
|
||||||
query.bindValue(":id_folder", folderId);
|
query.bindValue(":id_folder", folderId);
|
||||||
query.bindValue(":user", QString::fromStdString(userInfo->name()));
|
query.bindValue(":id_user", userInfo->id());
|
||||||
query.bindValue(":name", deckName);
|
query.bindValue(":name", deckName);
|
||||||
query.bindValue(":content", deckStr);
|
query.bindValue(":content", deckStr);
|
||||||
sqlInterface->execSqlQuery(query);
|
sqlInterface->execSqlQuery(query);
|
||||||
|
@ -515,9 +533,9 @@ Response::ResponseCode ServerSocketInterface::cmdDeckUpload(const Command_DeckUp
|
||||||
rc.setResponseExtension(re);
|
rc.setResponseExtension(re);
|
||||||
} else if (cmd.has_deck_id()) {
|
} else if (cmd.has_deck_id()) {
|
||||||
QSqlQuery query(sqlInterface->getDatabase());
|
QSqlQuery query(sqlInterface->getDatabase());
|
||||||
query.prepare("update " + servatrice->getDbPrefix() + "_decklist_files set name=:name, upload_time=NOW(), content=:content where id = :id_deck and user = :user");
|
query.prepare("update " + servatrice->getDbPrefix() + "_decklist_files set name=:name, upload_time=NOW(), content=:content where id = :id_deck and id_user = :id_user");
|
||||||
query.bindValue(":id_deck", cmd.deck_id());
|
query.bindValue(":id_deck", cmd.deck_id());
|
||||||
query.bindValue(":user", QString::fromStdString(userInfo->name()));
|
query.bindValue(":id_user", userInfo->id());
|
||||||
query.bindValue(":name", deckName);
|
query.bindValue(":name", deckName);
|
||||||
query.bindValue(":content", deckStr);
|
query.bindValue(":content", deckStr);
|
||||||
sqlInterface->execSqlQuery(query);
|
sqlInterface->execSqlQuery(query);
|
||||||
|
@ -544,7 +562,7 @@ Response::ResponseCode ServerSocketInterface::cmdDeckDownload(const Command_Deck
|
||||||
|
|
||||||
DeckList *deck;
|
DeckList *deck;
|
||||||
try {
|
try {
|
||||||
deck = sqlInterface->getDeckFromDatabase(cmd.deck_id(), QString::fromStdString(userInfo->name()));
|
deck = sqlInterface->getDeckFromDatabase(cmd.deck_id(), userInfo->id());
|
||||||
} catch(Response::ResponseCode r) {
|
} catch(Response::ResponseCode r) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -701,6 +719,7 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
|
||||||
query.bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason()));
|
query.bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason()));
|
||||||
sqlInterface->execSqlQuery(query);
|
sqlInterface->execSqlQuery(query);
|
||||||
|
|
||||||
|
servatrice->clientsLock.lockForRead();
|
||||||
QList<ServerSocketInterface *> userList = servatrice->getUsersWithAddressAsList(QHostAddress(address));
|
QList<ServerSocketInterface *> userList = servatrice->getUsersWithAddressAsList(QHostAddress(address));
|
||||||
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
|
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
|
||||||
if (user && !userList.contains(user))
|
if (user && !userList.contains(user))
|
||||||
|
@ -715,10 +734,11 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
|
||||||
for (int i = 0; i < userList.size(); ++i) {
|
for (int i = 0; i < userList.size(); ++i) {
|
||||||
SessionEvent *se = userList[i]->prepareSessionEvent(event);
|
SessionEvent *se = userList[i]->prepareSessionEvent(event);
|
||||||
userList[i]->sendProtocolItem(*se);
|
userList[i]->sendProtocolItem(*se);
|
||||||
userList[i]->prepareDestroy();
|
|
||||||
delete se;
|
delete se;
|
||||||
|
QMetaObject::invokeMethod(userList[i], "prepareDestroy", Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
servatrice->clientsLock.unlock();
|
||||||
|
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
}
|
}
|
||||||
|
@ -728,7 +748,7 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
|
||||||
|
|
||||||
Response::ResponseCode ServerSocketInterface::cmdUpdateServerMessage(const Command_UpdateServerMessage & /*cmd*/, ResponseContainer & /*rc*/)
|
Response::ResponseCode ServerSocketInterface::cmdUpdateServerMessage(const Command_UpdateServerMessage & /*cmd*/, ResponseContainer & /*rc*/)
|
||||||
{
|
{
|
||||||
servatrice->updateLoginMessage();
|
QMetaObject::invokeMethod(server, "updateLoginMessage");
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,18 +54,19 @@ class ServerSocketInterface : public Server_ProtocolHandler
|
||||||
private slots:
|
private slots:
|
||||||
void readClient();
|
void readClient();
|
||||||
void catchSocketError(QAbstractSocket::SocketError socketError);
|
void catchSocketError(QAbstractSocket::SocketError socketError);
|
||||||
void flushOutputBuffer();
|
void flushOutputQueue();
|
||||||
signals:
|
signals:
|
||||||
void outputBufferChanged();
|
void outputQueueChanged();
|
||||||
protected:
|
protected:
|
||||||
void logDebugMessage(const QString &message);
|
void logDebugMessage(const QString &message);
|
||||||
private:
|
private:
|
||||||
QMutex outputBufferMutex;
|
QMutex outputQueueMutex;
|
||||||
Servatrice *servatrice;
|
Servatrice *servatrice;
|
||||||
Servatrice_DatabaseInterface *sqlInterface;
|
Servatrice_DatabaseInterface *sqlInterface;
|
||||||
QTcpSocket *socket;
|
QTcpSocket *socket;
|
||||||
|
|
||||||
QByteArray inputBuffer, outputBuffer;
|
QByteArray inputBuffer;
|
||||||
|
QList<ServerMessage> outputQueue;
|
||||||
bool messageInProgress;
|
bool messageInProgress;
|
||||||
bool handshakeStarted;
|
bool handshakeStarted;
|
||||||
int messageLength;
|
int messageLength;
|
||||||
|
|
Loading…
Reference in a new issue