Merge branch 'master' into Customizable-shortcuts
This commit is contained in:
commit
be82472f35
42 changed files with 708 additions and 220 deletions
46
README.md
46
README.md
|
@ -1,33 +1,29 @@
|
|||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||
<p align='center'><img src=https://cloud.githubusercontent.com/assets/9874850/7516775/b00b8e36-f4d1-11e4-8da4-3df294d01f86.png></p>
|
||||
|
||||
- [Cockatrice](#cockatrice)
|
||||
- [Get Involved [](https://gitter.im/Cockatrice/Cockatrice)](#get-involved-)
|
||||
- [Community Resources](#community-resources)
|
||||
- [Translation Status [](https://www.transifex.com/projects/p/cockatrice/)](#translation-status-)
|
||||
- [Building [](https://travis-ci.org/Cockatrice/Cockatrice)](#building-)
|
||||
- [Building servatrice Docker container](#building-servatrice-docker-container)
|
||||
- [Running](#running)
|
||||
- [License](#license)
|
||||
---
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
**Table of Contents** [Cockatrice](#cockatrice) | [Get Involved] (#get-involved-) | [Community](#community-resources) | [Translation](#translation-status-) | [Building](#building-) | [Running](#running) | [License](#license)
|
||||
|
||||
---
|
||||
|
||||
# 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, supporting both Qt4 and Qt5.
|
||||
a network interface as well. Both client and server are written in Qt, supporting both Qt4 and Qt5.<br>
|
||||
|
||||
|
||||
# Get Involved [](https://gitter.im/Cockatrice/Cockatrice)
|
||||
|
||||
Chat with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.
|
||||
Chat with the Cockatrice developers on Gitter. Come here to talk about the application, features, or just to hang out. For support regarding specific servers, please contact that server's admin or forum for support rather than asking here.<br>
|
||||
|
||||
|
||||
# Community Resources
|
||||
- [reddit r/Cockatrice](http://reddit.com/r/cockatrice)
|
||||
- [Woogerworks Server & Forums](http://www.woogerworks.com)
|
||||
- [Cockatrice Official Wiki](https://github.com/Cockatrice/Cockatrice/wiki)
|
||||
- [reddit r/Cockatrice](http://reddit.com/r/cockatrice)
|
||||
- [Woogerworks](http://www.woogerworks.com) / [Chickatrice] (http://www.chickatrice.net/) / [Poixen](http://www.poixen.com/) (incomplete Serverlist)<br>
|
||||
|
||||
|
||||
# Translation Status [](https://www.transifex.com/projects/p/cockatrice/)
|
||||
|
||||
|
@ -38,26 +34,25 @@ Language statistics for `Cockatrice` *(on the left)* and `Oracle` *(on the right
|
|||
|
||||
[](https://www.transifex.com/projects/p/cockatrice/resource/cockatrice/) [](https://www.transifex.com/projects/p/cockatrice/resource/oracle/)
|
||||
|
||||
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information!
|
||||
Check out our [Translator FAQ](https://github.com/Cockatrice/Cockatrice/wiki/Translation-FAQ) for more information!<br>
|
||||
|
||||
|
||||
# Building [](https://travis-ci.org/Cockatrice/Cockatrice)
|
||||
|
||||
**Detailed compiling instructions are on the Cockatrice wiki under [Compiling Cockatrice](https://github.com/Cockatrice/Cockatrice/wiki/Compiling-Cockatrice)**
|
||||
|
||||
Dependencies:
|
||||
|
||||
- [Qt](http://qt-project.org/)
|
||||
- [protobuf](http://code.google.com/p/protobuf/)
|
||||
- [CMake](http://www.cmake.org/)
|
||||
|
||||
Oracle can optionally use zlib to load zipped files:
|
||||
|
||||
- [zlib](http://www.zlib.net/)
|
||||
|
||||
The server requires an additional dependency when compiled under Qt4:
|
||||
|
||||
- [libgcrypt](http://www.gnu.org/software/libgcrypt/)
|
||||
|
||||
|
||||
To compile:
|
||||
|
||||
mkdir build
|
||||
|
@ -75,15 +70,18 @@ The following flags can be passed to `cmake`:
|
|||
- `-DCMAKE_BUILD_TYPE=Debug` Compile in debug mode. Enables extra logging output, debug symbols, and much more verbose compiler warnings.
|
||||
- `-DUPDATE_TRANSLATIONS=1` Configure `make` to update the translation .ts files for new strings in the source code. Note: Running `make clean` will remove the .ts files.
|
||||
|
||||
# Building servatrice Docker container
|
||||
`docker build -t servatrice .`
|
||||
#### Building servatrice Docker container
|
||||
`docker build -t servatrice .`<br>
|
||||
|
||||
|
||||
# Running
|
||||
|
||||
`oracle` fetches card data
|
||||
`cockatrice` is the game client
|
||||
`servatrice` is the server
|
||||
`servatrice` is the server<br>
|
||||
|
||||
|
||||
# License
|
||||
|
||||
Cockatrice is free software, licensed under the GPLv2; see COPYING for details.
|
||||
Cockatrice is free software, licensed under the GPLv2; see COPYING for details.<br>
|
||||
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
#include <QSettings>
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
#include <QComboBox>
|
||||
#include <QRadioButton>
|
||||
#include <QGridLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDebug>
|
||||
#include <QEvent>
|
||||
#include <QKeyEvent>
|
||||
#include <iostream>
|
||||
#include "dlg_connect.h"
|
||||
|
||||
|
@ -14,8 +18,24 @@ DlgConnect::DlgConnect(QWidget *parent)
|
|||
QSettings settings;
|
||||
settings.beginGroup("server");
|
||||
|
||||
previousHostButton = new QRadioButton(tr("Previous Host"), this);
|
||||
|
||||
previousHosts = new QComboBox(this);
|
||||
previousHosts->installEventFilter(new DeleteHighlightedItemWhenShiftDelPressedEventFilter);
|
||||
QStringList previousHostList = settings.value("previoushosts").toStringList();
|
||||
if (previousHostList.isEmpty()) {
|
||||
previousHostList << "cockatrice.woogerworks.com";
|
||||
previousHostList << "vps.poixen.com";
|
||||
previousHostList << "chickatrice.net";
|
||||
}
|
||||
previousHosts->addItems(previousHostList);
|
||||
previousHosts->setCurrentIndex(settings.value("previoushostindex").toInt());
|
||||
|
||||
newHostButton = new QRadioButton(tr("New Host"), this);
|
||||
|
||||
hostLabel = new QLabel(tr("&Host:"));
|
||||
hostEdit = new QLineEdit(settings.value("hostname", "cockatrice.woogerworks.com").toString());
|
||||
hostEdit = new QLineEdit();
|
||||
hostEdit->setPlaceholderText(tr("Enter host name"));
|
||||
hostLabel->setBuddy(hostEdit);
|
||||
|
||||
portLabel = new QLabel(tr("&Port:"));
|
||||
|
@ -48,16 +68,19 @@ DlgConnect::DlgConnect(QWidget *parent)
|
|||
connect(savePasswordCheckBox, SIGNAL(stateChanged(int)), this, SLOT(passwordSaved(int)));
|
||||
|
||||
QGridLayout *grid = new QGridLayout;
|
||||
grid->addWidget(hostLabel, 0, 0);
|
||||
grid->addWidget(hostEdit, 0, 1);
|
||||
grid->addWidget(portLabel, 1, 0);
|
||||
grid->addWidget(portEdit, 1, 1);
|
||||
grid->addWidget(playernameLabel, 2, 0);
|
||||
grid->addWidget(playernameEdit, 2, 1);
|
||||
grid->addWidget(passwordLabel, 3, 0);
|
||||
grid->addWidget(passwordEdit, 3, 1);
|
||||
grid->addWidget(savePasswordCheckBox, 4, 0, 1, 2);
|
||||
grid->addWidget(autoConnectCheckBox, 5, 0, 1, 2);
|
||||
grid->addWidget(previousHostButton, 0, 1);
|
||||
grid->addWidget(previousHosts, 1, 1);
|
||||
grid->addWidget(newHostButton, 2, 1);
|
||||
grid->addWidget(hostLabel, 3, 0);
|
||||
grid->addWidget(hostEdit, 3, 1);
|
||||
grid->addWidget(portLabel, 4, 0);
|
||||
grid->addWidget(portEdit, 4, 1);
|
||||
grid->addWidget(playernameLabel, 5, 0);
|
||||
grid->addWidget(playernameEdit, 5, 1);
|
||||
grid->addWidget(passwordLabel, 6, 0);
|
||||
grid->addWidget(passwordEdit, 6, 1);
|
||||
grid->addWidget(savePasswordCheckBox, 7, 0, 1, 2);
|
||||
grid->addWidget(autoConnectCheckBox, 8, 0, 1, 2);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(actOk()));
|
||||
|
@ -71,8 +94,32 @@ DlgConnect::DlgConnect(QWidget *parent)
|
|||
setWindowTitle(tr("Connect to server"));
|
||||
setFixedHeight(sizeHint().height());
|
||||
setMinimumWidth(300);
|
||||
|
||||
connect(previousHostButton, SIGNAL(toggled(bool)), this, SLOT(previousHostSelected(bool)));
|
||||
connect(newHostButton, SIGNAL(toggled(bool)), this, SLOT(newHostSelected(bool)));
|
||||
|
||||
if (settings.value("previoushostlogin", 1).toInt())
|
||||
previousHostButton->setChecked(true);
|
||||
else
|
||||
newHostButton->setChecked(true);
|
||||
}
|
||||
|
||||
|
||||
void DlgConnect::previousHostSelected(bool state) {
|
||||
if (state) {
|
||||
hostEdit->setDisabled(true);
|
||||
previousHosts->setDisabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void DlgConnect::newHostSelected(bool state) {
|
||||
if (state) {
|
||||
hostEdit->setDisabled(false);
|
||||
previousHosts->setDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DlgConnect::passwordSaved(int state)
|
||||
{
|
||||
Q_UNUSED(state);
|
||||
|
@ -88,17 +135,34 @@ void DlgConnect::actOk()
|
|||
{
|
||||
QSettings settings;
|
||||
settings.beginGroup("server");
|
||||
settings.setValue("hostname", hostEdit->text());
|
||||
settings.setValue("port", portEdit->text());
|
||||
settings.setValue("playername", playernameEdit->text());
|
||||
settings.setValue("password", savePasswordCheckBox->isChecked() ? passwordEdit->text() : QString());
|
||||
settings.setValue("save_password", savePasswordCheckBox->isChecked() ? 1 : 0);
|
||||
settings.setValue("auto_connect", autoConnectCheckBox->isChecked() ? 1 : 0);
|
||||
settings.setValue("previoushostlogin", previousHostButton->isChecked() ? 1 : 0);
|
||||
|
||||
QStringList hostList;
|
||||
if (newHostButton->isChecked())
|
||||
if (!hostEdit->text().trimmed().isEmpty())
|
||||
hostList << hostEdit->text();
|
||||
|
||||
for (int i = 0; i < previousHosts->count(); i++)
|
||||
if(!previousHosts->itemText(i).trimmed().isEmpty())
|
||||
hostList << previousHosts->itemText(i);
|
||||
|
||||
settings.setValue("previoushosts", hostList);
|
||||
settings.setValue("previoushostindex", previousHosts->currentIndex());
|
||||
settings.endGroup();
|
||||
|
||||
accept();
|
||||
}
|
||||
|
||||
|
||||
QString DlgConnect::getHost() const {
|
||||
return previousHostButton->isChecked() ? previousHosts->currentText() : hostEdit->text();
|
||||
}
|
||||
|
||||
void DlgConnect::actCancel()
|
||||
{
|
||||
QSettings settings;
|
||||
|
@ -109,3 +173,17 @@ void DlgConnect::actCancel()
|
|||
|
||||
reject();
|
||||
}
|
||||
|
||||
|
||||
bool DeleteHighlightedItemWhenShiftDelPressedEventFilter::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
|
||||
if (keyEvent->key() == Qt::Key_Delete) {
|
||||
QComboBox* combobox = reinterpret_cast<QComboBox *>(obj);
|
||||
combobox->removeItem(combobox->currentIndex());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
|
|
@ -7,12 +7,22 @@
|
|||
class QLabel;
|
||||
class QPushButton;
|
||||
class QCheckBox;
|
||||
class QComboBox;
|
||||
class QRadioButton;
|
||||
|
||||
class DeleteHighlightedItemWhenShiftDelPressedEventFilter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
};
|
||||
|
||||
|
||||
class DlgConnect : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DlgConnect(QWidget *parent = 0);
|
||||
QString getHost() const { return hostEdit->text(); }
|
||||
QString getHost() const;
|
||||
int getPort() const { return portEdit->text().toInt(); }
|
||||
QString getPlayerName() const { return playernameEdit->text(); }
|
||||
QString getPassword() const { return passwordEdit->text(); }
|
||||
|
@ -20,10 +30,14 @@ private slots:
|
|||
void actOk();
|
||||
void actCancel();
|
||||
void passwordSaved(int state);
|
||||
void previousHostSelected(bool state);
|
||||
void newHostSelected(bool state);
|
||||
private:
|
||||
QLabel *hostLabel, *portLabel, *playernameLabel, *passwordLabel;
|
||||
QLineEdit *hostEdit, *portEdit, *playernameEdit, *passwordEdit;
|
||||
QCheckBox *savePasswordCheckBox, *autoConnectCheckBox;
|
||||
QComboBox *previousHosts;
|
||||
QRadioButton *newHostButton, *previousHostButton;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -574,6 +574,9 @@ MessagesSettingsPage::MessagesSettingsPage()
|
|||
{
|
||||
chatMentionCheckBox.setChecked(settingsCache->getChatMention());
|
||||
connect(&chatMentionCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMention(int)));
|
||||
|
||||
chatMentionCompleterCheckbox.setChecked(settingsCache->getChatMentionCompleter());
|
||||
connect(&chatMentionCompleterCheckbox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setChatMentionCompleter(int)));
|
||||
|
||||
ignoreUnregUsersMainChat.setChecked(settingsCache->getIgnoreUnregisteredUsers());
|
||||
ignoreUnregUserMessages.setChecked(settingsCache->getIgnoreUnregisteredUserMessages());
|
||||
|
@ -606,11 +609,12 @@ MessagesSettingsPage::MessagesSettingsPage()
|
|||
chatGrid->addWidget(&chatMentionCheckBox, 0, 0);
|
||||
chatGrid->addWidget(&invertMentionForeground, 0, 1);
|
||||
chatGrid->addWidget(mentionColor, 0, 2);
|
||||
chatGrid->addWidget(&ignoreUnregUsersMainChat, 1, 0);
|
||||
chatGrid->addWidget(&chatMentionCompleterCheckbox, 1, 0);
|
||||
chatGrid->addWidget(&ignoreUnregUsersMainChat, 2, 0);
|
||||
chatGrid->addWidget(&hexLabel, 1, 2);
|
||||
chatGrid->addWidget(&ignoreUnregUserMessages, 2, 0);
|
||||
chatGrid->addWidget(&messagePopups, 3, 0);
|
||||
chatGrid->addWidget(&mentionPopups, 4, 0);
|
||||
chatGrid->addWidget(&ignoreUnregUserMessages, 3, 0);
|
||||
chatGrid->addWidget(&messagePopups, 4, 0);
|
||||
chatGrid->addWidget(&mentionPopups, 5, 0);
|
||||
chatGroupBox = new QGroupBox;
|
||||
chatGroupBox->setLayout(chatGrid);
|
||||
|
||||
|
@ -735,6 +739,7 @@ void MessagesSettingsPage::retranslateUi()
|
|||
chatGroupBox->setTitle(tr("Chat settings"));
|
||||
highlightGroupBox->setTitle(tr("Custom alert words"));
|
||||
chatMentionCheckBox.setText(tr("Enable chat mentions"));
|
||||
chatMentionCompleterCheckbox.setText(tr("Enable mention completer"));
|
||||
messageShortcuts->setTitle(tr("In-game message macros"));
|
||||
ignoreUnregUsersMainChat.setText(tr("Ignore chat room messages sent by unregistered users"));
|
||||
ignoreUnregUserMessages.setText(tr("Ignore private messages sent by unregistered users"));
|
||||
|
|
|
@ -168,6 +168,7 @@ private:
|
|||
QAction *aAdd;
|
||||
QAction *aRemove;
|
||||
QCheckBox chatMentionCheckBox;
|
||||
QCheckBox chatMentionCompleterCheckbox;
|
||||
QCheckBox invertMentionForeground;
|
||||
QCheckBox invertHighlightForeground;
|
||||
QCheckBox ignoreUnregUsersMainChat;
|
||||
|
|
|
@ -33,7 +33,7 @@ ServerInfo_User LocalServer_DatabaseInterface::getUserData(const QString &name,
|
|||
return result;
|
||||
}
|
||||
|
||||
AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, const QString & /* user */, const QString & /* password */, QString & /* reasonStr */, int & /* secondsLeft */)
|
||||
AuthenticationResult LocalServer_DatabaseInterface::checkUserPassword(Server_ProtocolHandler * /* handler */, const QString & /* user */, const QString & /* password */, const QString & /* clientId */, QString & /* reasonStr */, int & /* secondsLeft */)
|
||||
{
|
||||
return UnknownUser;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ protected:
|
|||
ServerInfo_User getUserData(const QString &name, bool withId = false);
|
||||
public:
|
||||
LocalServer_DatabaseInterface(LocalServer *_localServer);
|
||||
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft);
|
||||
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft);
|
||||
int getNextGameId() { return localServer->getNextLocalGameId(); }
|
||||
int getNextReplayId() { return -1; }
|
||||
int getActiveUserCount() { return 0; }
|
||||
|
|
|
@ -222,8 +222,8 @@ int main(int argc, char *argv[])
|
|||
settingsCache->setClientID(generateClientID());
|
||||
qDebug() << "ClientID In Cache: " << settingsCache->getClientID();
|
||||
|
||||
ui.showMaximized();
|
||||
qDebug("main(): ui.showMaximized() finished");
|
||||
ui.show();
|
||||
qDebug("main(): ui.show() finished");
|
||||
|
||||
app.exec();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "pb/server_message.pb.h"
|
||||
#include "pb/event_server_identification.pb.h"
|
||||
#include "settingscache.h"
|
||||
#include "main.h"
|
||||
|
||||
static const unsigned int protocolVersion = 14;
|
||||
|
||||
|
@ -78,6 +79,7 @@ void RemoteClient::processServerIdentificationEvent(const Event_ServerIdentifica
|
|||
cmdRegister.set_gender((ServerInfo_User_Gender) gender);
|
||||
cmdRegister.set_country(country.toStdString());
|
||||
cmdRegister.set_real_name(realName.toStdString());
|
||||
cmdRegister.set_clientid(settingsCache->getClientID().toStdString());
|
||||
|
||||
PendingCommand *pend = prepareSessionCommand(cmdRegister);
|
||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(registerResponse(Response)));
|
||||
|
|
|
@ -78,6 +78,7 @@ SettingsCache::SettingsCache()
|
|||
minPlayersForMultiColumnLayout = settings->value("interface/min_players_multicolumn", 5).toInt();
|
||||
tapAnimation = settings->value("cards/tapanimation", true).toBool();
|
||||
chatMention = settings->value("chat/mention", true).toBool();
|
||||
chatMentionCompleter = settings->value("chat/mentioncompleter", true).toBool();
|
||||
chatMentionForeground = settings->value("chat/mentionforeground", true).toBool();
|
||||
chatHighlightForeground = settings->value("chat/highlightforeground", true).toBool();
|
||||
chatMentionColor = settings->value("chat/mentioncolor", "A6120D").toString();
|
||||
|
@ -361,6 +362,13 @@ void SettingsCache::setChatMention(int _chatMention) {
|
|||
settings->setValue("chat/mention", chatMention);
|
||||
}
|
||||
|
||||
void SettingsCache::setChatMentionCompleter(const int _enableMentionCompleter)
|
||||
{
|
||||
chatMentionCompleter = _enableMentionCompleter;
|
||||
settings->setValue("chat/mentioncompleter", chatMentionCompleter);
|
||||
emit chatMentionCompleterChanged();
|
||||
}
|
||||
|
||||
void SettingsCache::setChatMentionForeground(int _chatMentionForeground) {
|
||||
chatMentionForeground = _chatMentionForeground;
|
||||
settings->setValue("chat/mentionforeground", chatMentionForeground);
|
||||
|
|
|
@ -44,6 +44,7 @@ signals:
|
|||
void ignoreUnregisteredUserMessagesChanged();
|
||||
void pixmapCacheSizeChanged(int newSizeInMBs);
|
||||
void masterVolumeChanged(int value);
|
||||
void chatMentionCompleterChanged();
|
||||
private:
|
||||
QSettings *settings;
|
||||
ShortcutsSettings *shortcutsSettings;
|
||||
|
@ -66,6 +67,7 @@ private:
|
|||
int minPlayersForMultiColumnLayout;
|
||||
bool tapAnimation;
|
||||
bool chatMention;
|
||||
bool chatMentionCompleter;
|
||||
QString chatMentionColor;
|
||||
QString chatHighlightColor;
|
||||
bool chatMentionForeground;
|
||||
|
@ -137,6 +139,7 @@ public:
|
|||
int getMinPlayersForMultiColumnLayout() const { return minPlayersForMultiColumnLayout; }
|
||||
bool getTapAnimation() const { return tapAnimation; }
|
||||
bool getChatMention() const { return chatMention; }
|
||||
bool getChatMentionCompleter() const { return chatMentionCompleter; }
|
||||
bool getChatMentionForeground() const { return chatMentionForeground; }
|
||||
bool getChatHighlightForeground() const { return chatHighlightForeground; }
|
||||
bool getZoneViewSortByName() const { return zoneViewSortByName; }
|
||||
|
@ -220,6 +223,7 @@ public slots:
|
|||
void setMinPlayersForMultiColumnLayout(int _minPlayersForMultiColumnLayout);
|
||||
void setTapAnimation(int _tapAnimation);
|
||||
void setChatMention(int _chatMention);
|
||||
void setChatMentionCompleter(int _chatMentionCompleter);
|
||||
void setChatMentionForeground(int _chatMentionForeground);
|
||||
void setChatHighlightForeground(int _chatHighlightForeground);
|
||||
void setZoneViewSortByName(int _zoneViewSortByName);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "shortcutssettings.h"
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
|
||||
ShortcutsSettings::ShortcutsSettings(QString settingsPath, QObject *parent) : QObject(parent)
|
||||
{
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <QTimer>
|
||||
#include <QDockWidget>
|
||||
#include <QPushButton>
|
||||
#include <QDir>
|
||||
#include <QDesktopServices>
|
||||
#include "tab_deck_editor.h"
|
||||
#include "window_sets.h"
|
||||
#include "carddatabase.h"
|
||||
|
@ -363,6 +365,7 @@ void TabDeckEditor::createMenus()
|
|||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||
dbMenu->addSeparator();
|
||||
dbMenu->addAction(aOpenCustomFolder);
|
||||
dbMenu->addAction(aOpenCustomsetsFolder);
|
||||
#endif
|
||||
addTabMenu(dbMenu);
|
||||
}
|
||||
|
@ -573,6 +576,7 @@ void TabDeckEditor::retranslateUi()
|
|||
aPrintDeck->setText(tr("&Print deck..."));
|
||||
aAnalyzeDeck->setText(tr("&Analyze deck on deckstats.net"));
|
||||
aOpenCustomFolder->setText(tr("Open custom image folder"));
|
||||
aOpenCustomsetsFolder->setText(tr("Open custom sets folder"));
|
||||
aClose->setText(tr("&Close"));
|
||||
aClose->setShortcuts(settingsCache->shortcuts().getShortcut(
|
||||
"TabDeckEditor/aClose",
|
||||
|
@ -825,6 +829,33 @@ void TabDeckEditor::actOpenCustomFolder() {
|
|||
|
||||
}
|
||||
|
||||
void TabDeckEditor::actOpenCustomsetsFolder() {
|
||||
#if QT_VERSION < 0x050000
|
||||
QString dataDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||
#else
|
||||
QString dataDir = QStandardPaths::standardLocations(QStandardPaths::DataLocation).first();
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
|
||||
QStringList scriptArgs;
|
||||
scriptArgs << QLatin1String("-e");
|
||||
scriptArgs << QString::fromLatin1("tell application \"Finder\" to open POSIX file \"%1\"").arg(dataDir + "/customsets/");
|
||||
scriptArgs << QLatin1String("-e");
|
||||
scriptArgs << QLatin1String("tell application \"Finder\" to activate");
|
||||
|
||||
QProcess::execute("/usr/bin/osascript", scriptArgs);
|
||||
#endif
|
||||
#if defined(Q_OS_WIN)
|
||||
QStringList args;
|
||||
dataDir.append("/customsets");
|
||||
args << QDir::toNativeSeparators(dataDir);
|
||||
aOpenCustomsetsFolder->setText(dataDir);
|
||||
QProcess::startDetached("explorer", args);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void TabDeckEditor::actEditSets()
|
||||
{
|
||||
WndSets *w = new WndSets;
|
||||
|
|
|
@ -54,6 +54,7 @@ class TabDeckEditor : public Tab {
|
|||
void actPrintDeck();
|
||||
void actAnalyzeDeck();
|
||||
void actOpenCustomFolder();
|
||||
void actOpenCustomsetsFolder();
|
||||
|
||||
void actEditSets();
|
||||
void actEditTokens();
|
||||
|
@ -112,7 +113,7 @@ private:
|
|||
QWidget *filterBox;
|
||||
|
||||
QMenu *deckMenu, *dbMenu;
|
||||
QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose, *aOpenCustomFolder;
|
||||
QAction *aNewDeck, *aLoadDeck, *aSaveDeck, *aSaveDeckAs, *aLoadDeckFromClipboard, *aSaveDeckToClipboard, *aPrintDeck, *aAnalyzeDeck, *aClose, *aOpenCustomFolder, *aOpenCustomsetsFolder;
|
||||
QAction *aEditSets, *aEditTokens, *aClearFilterAll, *aClearFilterOne;
|
||||
QAction *aAddCard, *aAddCardToSideboard, *aRemoveCard, *aIncrement, *aDecrement;// *aUpdatePrices;
|
||||
QAction *aResetLayout;
|
||||
|
|
|
@ -11,6 +11,14 @@
|
|||
#include <QSplitter>
|
||||
#include <QApplication>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QCompleter>
|
||||
#include <QWidget>
|
||||
#include <QTextCursor>
|
||||
#include <QAbstractItemView>
|
||||
#include <QScrollBar>
|
||||
#include <QStringListModel>
|
||||
#include <QKeyEvent>
|
||||
#include <QFocusEvent>
|
||||
#include "tab_supervisor.h"
|
||||
#include "tab_room.h"
|
||||
#include "tab_userlists.h"
|
||||
|
@ -51,8 +59,9 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI
|
|||
connect(chatView, SIGNAL(showCardInfoPopup(QPoint, QString)), this, SLOT(showCardInfoPopup(QPoint, QString)));
|
||||
connect(chatView, SIGNAL(deleteCardInfoPopup(QString)), this, SLOT(deleteCardInfoPopup(QString)));
|
||||
connect(chatView, SIGNAL(addMentionTag(QString)), this, SLOT(addMentionTag(QString)));
|
||||
connect(settingsCache, SIGNAL(chatMentionCompleterChanged()), this, SLOT(actCompleterChanged()));
|
||||
sayLabel = new QLabel;
|
||||
sayEdit = new QLineEdit;
|
||||
sayEdit = new CustomLineEdit;
|
||||
sayLabel->setBuddy(sayEdit);
|
||||
connect(sayEdit, SIGNAL(returnPressed()), this, SLOT(sendMessage()));
|
||||
|
||||
|
@ -103,13 +112,26 @@ TabRoom::TabRoom(TabSupervisor *_tabSupervisor, AbstractClient *_client, ServerI
|
|||
setLayout(hbox);
|
||||
|
||||
const int userListSize = info.user_list_size();
|
||||
for (int i = 0; i < userListSize; ++i)
|
||||
for (int i = 0; i < userListSize; ++i){
|
||||
userList->processUserInfo(info.user_list(i), true);
|
||||
autocompleteUserList.append("@" + QString::fromStdString(info.user_list(i).name()));
|
||||
}
|
||||
userList->sortItems();
|
||||
|
||||
const int gameListSize = info.game_list_size();
|
||||
for (int i = 0; i < gameListSize; ++i)
|
||||
gameSelector->processGameInfo(info.game_list(i));
|
||||
|
||||
completer = new QCompleter(autocompleteUserList, sayEdit);
|
||||
completer->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
completer->setMaxVisibleItems(5);
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
completer->setFilterMode(Qt::MatchStartsWith);
|
||||
#endif
|
||||
|
||||
sayEdit->setCompleter(completer);
|
||||
actCompleterChanged();
|
||||
}
|
||||
|
||||
TabRoom::~TabRoom()
|
||||
|
@ -119,7 +141,7 @@ TabRoom::~TabRoom()
|
|||
|
||||
void TabRoom::retranslateUi()
|
||||
{
|
||||
gameSelector->retranslateUi();
|
||||
gameSelector->retranslateUi();
|
||||
chatView->retranslateUi();
|
||||
userList->retranslateUi();
|
||||
sayLabel->setText(tr("&Say:"));
|
||||
|
@ -166,16 +188,20 @@ QString TabRoom::sanitizeHtml(QString dirty) const
|
|||
|
||||
void TabRoom::sendMessage()
|
||||
{
|
||||
if (sayEdit->text().isEmpty())
|
||||
return;
|
||||
if (sayEdit->text().isEmpty()){
|
||||
return;
|
||||
}else if (completer->popup()->isVisible()){
|
||||
completer->popup()->hide();
|
||||
return;
|
||||
}else{
|
||||
Command_RoomSay cmd;
|
||||
cmd.set_message(sayEdit->text().toStdString());
|
||||
|
||||
Command_RoomSay cmd;
|
||||
cmd.set_message(sayEdit->text().toStdString());
|
||||
|
||||
PendingCommand *pend = prepareRoomCommand(cmd);
|
||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(sayFinished(const Response &)));
|
||||
sendRoomCommand(pend);
|
||||
sayEdit->clear();
|
||||
PendingCommand *pend = prepareRoomCommand(cmd);
|
||||
connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this, SLOT(sayFinished(const Response &)));
|
||||
sendRoomCommand(pend);
|
||||
sayEdit->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TabRoom::sayFinished(const Response &response)
|
||||
|
@ -200,6 +226,11 @@ void TabRoom::actOpenChatSettings() {
|
|||
settings.exec();
|
||||
}
|
||||
|
||||
void TabRoom::actCompleterChanged()
|
||||
{
|
||||
settingsCache->getChatMentionCompleter() ? completer->setCompletionRole(2) : completer->setCompletionRole(1);
|
||||
}
|
||||
|
||||
void TabRoom::processRoomEvent(const RoomEvent &event)
|
||||
{
|
||||
switch (static_cast<RoomEvent::RoomEventType>(getPbExtension(event))) {
|
||||
|
@ -222,11 +253,17 @@ void TabRoom::processJoinRoomEvent(const Event_JoinRoom &event)
|
|||
{
|
||||
userList->processUserInfo(event.user_info(), true);
|
||||
userList->sortItems();
|
||||
if (!autocompleteUserList.contains("@" + QString::fromStdString(event.user_info().name()))){
|
||||
autocompleteUserList << "@" + QString::fromStdString(event.user_info().name());
|
||||
sayEdit->updateCompleterModel(autocompleteUserList);
|
||||
}
|
||||
}
|
||||
|
||||
void TabRoom::processLeaveRoomEvent(const Event_LeaveRoom &event)
|
||||
{
|
||||
userList->deleteUser(QString::fromStdString(event.name()));
|
||||
autocompleteUserList.removeOne("@" + QString::fromStdString(event.name()));
|
||||
sayEdit->updateCompleterModel(autocompleteUserList);
|
||||
}
|
||||
|
||||
void TabRoom::processRoomSayEvent(const Event_RoomSay &event)
|
||||
|
@ -259,3 +296,124 @@ void TabRoom::sendRoomCommand(PendingCommand *pend)
|
|||
{
|
||||
client->sendCommand(pend);
|
||||
}
|
||||
|
||||
CustomLineEdit::CustomLineEdit(QWidget *parent)
|
||||
: QLineEdit(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void CustomLineEdit::focusOutEvent(QFocusEvent * e){
|
||||
QLineEdit::focusOutEvent(e);
|
||||
if (c->popup()->isVisible()){
|
||||
//Remove Popup
|
||||
c->popup()->hide();
|
||||
//Truncate the line to last space or whole string
|
||||
QString textValue = text();
|
||||
int lastIndex = textValue.length();
|
||||
int lastWordStartIndex = textValue.lastIndexOf(" ") + 1;
|
||||
int leftShift = qMin(lastIndex, lastWordStartIndex);
|
||||
setText(textValue.left(leftShift));
|
||||
//Insert highlighted line from popup
|
||||
insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " ");
|
||||
//Set focus back to the textbox since tab was pressed
|
||||
setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void CustomLineEdit::keyPressEvent(QKeyEvent * event)
|
||||
{
|
||||
switch (event->key()){
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Escape:
|
||||
if (c->popup()->isVisible()){
|
||||
event->ignore();
|
||||
//Remove Popup
|
||||
c->popup()->hide();
|
||||
//Truncate the line to last space or whole string
|
||||
QString textValue = text();
|
||||
int lastIndexof = textValue.lastIndexOf(" ");
|
||||
QString finalString = textValue.left(lastIndexof);
|
||||
//Add a space if there's a word
|
||||
if (finalString != "")
|
||||
finalString += " ";
|
||||
setText(finalString);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case Qt::Key_Space:
|
||||
if (c->popup()->isVisible()){
|
||||
event->ignore();
|
||||
//Remove Popup
|
||||
c->popup()->hide();
|
||||
//Truncate the line to last space or whole string
|
||||
QString textValue = text();
|
||||
int lastIndex = textValue.length();
|
||||
int lastWordStartIndex = textValue.lastIndexOf(" ") + 1;
|
||||
int leftShift = qMin(lastIndex, lastWordStartIndex);
|
||||
setText(textValue.left(leftShift));
|
||||
//Insert highlighted line from popup
|
||||
insert(c->completionModel()->index(c->popup()->currentIndex().row(), 0).data().toString() + " ");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QLineEdit::keyPressEvent(event);
|
||||
//Wait until the first character after @
|
||||
if (!c || text().right(1).contains("@"))
|
||||
return;
|
||||
|
||||
//Set new completion prefix
|
||||
c->setCompletionPrefix(cursorWord(text()));
|
||||
if (c->completionPrefix().length() < 1){
|
||||
c->popup()->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
//Draw completion box
|
||||
QRect cr = cursorRect();
|
||||
cr.setWidth(c->popup()->sizeHintForColumn(0) + c->popup()->verticalScrollBar()->sizeHint().width());
|
||||
c->complete(cr);
|
||||
|
||||
//Select first item in the completion popup
|
||||
QItemSelectionModel* sm = new QItemSelectionModel(c->completionModel());
|
||||
c->popup()->setSelectionModel(sm);
|
||||
sm->select(c->completionModel()->index(0, 0), QItemSelectionModel::ClearAndSelect);
|
||||
sm->setCurrentIndex(c->completionModel()->index(0, 0), QItemSelectionModel::NoUpdate);
|
||||
}
|
||||
|
||||
QString CustomLineEdit::cursorWord(const QString &line) const
|
||||
{
|
||||
return line.mid(line.left(cursorPosition()).lastIndexOf(" ") + 1,
|
||||
cursorPosition() - line.left(cursorPosition()).lastIndexOf(" ") - 1);
|
||||
}
|
||||
|
||||
void CustomLineEdit::insertCompletion(QString arg)
|
||||
{
|
||||
QString s_arg = arg + " ";
|
||||
setText(text().replace(text().left(cursorPosition()).lastIndexOf(" ") + 1,
|
||||
cursorPosition() - text().left(cursorPosition()).lastIndexOf(" ") - 1, s_arg));
|
||||
}
|
||||
|
||||
void CustomLineEdit::setCompleter(QCompleter* completer)
|
||||
{
|
||||
c = completer;
|
||||
c->setWidget(this);
|
||||
connect(c, SIGNAL(activated(QString)),this, SLOT(insertCompletion(QString)));
|
||||
}
|
||||
|
||||
void CustomLineEdit::updateCompleterModel(QStringList completionList)
|
||||
{
|
||||
if (!c || c->popup()->isVisible())
|
||||
return;
|
||||
|
||||
QStringListModel *model;
|
||||
model = (QStringListModel*)(c->model());
|
||||
if (model == NULL)
|
||||
model = new QStringListModel();
|
||||
QStringList updatedList = completionList;
|
||||
model->setStringList(updatedList);
|
||||
}
|
|
@ -4,6 +4,9 @@
|
|||
#include "tab.h"
|
||||
#include <QGroupBox>
|
||||
#include <QMap>
|
||||
#include <QLineEdit>
|
||||
#include <QKeyEvent>
|
||||
#include <QFocusEvent>
|
||||
|
||||
namespace google { namespace protobuf { class Message; } }
|
||||
class AbstractClient;
|
||||
|
@ -13,6 +16,7 @@ class ChatView;
|
|||
class QLineEdit;
|
||||
class QPushButton;
|
||||
class QTextTable;
|
||||
class QCompleter;
|
||||
class RoomEvent;
|
||||
class ServerInfo_Room;
|
||||
class ServerInfo_Game;
|
||||
|
@ -24,6 +28,7 @@ class GameSelector;
|
|||
class Response;
|
||||
class PendingCommand;
|
||||
class ServerInfo_User;
|
||||
class CustomLineEdit;
|
||||
|
||||
class TabRoom : public Tab {
|
||||
Q_OBJECT
|
||||
|
@ -38,14 +43,17 @@ private:
|
|||
UserList *userList;
|
||||
ChatView *chatView;
|
||||
QLabel *sayLabel;
|
||||
QLineEdit *sayEdit;
|
||||
CustomLineEdit *sayEdit;
|
||||
QGroupBox *chatGroupBox;
|
||||
|
||||
QMenu *roomMenu;
|
||||
QAction *aLeaveRoom;
|
||||
QAction *aOpenChatSettings;
|
||||
QAction * aClearChat;
|
||||
QAction *aClearChat;
|
||||
QString sanitizeHtml(QString dirty) const;
|
||||
|
||||
QStringList autocompleteUserList;
|
||||
QCompleter *completer;
|
||||
signals:
|
||||
void roomClosing(TabRoom *tab);
|
||||
void openMessageDialog(const QString &userName, bool focus);
|
||||
|
@ -59,7 +67,8 @@ private slots:
|
|||
void addMentionTag(QString mentionTag);
|
||||
void focusTab();
|
||||
void actShowMentionPopup(QString &sender);
|
||||
|
||||
void actCompleterChanged();
|
||||
|
||||
void processListGamesEvent(const Event_ListGames &event);
|
||||
void processJoinRoomEvent(const Event_JoinRoom &event);
|
||||
void processLeaveRoomEvent(const Event_LeaveRoom &event);
|
||||
|
@ -81,4 +90,21 @@ public:
|
|||
void sendRoomCommand(PendingCommand *pend);
|
||||
};
|
||||
|
||||
class CustomLineEdit : public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QString cursorWord(const QString& line) const;
|
||||
QCompleter* c;
|
||||
private slots:
|
||||
void insertCompletion(QString);
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent * event);
|
||||
void focusOutEvent(QFocusEvent * e);
|
||||
public:
|
||||
explicit CustomLineEdit(QWidget *parent = 0);
|
||||
void setCompleter(QCompleter*);
|
||||
void updateCompleterModel(QStringList);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -99,6 +99,7 @@ void UserContextMenu::banUser_dialogFinished()
|
|||
cmd.set_minutes(dlg->getMinutes());
|
||||
cmd.set_reason(dlg->getReason().toStdString());
|
||||
cmd.set_visible_reason(dlg->getVisibleReason().toStdString());
|
||||
cmd.set_clientid(dlg->getBanId().toStdString());
|
||||
|
||||
client->sendCommand(client->prepareModeratorCommand(cmd));
|
||||
}
|
||||
|
|
|
@ -36,11 +36,19 @@ BanDialog::BanDialog(const ServerInfo_User &info, QWidget *parent)
|
|||
ipBanCheckBox = new QCheckBox(tr("ban &IP address"));
|
||||
ipBanCheckBox->setChecked(true);
|
||||
ipBanEdit = new QLineEdit(QString::fromStdString(info.address()));
|
||||
idBanCheckBox = new QCheckBox(tr("ban client I&D"));
|
||||
idBanCheckBox->setChecked(true);
|
||||
idBanEdit = new QLineEdit(QString::fromStdString(info.clientid()));
|
||||
if (QString::fromStdString(info.clientid()).isEmpty())
|
||||
idBanCheckBox->setChecked(false);
|
||||
|
||||
QGridLayout *banTypeGrid = new QGridLayout;
|
||||
banTypeGrid->addWidget(nameBanCheckBox, 0, 0);
|
||||
banTypeGrid->addWidget(nameBanEdit, 0, 1);
|
||||
banTypeGrid->addWidget(ipBanCheckBox, 1, 0);
|
||||
banTypeGrid->addWidget(ipBanEdit, 1, 1);
|
||||
banTypeGrid->addWidget(idBanCheckBox, 2, 0);
|
||||
banTypeGrid->addWidget(idBanEdit, 2, 1);
|
||||
QGroupBox *banTypeGroupBox = new QGroupBox(tr("Ban type"));
|
||||
banTypeGroupBox->setLayout(banTypeGrid);
|
||||
|
||||
|
@ -110,8 +118,8 @@ BanDialog::BanDialog(const ServerInfo_User &info, QWidget *parent)
|
|||
|
||||
void BanDialog::okClicked()
|
||||
{
|
||||
if (!nameBanCheckBox->isChecked() && !ipBanCheckBox->isChecked()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("You have to select a name-based or IP-based ban, or both."));
|
||||
if (!nameBanCheckBox->isChecked() && !ipBanCheckBox->isChecked() && !idBanCheckBox->isChecked()) {
|
||||
QMessageBox::critical(this, tr("Error"), tr("You have to select a name-based, IP-based, clientId based, or some combination of the three to place a ban."));
|
||||
return;
|
||||
}
|
||||
accept();
|
||||
|
@ -127,6 +135,11 @@ void BanDialog::enableTemporaryEdits(bool enabled)
|
|||
minutesEdit->setEnabled(enabled);
|
||||
}
|
||||
|
||||
QString BanDialog::getBanId() const
|
||||
{
|
||||
return idBanCheckBox->isChecked() ? idBanEdit->text() : QString();
|
||||
}
|
||||
|
||||
QString BanDialog::getBanName() const
|
||||
{
|
||||
return nameBanCheckBox->isChecked() ? nameBanEdit->text() : QString();
|
||||
|
|
|
@ -24,8 +24,8 @@ class BanDialog : public QDialog {
|
|||
Q_OBJECT
|
||||
private:
|
||||
QLabel *daysLabel, *hoursLabel, *minutesLabel;
|
||||
QCheckBox *nameBanCheckBox, *ipBanCheckBox;
|
||||
QLineEdit *nameBanEdit, *ipBanEdit;
|
||||
QCheckBox *nameBanCheckBox, *ipBanCheckBox, *idBanCheckBox;
|
||||
QLineEdit *nameBanEdit, *ipBanEdit, *idBanEdit;
|
||||
QSpinBox *daysEdit, *hoursEdit, *minutesEdit;
|
||||
QRadioButton *permanentRadio, *temporaryRadio;
|
||||
QPlainTextEdit *reasonEdit, *visibleReasonEdit;
|
||||
|
@ -36,6 +36,7 @@ public:
|
|||
BanDialog(const ServerInfo_User &info, QWidget *parent = 0);
|
||||
QString getBanName() const;
|
||||
QString getBanIP() const;
|
||||
QString getBanId() const;
|
||||
int getMinutes() const;
|
||||
QString getReason() const;
|
||||
QString getVisibleReason() const;
|
||||
|
|
|
@ -319,6 +319,9 @@ void MainWindow::loginError(Response::ResponseCode r, QString reasonStr, quint32
|
|||
case Response::RespClientIdRequired:
|
||||
QMessageBox::critical(this, tr("Error"), tr("This server requires client ID's. Your client is either failing to generate an ID or you are running a modified client.\nPlease close and reopen your client to try again."));
|
||||
break;
|
||||
case Response::RespContextError:
|
||||
QMessageBox::critical(this, tr("Error"), tr("An internal error has occurred, please try closing and reopening your client and try again. If the error persists try updating your client to the most recent build and if need be contact your software provider."));
|
||||
break;
|
||||
case Response::RespAccountNotActivated: {
|
||||
bool ok = false;
|
||||
QString token = QInputDialog::getText(this, tr("Account activation"), tr("Your account has not been activated yet.\nYou need to provide the activation token received in the activation email"), QLineEdit::Normal, QString(), &ok);
|
||||
|
|
|
@ -14,4 +14,5 @@ message Command_BanFromServer {
|
|||
optional uint32 minutes = 3;
|
||||
optional string reason = 4;
|
||||
optional string visible_reason = 5;
|
||||
optional string clientid = 6;
|
||||
}
|
||||
|
|
|
@ -23,4 +23,5 @@ message ServerInfo_User {
|
|||
optional uint64 session_id = 10;
|
||||
optional uint64 accountage_secs = 11;
|
||||
optional string email = 12;
|
||||
optional string clientid = 13;
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ message Command_Register {
|
|||
// Country code of the user. 2 letter ISO format
|
||||
optional string country = 5;
|
||||
optional string real_name = 6;
|
||||
optional string clientid = 7;
|
||||
}
|
||||
|
||||
// User wants to activate an account
|
||||
|
|
|
@ -46,7 +46,7 @@ Server::Server(bool _threaded, QObject *parent)
|
|||
qRegisterMetaType<GameEventContainer>("GameEventContainer");
|
||||
qRegisterMetaType<IslMessage>("IslMessage");
|
||||
qRegisterMetaType<Command_JoinGame>("Command_JoinGame");
|
||||
|
||||
|
||||
connect(this, SIGNAL(sigSendIslMessage(IslMessage, int)), this, SLOT(doSendIslMessage(IslMessage, int)), Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
@ -62,9 +62,9 @@ void Server::prepareDestroy()
|
|||
for (int i = 0; i < clients.size(); ++i)
|
||||
QMetaObject::invokeMethod(clients.at(i), "prepareDestroy", Qt::QueuedConnection);
|
||||
clientsLock.unlock();
|
||||
|
||||
|
||||
bool done = false;
|
||||
|
||||
|
||||
class SleeperThread : public QThread
|
||||
{
|
||||
public:
|
||||
|
@ -83,7 +83,7 @@ void Server::prepareDestroy()
|
|||
while (!clients.isEmpty())
|
||||
clients.first()->prepareDestroy();
|
||||
}
|
||||
|
||||
|
||||
roomsLock.lockForWrite();
|
||||
QMapIterator<int, Server_Room *> roomIterator(rooms);
|
||||
while (roomIterator.hasNext())
|
||||
|
@ -107,21 +107,21 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
|
|||
{
|
||||
if (name.size() > 35)
|
||||
name = name.left(35);
|
||||
|
||||
|
||||
Server_DatabaseInterface *databaseInterface = getDatabaseInterface();
|
||||
|
||||
|
||||
QWriteLocker locker(&clientsLock);
|
||||
|
||||
AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, reasonStr, secondsLeft);
|
||||
|
||||
AuthenticationResult authState = databaseInterface->checkUserPassword(session, name, password, clientid, reasonStr, secondsLeft);
|
||||
if (authState == NotLoggedIn || authState == UserIsBanned || authState == UsernameInvalid || authState == UserIsInactive)
|
||||
return authState;
|
||||
|
||||
|
||||
ServerInfo_User data = databaseInterface->getUserData(name, true);
|
||||
data.set_address(session->getAddress().toStdString());
|
||||
name = QString::fromStdString(data.name()); // Compensate for case indifference
|
||||
|
||||
|
||||
databaseInterface->lockSessionTables();
|
||||
|
||||
|
||||
if (authState == PasswordRight) {
|
||||
|
||||
// verify that new session would not cause problems with older existing session
|
||||
|
@ -133,8 +133,7 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
|
|||
} else if (authState == UnknownUser) {
|
||||
// Change user name so that no two users have the same names,
|
||||
// don't interfere with registered user names though.
|
||||
bool requireReg = databaseInterface->getRequireRegistration();
|
||||
if (requireReg) {
|
||||
if (getRegOnlyServer()) {
|
||||
qDebug("Login denied: registration required");
|
||||
databaseInterface->unlockSessionTables();
|
||||
return RegistrationRequired;
|
||||
|
@ -147,18 +146,18 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
|
|||
name = tempName;
|
||||
data.set_name(name.toStdString());
|
||||
}
|
||||
|
||||
|
||||
users.insert(name, session);
|
||||
qDebug() << "Server::loginUser:" << session << "name=" << name;
|
||||
|
||||
data.set_session_id(databaseInterface->startSession(name, session->getAddress()));
|
||||
|
||||
data.set_session_id(databaseInterface->startSession(name, session->getAddress(), clientid));
|
||||
databaseInterface->unlockSessionTables();
|
||||
|
||||
|
||||
usersBySessionId.insert(data.session_id(), session);
|
||||
|
||||
|
||||
qDebug() << "session id:" << data.session_id();
|
||||
session->setUserInfo(data);
|
||||
|
||||
|
||||
Event_UserJoined event;
|
||||
event.mutable_user_info()->CopyFrom(session->copyUserInfo(false));
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
|
@ -166,10 +165,10 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
|
|||
if (clients[i]->getAcceptsUserListChanges())
|
||||
clients[i]->sendProtocolItem(*se);
|
||||
delete se;
|
||||
|
||||
|
||||
event.mutable_user_info()->CopyFrom(session->copyUserInfo(true, true, true));
|
||||
locker.unlock();
|
||||
|
||||
|
||||
if (clientid.isEmpty()){
|
||||
// client id is empty, either out dated client or client has been modified
|
||||
if (getClientIdRequired())
|
||||
|
@ -180,10 +179,11 @@ AuthenticationResult Server::loginUser(Server_ProtocolHandler *session, QString
|
|||
databaseInterface->updateUsersClientID(name, clientid);
|
||||
}
|
||||
|
||||
databaseInterface->updateUsersLastLoginTime(name);
|
||||
se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
sendIsl_SessionEvent(*se);
|
||||
delete se;
|
||||
|
||||
|
||||
return authState;
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ QList<PlayerReference> Server::getPersistentPlayerReferences(const QString &user
|
|||
Server_AbstractUserInterface *Server::findUser(const QString &userName) const
|
||||
{
|
||||
// Call this only with clientsLock set.
|
||||
|
||||
|
||||
Server_AbstractUserInterface *userHandler = users.value(userName);
|
||||
if (userHandler)
|
||||
return userHandler;
|
||||
|
@ -236,10 +236,10 @@ void Server::removeClient(Server_ProtocolHandler *client)
|
|||
clients[i]->sendProtocolItem(*se);
|
||||
sendIsl_SessionEvent(*se);
|
||||
delete se;
|
||||
|
||||
|
||||
users.remove(QString::fromStdString(data->name()));
|
||||
qDebug() << "Server::removeClient: name=" << QString::fromStdString(data->name());
|
||||
|
||||
|
||||
if (data->has_session_id()) {
|
||||
const qint64 sessionId = data->session_id();
|
||||
usersBySessionId.remove(sessionId);
|
||||
|
@ -254,21 +254,21 @@ void Server::externalUserJoined(const ServerInfo_User &userInfo)
|
|||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
clientsLock.lockForWrite();
|
||||
|
||||
|
||||
Server_RemoteUserInterface *newUser = new Server_RemoteUserInterface(this, ServerInfo_User_Container(userInfo));
|
||||
externalUsers.insert(QString::fromStdString(userInfo.name()), newUser);
|
||||
externalUsersBySessionId.insert(userInfo.session_id(), newUser);
|
||||
|
||||
|
||||
Event_UserJoined event;
|
||||
event.mutable_user_info()->CopyFrom(userInfo);
|
||||
|
||||
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
for (int i = 0; i < clients.size(); ++i)
|
||||
if (clients[i]->getAcceptsUserListChanges())
|
||||
clients[i]->sendProtocolItem(*se);
|
||||
delete se;
|
||||
clientsLock.unlock();
|
||||
|
||||
|
||||
ResponseContainer rc(-1);
|
||||
newUser->joinPersistentGames(rc);
|
||||
newUser->sendResponseContainer(rc, Response::RespNothing);
|
||||
|
@ -277,12 +277,12 @@ void Server::externalUserJoined(const ServerInfo_User &userInfo)
|
|||
void Server::externalUserLeft(const QString &userName)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
|
||||
clientsLock.lockForWrite();
|
||||
Server_AbstractUserInterface *user = externalUsers.take(userName);
|
||||
externalUsersBySessionId.remove(user->getUserInfo()->session_id());
|
||||
clientsLock.unlock();
|
||||
|
||||
|
||||
QMap<int, QPair<int, int> > userGames(user->getGames());
|
||||
QMapIterator<int, QPair<int, int> > userGamesIterator(userGames);
|
||||
roomsLock.lockForRead();
|
||||
|
@ -291,26 +291,26 @@ void Server::externalUserLeft(const QString &userName)
|
|||
Server_Room *room = rooms.value(userGamesIterator.value().first);
|
||||
if (!room)
|
||||
continue;
|
||||
|
||||
|
||||
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||
Server_Game *game = room->getGames().value(userGamesIterator.key());
|
||||
if (!game)
|
||||
continue;
|
||||
|
||||
|
||||
QMutexLocker gameLocker(&game->gameMutex);
|
||||
Server_Player *player = game->getPlayers().value(userGamesIterator.value().second);
|
||||
if (!player)
|
||||
continue;
|
||||
|
||||
|
||||
player->disconnectClient();
|
||||
}
|
||||
roomsLock.unlock();
|
||||
|
||||
|
||||
delete user;
|
||||
|
||||
|
||||
Event_UserLeft event;
|
||||
event.set_name(userName.toStdString());
|
||||
|
||||
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
clientsLock.lockForRead();
|
||||
for (int i = 0; i < clients.size(); ++i)
|
||||
|
@ -324,7 +324,7 @@ void Server::externalRoomUserJoined(int roomId, const ServerInfo_User &userInfo)
|
|||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalRoomUserJoined: room id=" << roomId << "not found";
|
||||
|
@ -337,7 +337,7 @@ void Server::externalRoomUserLeft(int roomId, const QString &userName)
|
|||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalRoomUserLeft: room id=" << roomId << "not found";
|
||||
|
@ -350,7 +350,7 @@ void Server::externalRoomSay(int roomId, const QString &userName, const QString
|
|||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalRoomSay: room id=" << roomId << "not found";
|
||||
|
@ -365,7 +365,7 @@ void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &game
|
|||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
QReadLocker locker(&roomsLock);
|
||||
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalRoomGameListChanged: room id=" << roomId << "not found";
|
||||
|
@ -377,11 +377,11 @@ void Server::externalRoomGameListChanged(int roomId, const ServerInfo_Game &game
|
|||
void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cmdId, int roomId, int serverId, qint64 sessionId)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
|
||||
try {
|
||||
QReadLocker roomsLocker(&roomsLock);
|
||||
QReadLocker clientsLocker(&clientsLock);
|
||||
|
||||
|
||||
Server_Room *room = rooms.value(roomId);
|
||||
if (!room) {
|
||||
qDebug() << "externalJoinGameCommandReceived: room id=" << roomId << "not found";
|
||||
|
@ -392,7 +392,7 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm
|
|||
qDebug() << "externalJoinGameCommandReceived: session id=" << sessionId << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
|
||||
ResponseContainer responseContainer(cmdId);
|
||||
Response::ResponseCode responseCode = room->processJoinGameCommand(cmd, responseContainer, userInterface);
|
||||
userInterface->sendResponseContainer(responseContainer, responseCode);
|
||||
|
@ -400,7 +400,7 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm
|
|||
Response response;
|
||||
response.set_cmd_id(cmdId);
|
||||
response.set_response_code(code);
|
||||
|
||||
|
||||
sendIsl_Response(response, serverId, sessionId);
|
||||
}
|
||||
}
|
||||
|
@ -408,44 +408,44 @@ void Server::externalJoinGameCommandReceived(const Command_JoinGame &cmd, int cm
|
|||
void Server::externalGameCommandContainerReceived(const CommandContainer &cont, int playerId, int serverId, qint64 sessionId)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
|
||||
try {
|
||||
ResponseContainer responseContainer(cont.cmd_id());
|
||||
Response::ResponseCode finalResponseCode = Response::RespOk;
|
||||
|
||||
|
||||
QReadLocker roomsLocker(&roomsLock);
|
||||
Server_Room *room = rooms.value(cont.room_id());
|
||||
if (!room) {
|
||||
qDebug() << "externalGameCommandContainerReceived: room id=" << cont.room_id() << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
|
||||
QReadLocker roomGamesLocker(&room->gamesLock);
|
||||
Server_Game *game = room->getGames().value(cont.game_id());
|
||||
if (!game) {
|
||||
qDebug() << "externalGameCommandContainerReceived: game id=" << cont.game_id() << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
|
||||
QMutexLocker gameLocker(&game->gameMutex);
|
||||
Server_Player *player = game->getPlayers().value(playerId);
|
||||
if (!player) {
|
||||
qDebug() << "externalGameCommandContainerReceived: player id=" << playerId << "not found";
|
||||
throw Response::RespNotInRoom;
|
||||
}
|
||||
|
||||
|
||||
GameEventStorage ges;
|
||||
for (int i = cont.game_command_size() - 1; i >= 0; --i) {
|
||||
const GameCommand &sc = cont.game_command(i);
|
||||
qDebug() << "[ISL]" << QString::fromStdString(sc.ShortDebugString());
|
||||
|
||||
|
||||
Response::ResponseCode resp = player->processGameCommand(sc, responseContainer, ges);
|
||||
|
||||
|
||||
if (resp != Response::RespOk)
|
||||
finalResponseCode = resp;
|
||||
}
|
||||
ges.sendToGame(game);
|
||||
|
||||
|
||||
if (finalResponseCode != Response::RespNothing) {
|
||||
player->playerMutex.lock();
|
||||
player->getUserInterface()->sendResponseContainer(responseContainer, finalResponseCode);
|
||||
|
@ -455,7 +455,7 @@ void Server::externalGameCommandContainerReceived(const CommandContainer &cont,
|
|||
Response response;
|
||||
response.set_cmd_id(cont.cmd_id());
|
||||
response.set_response_code(code);
|
||||
|
||||
|
||||
sendIsl_Response(response, serverId, sessionId);
|
||||
}
|
||||
}
|
||||
|
@ -463,9 +463,9 @@ void Server::externalGameCommandContainerReceived(const CommandContainer &cont,
|
|||
void Server::externalGameEventContainerReceived(const GameEventContainer &cont, qint64 sessionId)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
|
||||
QReadLocker usersLocker(&clientsLock);
|
||||
|
||||
|
||||
Server_ProtocolHandler *client = usersBySessionId.value(sessionId);
|
||||
if (!client) {
|
||||
qDebug() << "externalGameEventContainerReceived: session" << sessionId << "not found";
|
||||
|
@ -477,9 +477,9 @@ void Server::externalGameEventContainerReceived(const GameEventContainer &cont,
|
|||
void Server::externalResponseReceived(const Response &resp, qint64 sessionId)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
|
||||
QReadLocker usersLocker(&clientsLock);
|
||||
|
||||
|
||||
Server_ProtocolHandler *client = usersBySessionId.value(sessionId);
|
||||
if (!client) {
|
||||
qDebug() << "externalResponseReceived: session" << sessionId << "not found";
|
||||
|
@ -491,10 +491,10 @@ void Server::externalResponseReceived(const Response &resp, qint64 sessionId)
|
|||
void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl)
|
||||
{
|
||||
// This function is always called from the main thread via signal/slot.
|
||||
|
||||
|
||||
Event_ListRooms event;
|
||||
event.add_room_list()->CopyFrom(roomInfo);
|
||||
|
||||
|
||||
SessionEvent *se = Server_ProtocolHandler::prepareSessionEvent(event);
|
||||
|
||||
clientsLock.lockForRead();
|
||||
|
@ -502,10 +502,10 @@ void Server::broadcastRoomUpdate(const ServerInfo_Room &roomInfo, bool sendToIsl
|
|||
if (clients[i]->getAcceptsRoomListChanges())
|
||||
clients[i]->sendProtocolItem(*se);
|
||||
clientsLock.unlock();
|
||||
|
||||
|
||||
if (sendToIsl)
|
||||
sendIsl_SessionEvent(*se);
|
||||
|
||||
|
||||
delete se;
|
||||
}
|
||||
|
||||
|
@ -543,7 +543,7 @@ void Server::sendIsl_Response(const Response &item, int serverId, qint64 session
|
|||
if (sessionId != -1)
|
||||
msg.set_session_id(sessionId);
|
||||
msg.mutable_response()->CopyFrom(item);
|
||||
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
|
@ -554,7 +554,7 @@ void Server::sendIsl_SessionEvent(const SessionEvent &item, int serverId, qint64
|
|||
if (sessionId != -1)
|
||||
msg.set_session_id(sessionId);
|
||||
msg.mutable_session_event()->CopyFrom(item);
|
||||
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
|
@ -565,7 +565,7 @@ void Server::sendIsl_GameEventContainer(const GameEventContainer &item, int serv
|
|||
if (sessionId != -1)
|
||||
msg.set_session_id(sessionId);
|
||||
msg.mutable_game_event_container()->CopyFrom(item);
|
||||
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
|
@ -576,7 +576,7 @@ void Server::sendIsl_RoomEvent(const RoomEvent &item, int serverId, qint64 sessi
|
|||
if (sessionId != -1)
|
||||
msg.set_session_id(sessionId);
|
||||
msg.mutable_room_event()->CopyFrom(item);
|
||||
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
|
@ -586,11 +586,11 @@ void Server::sendIsl_GameCommand(const CommandContainer &item, int serverId, qin
|
|||
msg.set_message_type(IslMessage::GAME_COMMAND_CONTAINER);
|
||||
msg.set_session_id(sessionId);
|
||||
msg.set_player_id(playerId);
|
||||
|
||||
|
||||
CommandContainer *cont = msg.mutable_game_command();
|
||||
cont->CopyFrom(item);
|
||||
cont->set_room_id(roomId);
|
||||
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
||||
|
@ -599,10 +599,10 @@ void Server::sendIsl_RoomCommand(const CommandContainer &item, int serverId, qin
|
|||
IslMessage msg;
|
||||
msg.set_message_type(IslMessage::ROOM_COMMAND_CONTAINER);
|
||||
msg.set_session_id(sessionId);
|
||||
|
||||
|
||||
CommandContainer *cont = msg.mutable_room_command();
|
||||
cont->CopyFrom(item);
|
||||
cont->set_room_id(roomId);
|
||||
|
||||
|
||||
emit sigSendIslMessage(msg, serverId);
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
|
||||
virtual bool getGameShouldPing() const { return false; }
|
||||
virtual bool getClientIdRequired() const { return false; }
|
||||
virtual bool getRegOnlyServer() const { return false; }
|
||||
virtual int getPingClockInterval() const { return 0; }
|
||||
virtual int getMaxGameInactivityTime() const { return 9999999; }
|
||||
virtual int getMaxPlayerInactivityTime() const { return 9999999; }
|
||||
|
|
|
@ -12,8 +12,8 @@ public:
|
|||
Server_DatabaseInterface(QObject *parent = 0)
|
||||
: QObject(parent) { }
|
||||
|
||||
virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &secondsLeft) = 0;
|
||||
virtual bool checkUserIsBanned(const QString & /* ipAddress */, const QString & /* userName */, QString & /* banReason */, int & /* banSecondsRemaining */) { return false; }
|
||||
virtual AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft) = 0;
|
||||
virtual bool checkUserIsBanned(const QString & /* ipAddress */, const QString & /* userName */, const QString & /* clientId */, QString & /* banReason */, int & /* banSecondsRemaining */) { return false; }
|
||||
virtual bool activeUserExists(const QString & /* user */) { return false; }
|
||||
virtual bool userExists(const QString & /* user */) { return false; }
|
||||
virtual QMap<QString, ServerInfo_User> getBuddyList(const QString & /* name */) { return QMap<QString, ServerInfo_User>(); }
|
||||
|
@ -24,7 +24,7 @@ public:
|
|||
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 */, int /* userId */) { return 0; }
|
||||
|
||||
virtual qint64 startSession(const QString & /* userName */, const QString & /* address */) { return 0; }
|
||||
virtual qint64 startSession(const QString & /* userName */, const QString & /* address */, const QString & /* clientId */) { return 0; }
|
||||
virtual bool usernameIsValid(const QString & /*userName */, QString & /* error */) { return true; };
|
||||
public slots:
|
||||
virtual void endSession(qint64 /* sessionId */ ) { }
|
||||
|
@ -42,6 +42,7 @@ public:
|
|||
virtual bool registerUser(const QString & /* userName */, const QString & /* realName */, ServerInfo_User_Gender const & /* gender */, const QString & /* password */, const QString & /* emailAddress */, const QString & /* country */, bool /* active = false */) { return false; }
|
||||
virtual bool activateUser(const QString & /* userName */, const QString & /* token */) { return false; }
|
||||
virtual void updateUsersClientID(const QString & /* userName */, const QString & /* userClientID */) { }
|
||||
virtual void updateUsersLastLoginTime(const QString & /* userName */) { }
|
||||
|
||||
enum LogMessage_TargetType { MessageTargetRoom, MessageTargetGame, MessageTargetChat, MessageTargetIslRoom };
|
||||
virtual void logMessage(const int /* senderId */, const QString & /* senderName */, const QString & /* senderIp */, const QString & /* logMessage */, LogMessage_TargetType /* targetType */, const int /* targetId */, const QString & /* targetName */) { };
|
||||
|
|
|
@ -385,8 +385,6 @@ Response::ResponseCode Server_ProtocolHandler::cmdLogin(const Command_Login &cmd
|
|||
|
||||
if (userName.isEmpty() || (userInfo != 0))
|
||||
return Response::RespContextError;
|
||||
if (clientId.isEmpty())
|
||||
return Response::RespContextError;
|
||||
|
||||
QString reasonStr;
|
||||
int banSecondsLeft = 0;
|
||||
|
|
|
@ -36,6 +36,7 @@ ServerInfo_User &ServerInfo_User_Container::copyUserInfo(ServerInfo_User &result
|
|||
if (!sessionInfo) {
|
||||
result.clear_session_id();
|
||||
result.clear_address();
|
||||
result.clear_clientid();
|
||||
}
|
||||
if (!internalInfo)
|
||||
{
|
||||
|
|
5
servatrice/migrations/servatrice_0002_to_0003.sql
Normal file
5
servatrice/migrations/servatrice_0002_to_0003.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
-- Servatrice db migration from version 2 to version 3
|
||||
|
||||
alter table cockatrice_users add clientid varchar(15) not null;
|
||||
|
||||
UPDATE cockatrice_schema_version SET version=3 WHERE version=2;
|
5
servatrice/migrations/servatrice_0003_to_0004.sql
Normal file
5
servatrice/migrations/servatrice_0003_to_0004.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
-- Servatrice db migration from version 3 to version 4
|
||||
|
||||
alter table cockatrice_sessions add clientid varchar(15) not null;
|
||||
|
||||
UPDATE cockatrice_schema_version SET version=4 WHERE version=3;
|
5
servatrice/migrations/servatrice_0004_to_0005.sql
Normal file
5
servatrice/migrations/servatrice_0004_to_0005.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
-- Servatrice db migration from version 4 to version 5
|
||||
|
||||
alter table cockatrice_bans add clientid varchar(15) not null;
|
||||
|
||||
UPDATE cockatrice_schema_version SET version=5 WHERE version=4;
|
5
servatrice/migrations/servatrice_0005_to_0006.sql
Normal file
5
servatrice/migrations/servatrice_0005_to_0006.sql
Normal file
|
@ -0,0 +1,5 @@
|
|||
-- Servatrice db migration from version 5 to version 6
|
||||
|
||||
alter table cockatrice_users add last_login datetime not null;
|
||||
|
||||
UPDATE cockatrice_schema_version SET version=6 WHERE version=5;
|
36
servatrice/scripts/linux/maint_countrycodes
Normal file
36
servatrice/scripts/linux/maint_countrycodes
Normal file
|
@ -0,0 +1,36 @@
|
|||
#!/bin/bash
|
||||
|
||||
# THIS SCRIPT EXPECTS TO BE EXECUTED FROM THE GITHUB SOURCE FOLDER PATH STRUCTURE
|
||||
# OTHERWISE, UPDATE THE 'COUNTRYCODEIMAGEPATH' TO POINT TO THE FOLDER CONTAINING THE COUNTRY CODE IMAGES
|
||||
# USE THIS SCRIPT TO COMPARE EXISTING USER ACCOUNTS TO VALID COUNTRY CODES AND CLEAR INVALID COUNTRY CODE DATA
|
||||
|
||||
MODE="report" #set this to correct to fix invalid country codes, otherwise it only reports
|
||||
DBNAME="servatrice" #set this to the database name used
|
||||
TABLEPREFIX="cockatrice" #set this to the prefix used for the table names in the database (do not inclue the _)
|
||||
SQLCONFFILE="./mysql.cnf" #set this to the path that contains the mysql.cnf file
|
||||
COUNTRYCODEIMAGEPATH='../../../cockatrice/resources/countries'
|
||||
VALIDCOUNT=0
|
||||
INVALIDCOUNT=0
|
||||
|
||||
for i in `mysql --defaults-file=$SQLCONFFILE -h localhost -e "select distinct(country) from ""$DBNAME"".""$TABLEPREFIX""_users;"`
|
||||
do
|
||||
if [ "$i" != "country" ]; then
|
||||
if [ -f "$COUNTRYCODEIMAGEPATH/$i.svg" ]; then
|
||||
((VALIDCOUNT++))
|
||||
else
|
||||
((INVALIDCOUNT++))
|
||||
|
||||
if [ "$MODE" == "correct" ]; then
|
||||
echo "$i COUNTRY CODE INVALID, ATTEMPTING TO CORRECT"
|
||||
mysql --defaults-file=$SQLCONFFILE -h localhost -e "update ""$DBNAME"".""$TABLEPREFIX""_users set country = '' where country = '$i';"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$MODE" == "correct" ]; then
|
||||
mysql --defaults-file=$SQLCONFFILE -h localhost -e "update ""$DBNAME"".""$TABLEPREFIX""_users set country = lower(country);"
|
||||
fi
|
||||
|
||||
echo "INVALID: $INVALIDCOUNT"
|
||||
echo "VALID: $VALIDCOUNT"
|
|
@ -62,8 +62,8 @@ method=none
|
|||
; if the chosen authentication method is password, here you can define the password your users will use to log in
|
||||
password=123456
|
||||
|
||||
; Accept only registered users? default is 0 (accept unregistered users)
|
||||
regonly=0
|
||||
; Accept only registered users? default is false (accept unregistered users)
|
||||
regonly=false
|
||||
|
||||
[users]
|
||||
|
||||
|
@ -193,6 +193,11 @@ roomlist\1\game_types\3\name="GameType3"
|
|||
; default is 120
|
||||
max_game_inactivity_time=120
|
||||
|
||||
; All actions during a game are recorded and stored in the database as a replay that all participants of
|
||||
; the game can go back to and review after the game is closed. This can require a fairly large amount of
|
||||
; storage to save all the information. Disable this option to prevent the storing of replay data in
|
||||
; the database. Default value is true.
|
||||
store_replays=true
|
||||
|
||||
[security]
|
||||
; You may want to restrict the number of users that can connect to your server at any given time.
|
||||
|
|
|
@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_schema_version` (
|
|||
PRIMARY KEY (`version`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO cockatrice_schema_version VALUES(3);
|
||||
INSERT INTO cockatrice_schema_version VALUES(6);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `cockatrice_decklist_files` (
|
||||
`id` int(7) unsigned zerofill NOT NULL auto_increment,
|
||||
|
@ -84,6 +84,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_users` (
|
|||
`active` tinyint(1) NOT NULL,
|
||||
`token` binary(16) NOT NULL,
|
||||
`clientid` varchar(15) NOT NULL,
|
||||
`last_login` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `name` (`name`),
|
||||
KEY `token` (`token`),
|
||||
|
@ -132,6 +133,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_bans` (
|
|||
`minutes` int(6) NOT NULL,
|
||||
`reason` text NOT NULL,
|
||||
`visible_reason` text NOT NULL,
|
||||
`clientid` varchar(15) NOT NULL,
|
||||
PRIMARY KEY (`user_name`,`time_from`),
|
||||
KEY `time_from` (`time_from`,`ip_address`),
|
||||
KEY `ip_address` (`ip_address`)
|
||||
|
@ -144,6 +146,7 @@ CREATE TABLE IF NOT EXISTS `cockatrice_sessions` (
|
|||
`ip_address` char(15) COLLATE utf8_unicode_ci NOT NULL,
|
||||
`start_time` datetime NOT NULL,
|
||||
`end_time` datetime DEFAULT NULL,
|
||||
`clientid` varchar(15) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `username` (`user_name`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
|
||||
|
|
|
@ -143,7 +143,7 @@ bool Servatrice::initServer()
|
|||
serverName = settingsCache->value("server/name", "My Cockatrice server").toString();
|
||||
serverId = settingsCache->value("server/id", 0).toInt();
|
||||
clientIdRequired = settingsCache->value("server/requireclientid",0).toBool();
|
||||
bool regServerOnly = settingsCache->value("authentication/regonly", 0).toBool();
|
||||
regServerOnly = settingsCache->value("authentication/regonly", 0).toBool();
|
||||
|
||||
const QString authenticationMethodStr = settingsCache->value("authentication/method").toString();
|
||||
if (authenticationMethodStr == "sql") {
|
||||
|
@ -161,7 +161,8 @@ bool Servatrice::initServer()
|
|||
qDebug() << "Authenticating method: none";
|
||||
authenticationMethod = AuthenticationNone;
|
||||
}
|
||||
|
||||
|
||||
qDebug() << "Store Replays: " << settingsCache->value("game/store_replays", true).toBool();
|
||||
qDebug() << "Client ID Required: " << clientIdRequired;
|
||||
bool maxUserLimitEnabled = settingsCache->value("security/enable_max_user_limit", false).toBool();
|
||||
qDebug() << "Maximum user limit enabled: " << maxUserLimitEnabled;
|
||||
|
@ -174,7 +175,7 @@ bool Servatrice::initServer()
|
|||
bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
|
||||
bool requireEmailForRegistration = settingsCache->value("registration/requireemail", true).toBool();
|
||||
|
||||
qDebug() << "Registration enabled: " << registrationEnabled;
|
||||
qDebug() << "Registration enabled: " << regServerOnly;
|
||||
if (registrationEnabled)
|
||||
qDebug() << "Require email address to register: " << requireEmailForRegistration;
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ private:
|
|||
QString shutdownReason;
|
||||
int shutdownMinutes;
|
||||
QTimer *shutdownTimer;
|
||||
bool isFirstShutdownMessage, clientIdRequired;
|
||||
bool isFirstShutdownMessage, clientIdRequired, regServerOnly;
|
||||
|
||||
mutable QMutex serverListMutex;
|
||||
QList<ServerProperties> serverList;
|
||||
|
@ -138,6 +138,7 @@ public:
|
|||
QString getLoginMessage() const { QMutexLocker locker(&loginMessageMutex); return loginMessage; }
|
||||
bool getGameShouldPing() const { return true; }
|
||||
bool getClientIdRequired() const { return clientIdRequired; }
|
||||
bool getRegOnlyServer() const { return regServerOnly; }
|
||||
int getPingClockInterval() const { return pingClockInterval; }
|
||||
int getMaxGameInactivityTime() const { return maxGameInactivityTime; }
|
||||
int getMaxPlayerInactivityTime() const { return maxPlayerInactivityTime; }
|
||||
|
|
|
@ -44,7 +44,7 @@ bool Servatrice_DatabaseInterface::initDatabase(const QString &type, const QStri
|
|||
sqlDatabase.setDatabaseName(databaseName);
|
||||
sqlDatabase.setUserName(userName);
|
||||
sqlDatabase.setPassword(password);
|
||||
|
||||
|
||||
return openDatabase();
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ bool Servatrice_DatabaseInterface::openDatabase()
|
|||
{
|
||||
if (sqlDatabase.isOpen())
|
||||
sqlDatabase.close();
|
||||
|
||||
|
||||
const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId);
|
||||
qDebug() << QString("[%1] Opening database...").arg(poolStr);
|
||||
if (!sqlDatabase.open()) {
|
||||
|
@ -92,7 +92,7 @@ bool Servatrice_DatabaseInterface::checkSql()
|
|||
{
|
||||
if (!sqlDatabase.isValid())
|
||||
return false;
|
||||
|
||||
|
||||
if (!sqlDatabase.exec("select 1").isActive())
|
||||
return openDatabase();
|
||||
return true;
|
||||
|
@ -152,12 +152,6 @@ bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString
|
|||
return re.exactMatch(user);
|
||||
}
|
||||
|
||||
// TODO move this to Server
|
||||
bool Servatrice_DatabaseInterface::getRequireRegistration()
|
||||
{
|
||||
return settingsCache->value("authentication/regonly", 0).toBool();
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender, const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active)
|
||||
{
|
||||
if (!checkSql())
|
||||
|
@ -235,7 +229,7 @@ QChar Servatrice_DatabaseInterface::getGenderChar(ServerInfo_User_Gender const &
|
|||
}
|
||||
}
|
||||
|
||||
AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, QString &reasonStr, int &banSecondsLeft)
|
||||
AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &banSecondsLeft)
|
||||
{
|
||||
switch (server->getAuthenticationMethod()) {
|
||||
case Servatrice::AuthenticationNone: return UnknownUser;
|
||||
|
@ -252,23 +246,23 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
|
|||
|
||||
if (!usernameIsValid(user, reasonStr))
|
||||
return UsernameInvalid;
|
||||
|
||||
if (checkUserIsBanned(handler->getAddress(), user, reasonStr, banSecondsLeft))
|
||||
|
||||
if (checkUserIsBanned(handler->getAddress(), user, clientId, reasonStr, banSecondsLeft))
|
||||
return UserIsBanned;
|
||||
|
||||
|
||||
QSqlQuery *passwordQuery = prepareQuery("select password_sha512, active from {prefix}_users where name = :name");
|
||||
passwordQuery->bindValue(":name", user);
|
||||
if (!execSqlQuery(passwordQuery)) {
|
||||
qDebug("Login denied: SQL error");
|
||||
return NotLoggedIn;
|
||||
}
|
||||
|
||||
|
||||
if (passwordQuery->next()) {
|
||||
const QString correctPassword = passwordQuery->value(0).toString();
|
||||
const bool userIsActive = passwordQuery->value(1).toBool();
|
||||
if(!userIsActive) {
|
||||
qDebug("Login denied: user not active");
|
||||
return UserIsInactive;
|
||||
return UserIsInactive;
|
||||
}
|
||||
if (correctPassword == PasswordHasher::computeHash(password, correctPassword.left(16))) {
|
||||
qDebug("Login accepted: password right");
|
||||
|
@ -286,7 +280,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
|
|||
return UnknownUser;
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining)
|
||||
bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining)
|
||||
{
|
||||
if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql)
|
||||
return false;
|
||||
|
@ -297,11 +291,48 @@ bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress, c
|
|||
}
|
||||
|
||||
return
|
||||
checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining)
|
||||
|| checkUserIsNameBanned(userName, banReason, banSecondsRemaining);
|
||||
checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) || checkUserIsNameBanned(userName, banReason, banSecondsRemaining) || checkUserIsIdBanned(clientId, banReason, banSecondsRemaining);
|
||||
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId, QString &banReason, int &banSecondsRemaining)
|
||||
{
|
||||
if (clientId.isEmpty())
|
||||
return false;
|
||||
|
||||
QSqlQuery *idBanQuery = prepareQuery(
|
||||
"select"
|
||||
" timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)),"
|
||||
" b.minutes <=> 0,"
|
||||
" b.visible_reason"
|
||||
" from {prefix}_bans b"
|
||||
" where"
|
||||
" b.time_from = (select max(c.time_from)"
|
||||
" from {prefix}_bans c"
|
||||
" where c.clientid = :id)"
|
||||
" and b.clientid = :id2");
|
||||
|
||||
idBanQuery->bindValue(":id", clientId);
|
||||
idBanQuery->bindValue(":id2", clientId);
|
||||
if (!execSqlQuery(idBanQuery)) {
|
||||
qDebug() << "Id ban check failed: SQL error." << idBanQuery->lastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idBanQuery->next()) {
|
||||
const int secondsLeft = idBanQuery->value(0).toInt();
|
||||
const bool permanentBan = idBanQuery->value(1).toInt();
|
||||
if ((secondsLeft > 0) || permanentBan) {
|
||||
banReason = idBanQuery->value(2).toString();
|
||||
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
|
||||
qDebug() << "User is banned by client id" << clientId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName, QString &banReason, int &banSecondsRemaining)
|
||||
{
|
||||
QSqlQuery *nameBanQuery = prepareQuery("select timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)), b.minutes <=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from {prefix}_bans c where c.user_name = :name2) and b.user_name = :name1");
|
||||
|
@ -363,7 +394,7 @@ bool Servatrice_DatabaseInterface::activeUserExists(const QString &user)
|
|||
{
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
||||
checkSql();
|
||||
|
||||
|
||||
QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name and active = 1");
|
||||
query->bindValue(":name", user);
|
||||
if (!execSqlQuery(query))
|
||||
|
@ -377,7 +408,7 @@ bool Servatrice_DatabaseInterface::userExists(const QString &user)
|
|||
{
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
||||
checkSql();
|
||||
|
||||
|
||||
QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name");
|
||||
query->bindValue(":name", user);
|
||||
if (!execSqlQuery(query))
|
||||
|
@ -405,13 +436,13 @@ bool Servatrice_DatabaseInterface::isInBuddyList(const QString &whoseList, const
|
|||
{
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
|
||||
return false;
|
||||
|
||||
|
||||
if (!checkSql())
|
||||
return false;
|
||||
|
||||
|
||||
int id1 = getUserIdInDB(whoseList);
|
||||
int id2 = getUserIdInDB(who);
|
||||
|
||||
|
||||
QSqlQuery *query = prepareQuery("select 1 from {prefix}_buddylist where id_user1 = :id_user1 and id_user2 = :id_user2");
|
||||
query->bindValue(":id_user1", id1);
|
||||
query->bindValue(":id_user2", id2);
|
||||
|
@ -424,13 +455,13 @@ bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, cons
|
|||
{
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
|
||||
return false;
|
||||
|
||||
|
||||
if (!checkSql())
|
||||
return false;
|
||||
|
||||
|
||||
int id1 = getUserIdInDB(whoseList);
|
||||
int id2 = getUserIdInDB(who);
|
||||
|
||||
|
||||
QSqlQuery *query = prepareQuery("select 1 from {prefix}_ignorelist where id_user1 = :id_user1 and id_user2 = :id_user2");
|
||||
query->bindValue(":id_user1", id1);
|
||||
query->bindValue(":id_user2", id2);
|
||||
|
@ -442,11 +473,11 @@ bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, cons
|
|||
ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId)
|
||||
{
|
||||
ServerInfo_User result;
|
||||
|
||||
|
||||
if (withId)
|
||||
result.set_id(query->value(0).toInt());
|
||||
result.set_name(query->value(1).toString().toStdString());
|
||||
|
||||
|
||||
const int is_admin = query->value(2).toInt();
|
||||
int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered;
|
||||
if (is_admin == 1)
|
||||
|
@ -483,6 +514,10 @@ ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuer
|
|||
const QString email = query->value(8).toString();
|
||||
if (!email.isEmpty())
|
||||
result.set_email(email.toStdString());
|
||||
|
||||
const QString clientid = query->value(9).toString();
|
||||
if (!clientid.isEmpty())
|
||||
result.set_clientid(clientid.toStdString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -492,16 +527,16 @@ ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, b
|
|||
ServerInfo_User result;
|
||||
result.set_name(name.toStdString());
|
||||
result.set_user_level(ServerInfo_User::IsUser);
|
||||
|
||||
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
||||
if (!checkSql())
|
||||
return result;
|
||||
|
||||
QSqlQuery *query = prepareQuery("select id, name, admin, country, gender, realname, avatar_bmp, registrationDate, email from {prefix}_users where name = :name and active = 1");
|
||||
|
||||
QSqlQuery *query = prepareQuery("select id, name, admin, country, gender, realname, avatar_bmp, registrationDate, email, clientid from {prefix}_users where name = :name and active = 1");
|
||||
query->bindValue(":name", name);
|
||||
if (!execSqlQuery(query))
|
||||
return result;
|
||||
|
||||
|
||||
if (query->next())
|
||||
return evalUserQueryResult(query, true, withId);
|
||||
else
|
||||
|
@ -534,7 +569,7 @@ void Servatrice_DatabaseInterface::unlockSessionTables()
|
|||
bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName)
|
||||
{
|
||||
// Call only after lockSessionTables().
|
||||
|
||||
|
||||
QSqlQuery *query = prepareQuery("select 1 from {prefix}_sessions where user_name = :user_name and id_server = :id_server and end_time is null");
|
||||
query->bindValue(":id_server", server->getServerId());
|
||||
query->bindValue(":user_name", userName);
|
||||
|
@ -542,18 +577,19 @@ bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName)
|
|||
return query->next();
|
||||
}
|
||||
|
||||
qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, const QString &address)
|
||||
qint64 Servatrice_DatabaseInterface::startSession(const QString &userName, const QString &address, const QString &clientId)
|
||||
{
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
|
||||
return -1;
|
||||
|
||||
|
||||
if (!checkSql())
|
||||
return -1;
|
||||
|
||||
QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time) values(:user_name, :id_server, :ip_address, NOW())");
|
||||
|
||||
QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time, clientid) values(:user_name, :id_server, :ip_address, NOW(), :client_id)");
|
||||
query->bindValue(":user_name", userName);
|
||||
query->bindValue(":id_server", server->getServerId());
|
||||
query->bindValue(":ip_address", address);
|
||||
query->bindValue(":client_id", clientId);
|
||||
if (execSqlQuery(query))
|
||||
return query->lastInsertId().toInt();
|
||||
return -1;
|
||||
|
@ -563,13 +599,13 @@ void Servatrice_DatabaseInterface::endSession(qint64 sessionId)
|
|||
{
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
|
||||
return;
|
||||
|
||||
|
||||
if (!checkSql())
|
||||
return;
|
||||
|
||||
QSqlQuery *query = prepareQuery("lock tables {prefix}_sessions write");
|
||||
execSqlQuery(query);
|
||||
|
||||
|
||||
query = prepareQuery("update {prefix}_sessions set end_time=NOW() where id = :id_session");
|
||||
query->bindValue(":id_session", sessionId);
|
||||
execSqlQuery(query);
|
||||
|
@ -581,7 +617,7 @@ void Servatrice_DatabaseInterface::endSession(qint64 sessionId)
|
|||
QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getBuddyList(const QString &name)
|
||||
{
|
||||
QMap<QString, ServerInfo_User> result;
|
||||
|
||||
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
||||
checkSql();
|
||||
|
||||
|
@ -589,7 +625,7 @@ QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getBuddyList(const
|
|||
query->bindValue(":name", name);
|
||||
if (!execSqlQuery(query))
|
||||
return result;
|
||||
|
||||
|
||||
while (query->next()) {
|
||||
const ServerInfo_User &temp = evalUserQueryResult(query, false);
|
||||
result.insert(QString::fromStdString(temp.name()), temp);
|
||||
|
@ -601,7 +637,7 @@ QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getBuddyList(const
|
|||
QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getIgnoreList(const QString &name)
|
||||
{
|
||||
QMap<QString, ServerInfo_User> result;
|
||||
|
||||
|
||||
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
||||
checkSql();
|
||||
|
||||
|
@ -609,7 +645,7 @@ QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getIgnoreList(const
|
|||
query->bindValue(":name", name);
|
||||
if (!execSqlQuery(query))
|
||||
return result;
|
||||
|
||||
|
||||
while (query->next()) {
|
||||
ServerInfo_User temp = evalUserQueryResult(query, false);
|
||||
result.insert(QString::fromStdString(temp.name()), temp);
|
||||
|
@ -622,13 +658,13 @@ int Servatrice_DatabaseInterface::getNextGameId()
|
|||
{
|
||||
if (!sqlDatabase.isValid())
|
||||
return server->getNextLocalGameId();
|
||||
|
||||
|
||||
if (!checkSql())
|
||||
return -1;
|
||||
|
||||
|
||||
QSqlQuery *query = prepareQuery("insert into {prefix}_games (time_started) values (now())");
|
||||
execSqlQuery(query);
|
||||
|
||||
|
||||
return query->lastInsertId().toInt();
|
||||
}
|
||||
|
||||
|
@ -636,10 +672,10 @@ int Servatrice_DatabaseInterface::getNextReplayId()
|
|||
{
|
||||
if (!checkSql())
|
||||
return -1;
|
||||
|
||||
|
||||
QSqlQuery *query = prepareQuery("insert into {prefix}_replays () values ()");
|
||||
execSqlQuery(query);
|
||||
|
||||
|
||||
return query->lastInsertId().toInt();
|
||||
}
|
||||
|
||||
|
@ -647,7 +683,10 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
|
|||
{
|
||||
if (!checkSql())
|
||||
return;
|
||||
|
||||
|
||||
if (!settingsCache->value("game/store_replays", 1).toBool() )
|
||||
return;
|
||||
|
||||
QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames;
|
||||
QSetIterator<QString> playerIterator(allPlayersEver);
|
||||
while (playerIterator.hasNext()) {
|
||||
|
@ -665,20 +704,20 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
|
|||
userIds.append(id);
|
||||
replayNames.append(QString::fromStdString(gameInfo.description()));
|
||||
}
|
||||
|
||||
|
||||
QVariantList replayIds, replayGameIds, replayDurations, replayBlobs;
|
||||
for (int i = 0; i < replayList.size(); ++i) {
|
||||
QByteArray blob;
|
||||
const unsigned int size = replayList[i]->ByteSize();
|
||||
blob.resize(size);
|
||||
replayList[i]->SerializeToArray(blob.data(), size);
|
||||
|
||||
|
||||
replayIds.append(QVariant((qulonglong) replayList[i]->replay_id()));
|
||||
replayGameIds.append(gameInfo.game_id());
|
||||
replayDurations.append(replayList[i]->duration_seconds());
|
||||
replayBlobs.append(blob);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
QSqlQuery *query = prepareQuery("update {prefix}_games set room_name=:room_name, descr=:descr, creator_name=:creator_name, password=:password, game_types=:game_types, player_count=:player_count, time_finished=now() where id=:id_game");
|
||||
query->bindValue(":room_name", roomName);
|
||||
|
@ -717,17 +756,17 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
|
|||
DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int userId)
|
||||
{
|
||||
checkSql();
|
||||
|
||||
|
||||
QSqlQuery *query = prepareQuery("select content from {prefix}_decklist_files where id = :id and id_user = :id_user");
|
||||
query->bindValue(":id", deckId);
|
||||
query->bindValue(":id_user", userId);
|
||||
execSqlQuery(query);
|
||||
if (!query->next())
|
||||
throw Response::RespNameNotFound;
|
||||
|
||||
|
||||
DeckList *deck = new DeckList;
|
||||
deck->loadFromString_Native(query->value(0).toString());
|
||||
|
||||
|
||||
return deck;
|
||||
}
|
||||
|
||||
|
@ -789,7 +828,7 @@ bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user, const
|
|||
qDebug("Change password denied: SQL error");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (!passwordQuery->next())
|
||||
return true;
|
||||
|
||||
|
@ -831,7 +870,7 @@ int Servatrice_DatabaseInterface::getActiveUserCount()
|
|||
|
||||
void Servatrice_DatabaseInterface::updateUsersClientID(const QString &userName, const QString &userClientID)
|
||||
{
|
||||
|
||||
|
||||
if (!checkSql())
|
||||
return;
|
||||
|
||||
|
@ -839,5 +878,16 @@ void Servatrice_DatabaseInterface::updateUsersClientID(const QString &userName,
|
|||
query->bindValue(":clientid", userClientID);
|
||||
query->bindValue(":username", userName);
|
||||
execSqlQuery(query);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Servatrice_DatabaseInterface::updateUsersLastLoginTime(const QString &userName)
|
||||
{
|
||||
if (!checkSql())
|
||||
return;
|
||||
|
||||
QSqlQuery *query = prepareQuery("update {prefix}_users set last_login = NOW() where name = :user_name");
|
||||
query->bindValue(":user_name", userName);
|
||||
execSqlQuery(query);
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include "server.h"
|
||||
#include "server_database_interface.h"
|
||||
|
||||
#define DATABASE_SCHEMA_VERSION 3
|
||||
#define DATABASE_SCHEMA_VERSION 6
|
||||
|
||||
class Servatrice;
|
||||
|
||||
|
@ -22,13 +22,14 @@ private:
|
|||
Servatrice *server;
|
||||
ServerInfo_User evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId = false);
|
||||
/** Must be called after checkSql and server is known to be in auth mode. */
|
||||
bool checkUserIsIdBanned(const QString &clientId, QString &banReason, int &banSecondsRemaining);
|
||||
/** Must be called after checkSql and server is known to be in auth mode. */
|
||||
bool checkUserIsIpBanned(const QString &ipAddress, QString &banReason, int &banSecondsRemaining);
|
||||
/** Must be called after checkSql and server is known to be in auth mode. */
|
||||
bool checkUserIsNameBanned(QString const &userName, QString &banReason, int &banSecondsRemaining);
|
||||
|
||||
protected:
|
||||
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user,
|
||||
const QString &password, QString &reasonStr, int &secondsLeft);
|
||||
AuthenticationResult checkUserPassword(Server_ProtocolHandler *handler, const QString &user, const QString &password, const QString &clientId, QString &reasonStr, int &secondsLeft);
|
||||
|
||||
public slots:
|
||||
void initDatabase(const QSqlDatabase &_sqlDatabase);
|
||||
|
@ -36,7 +37,7 @@ public slots:
|
|||
public:
|
||||
Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server);
|
||||
~Servatrice_DatabaseInterface();
|
||||
bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName,
|
||||
bool initDatabase(const QString &type, const QString &hostName, const QString &databaseName,
|
||||
const QString &userName, const QString &password);
|
||||
bool openDatabase();
|
||||
bool checkSql();
|
||||
|
@ -52,28 +53,28 @@ public:
|
|||
bool isInBuddyList(const QString &whoseList, const QString &who);
|
||||
bool isInIgnoreList(const QString &whoseList, const QString &who);
|
||||
ServerInfo_User getUserData(const QString &name, bool withId = false);
|
||||
void storeGameInformation(const QString &roomName, const QStringList &roomGameTypes, const ServerInfo_Game &gameInfo,
|
||||
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, int userId);
|
||||
|
||||
int getNextGameId();
|
||||
int getNextReplayId();
|
||||
int getActiveUserCount();
|
||||
qint64 startSession(const QString &userName, const QString &address);
|
||||
qint64 startSession(const QString &userName, const QString &address, const QString &clientId);
|
||||
void endSession(qint64 sessionId);
|
||||
void clearSessionTables();
|
||||
void lockSessionTables();
|
||||
void unlockSessionTables();
|
||||
bool userSessionExists(const QString &userName);
|
||||
bool usernameIsValid(const QString &user, QString & error);
|
||||
bool checkUserIsBanned(const QString &ipAddress, const QString &userName, QString &banReason, int &banSecondsRemaining);
|
||||
bool checkUserIsBanned(const QString &ipAddress, const QString &userName, const QString &clientId, QString &banReason, int &banSecondsRemaining);
|
||||
|
||||
bool getRequireRegistration();
|
||||
bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender,
|
||||
bool registerUser(const QString &userName, const QString &realName, ServerInfo_User_Gender const &gender,
|
||||
const QString &password, const QString &emailAddress, const QString &country, QString &token, bool active = false);
|
||||
bool activateUser(const QString &userName, const QString &token);
|
||||
void updateUsersClientID(const QString &userName, const QString &userClientID);
|
||||
void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage,
|
||||
void updateUsersLastLoginTime(const QString &userName);
|
||||
void logMessage(const int senderId, const QString &senderName, const QString &senderIp, const QString &logMessage,
|
||||
LogMessage_TargetType targetType, const int targetId, const QString &targetName);
|
||||
bool changeUserPassword(const QString &user, const QString &oldPassword, const QString &newPassword);
|
||||
QChar getGenderChar(ServerInfo_User_Gender const &gender);
|
||||
|
|
|
@ -756,20 +756,40 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
|
|||
if (trustedSources.contains(address,Qt::CaseInsensitive))
|
||||
address = "";
|
||||
|
||||
QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason)");
|
||||
QSqlQuery *query = sqlInterface->prepareQuery("insert into {prefix}_bans (user_name, ip_address, id_admin, time_from, minutes, reason, visible_reason, clientid) values(:user_name, :ip_address, :id_admin, NOW(), :minutes, :reason, :visible_reason, :client_id)");
|
||||
query->bindValue(":user_name", userName);
|
||||
query->bindValue(":ip_address", address);
|
||||
query->bindValue(":id_admin", userInfo->id());
|
||||
query->bindValue(":minutes", minutes);
|
||||
query->bindValue(":reason", QString::fromStdString(cmd.reason()));
|
||||
query->bindValue(":visible_reason", QString::fromStdString(cmd.visible_reason()));
|
||||
query->bindValue(":client_id", QString::fromStdString(cmd.clientid()));
|
||||
sqlInterface->execSqlQuery(query);
|
||||
|
||||
servatrice->clientsLock.lockForRead();
|
||||
QList<ServerSocketInterface *> userList = servatrice->getUsersWithAddressAsList(QHostAddress(address));
|
||||
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
|
||||
if (user && !userList.contains(user))
|
||||
|
||||
if (!userName.isEmpty()) {
|
||||
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
|
||||
userList.append(user);
|
||||
}
|
||||
|
||||
if (userName.isEmpty() && address.isEmpty()) {
|
||||
QSqlQuery *query = sqlInterface->prepareQuery("select name from {prefix}_users where clientid = :client_id");
|
||||
query->bindValue(":client_id", QString::fromStdString(cmd.clientid()));
|
||||
sqlInterface->execSqlQuery(query);
|
||||
if (!sqlInterface->execSqlQuery(query)){
|
||||
qDebug("ClientID username ban lookup failed: SQL Error");
|
||||
} else {
|
||||
while (query->next()) {
|
||||
userName = query->value(0).toString();
|
||||
ServerSocketInterface *user = static_cast<ServerSocketInterface *>(server->getUsers().value(userName));
|
||||
if (user && !userList.contains(user))
|
||||
userList.append(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!userList.isEmpty()) {
|
||||
Event_ConnectionClosed event;
|
||||
event.set_reason(Event_ConnectionClosed::BANNED);
|
||||
|
@ -792,6 +812,7 @@ Response::ResponseCode ServerSocketInterface::cmdBanFromServer(const Command_Ban
|
|||
Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_Register &cmd, ResponseContainer &rc)
|
||||
{
|
||||
QString userName = QString::fromStdString(cmd.user_name());
|
||||
QString clientId = QString::fromStdString(cmd.clientid());
|
||||
qDebug() << "Got register command: " << userName;
|
||||
|
||||
bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
|
||||
|
@ -822,7 +843,7 @@ Response::ResponseCode ServerSocketInterface::cmdRegisterAccount(const Command_R
|
|||
|
||||
QString banReason;
|
||||
int banSecondsRemaining;
|
||||
if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, banReason, banSecondsRemaining))
|
||||
if (sqlInterface->checkUserIsBanned(this->getAddress(), userName, clientId, banReason, banSecondsRemaining))
|
||||
{
|
||||
Response_Register *re = new Response_Register;
|
||||
re->set_denied_reason_str(banReason.toStdString());
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#define QXT_VERSION 0x000602
|
||||
#define QXT_VERSION_STR "0.6.2"
|
||||
#define QXT_STATIC
|
||||
|
||||
//--------------------------global macros------------------------------
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
#include <QSharedDataPointer>
|
||||
#include <QIODevice>
|
||||
|
||||
class QxtMailAttachmentPrivate;
|
||||
struct QxtMailAttachmentPrivate;
|
||||
class QXT_NETWORK_EXPORT QxtMailAttachment
|
||||
{
|
||||
public:
|
||||
|
|
Loading…
Reference in a new issue