diff --git a/common/server_protocolhandler.cpp b/common/server_protocolhandler.cpp index 36cb4831..942d4508 100644 --- a/common/server_protocolhandler.cpp +++ b/common/server_protocolhandler.cpp @@ -353,6 +353,7 @@ ResponseCode Server_ProtocolHandler::cmdJoinRoom(Command_JoinRoom *cmd, CommandC return RespNameNotFound; r->addClient(this); + connect(r, SIGNAL(gameCreated(Server_Game *)), this, SLOT(gameCreated(Server_Game *))); rooms.insert(r->getId(), r); enqueueProtocolItem(new Event_RoomSay(r->getId(), QString(), r->getJoinMessage())); @@ -420,13 +421,17 @@ ResponseCode Server_ProtocolHandler::cmdCreateGame(Command_CreateGame *cmd, Comm for (int i = 0; i < gameTypeList.size(); ++i) gameTypes.append(gameTypeList[i]->getData()); - Server_Game *game = room->createGame(cmd->getDescription(), cmd->getPassword(), cmd->getMaxPlayers(), gameTypes, cmd->getOnlyBuddies(), cmd->getOnlyRegistered(), cmd->getSpectatorsAllowed(), cmd->getSpectatorsNeedPassword(), cmd->getSpectatorsCanTalk(), cmd->getSpectatorsSeeEverything(), this); + room->createGame(cmd->getDescription(), cmd->getPassword(), cmd->getMaxPlayers(), gameTypes, cmd->getOnlyBuddies(), cmd->getOnlyRegistered(), cmd->getSpectatorsAllowed(), cmd->getSpectatorsNeedPassword(), cmd->getSpectatorsCanTalk(), cmd->getSpectatorsSeeEverything(), this); + return RespOk; +} + +void Server_ProtocolHandler::gameCreated(Server_Game *game) +{ Server_Player *creator = game->getPlayers().values().first(); games.insert(game->getGameId(), QPair(game, creator)); - enqueueProtocolItem(new Event_GameJoined(game->getGameId(), game->getDescription(), creator->getPlayerId(), false, game->getSpectatorsCanTalk(), game->getSpectatorsSeeEverything(), false)); - enqueueProtocolItem(GameEventContainer::makeNew(new Event_GameStateChanged(game->getGameStarted(), game->getActivePlayer(), game->getActivePhase(), game->getGameState(creator)), game->getGameId())); - return RespOk; + sendProtocolItem(new Event_GameJoined(game->getGameId(), game->getDescription(), creator->getPlayerId(), false, game->getSpectatorsCanTalk(), game->getSpectatorsSeeEverything(), false)); + sendProtocolItem(GameEventContainer::makeNew(new Event_GameStateChanged(game->getGameStarted(), game->getActivePlayer(), game->getActivePhase(), game->getGameState(creator)), game->getGameId())); } ResponseCode Server_ProtocolHandler::cmdJoinGame(Command_JoinGame *cmd, CommandContainer * /*cont*/, Server_Room *room) diff --git a/common/server_protocolhandler.h b/common/server_protocolhandler.h index 677ba095..e8b4dfac 100644 --- a/common/server_protocolhandler.h +++ b/common/server_protocolhandler.h @@ -91,6 +91,7 @@ private: ResponseCode processCommandHelper(Command *command, CommandContainer *cont); private slots: void pingClockTimeout(); + void gameCreated(Server_Game *game); public: Server_ProtocolHandler(Server *_server, QObject *parent = 0); ~Server_ProtocolHandler(); diff --git a/common/server_room.cpp b/common/server_room.cpp index fe7b83ee..4e918218 100644 --- a/common/server_room.cpp +++ b/common/server_room.cpp @@ -6,6 +6,7 @@ Server_Room::Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent) : QObject(parent), id(_id), name(_name), description(_description), autoJoin(_autoJoin), joinMessage(_joinMessage), gameTypes(_gameTypes) { + connect(this, SIGNAL(sigCreateGame(const QString &, const QString &, int, const QList &, bool, bool, bool, bool, bool, bool, Server_ProtocolHandler *)), this, SLOT(doCreateGame(const QString &, const QString &, int, const QList &, bool, bool, bool, bool, bool, bool, Server_ProtocolHandler *))); } Server *Server_Room::getServer() const @@ -68,7 +69,7 @@ void Server_Room::broadcastGameListUpdate(Server_Game *game) delete event; } -Server_Game *Server_Room::createGame(const QString &description, const QString &password, int maxPlayers, const QList &gameTypes, bool onlyBuddies, bool onlyRegistered, bool spectatorsAllowed, bool spectatorsNeedPassword, bool spectatorsCanTalk, bool spectatorsSeeEverything, Server_ProtocolHandler *creator) +void Server_Room::doCreateGame(const QString &description, const QString &password, int maxPlayers, const QList &gameTypes, bool onlyBuddies, bool onlyRegistered, bool spectatorsAllowed, bool spectatorsNeedPassword, bool spectatorsCanTalk, bool spectatorsSeeEverything, Server_ProtocolHandler *creator) { Server_Game *newGame = new Server_Game(creator, static_cast(parent())->getNextGameId(), description, password, maxPlayers, gameTypes, onlyBuddies, onlyRegistered, spectatorsAllowed, spectatorsNeedPassword, spectatorsCanTalk, spectatorsSeeEverything, this); games.insert(newGame->getGameId(), newGame); @@ -78,8 +79,11 @@ Server_Game *Server_Room::createGame(const QString &description, const QString & emit gameCreated(newGame); emit roomInfoChanged(); - - return newGame; +} + +void Server_Room::createGame(const QString &description, const QString &password, int maxPlayers, const QList &gameTypes, bool onlyBuddies, bool onlyRegistered, bool spectatorsAllowed, bool spectatorsNeedPassword, bool spectatorsCanTalk, bool spectatorsSeeEverything, Server_ProtocolHandler *creator) +{ + emit sigCreateGame(description, password, maxPlayers, gameTypes, onlyBuddies, onlyRegistered, spectatorsAllowed, spectatorsNeedPassword, spectatorsCanTalk, spectatorsSeeEverything, creator); } void Server_Room::removeGame() diff --git a/common/server_room.h b/common/server_room.h index 8571f04e..0cd2b8b4 100644 --- a/common/server_room.h +++ b/common/server_room.h @@ -19,6 +19,8 @@ signals: void roomInfoChanged(); void gameCreated(Server_Game *game); void gameClosing(int gameId); + + void sigCreateGame(const QString &description, const QString &password, int maxPlayers, const QList &_gameTypes, bool onlyBuddies, bool onlyRegistered, bool spectatorsAllowed, bool spectatorsNeedPassword, bool spectatorsCanTalk, bool spectatorsSeeEverything, Server_ProtocolHandler *creator); private: int id; QString name; @@ -28,6 +30,7 @@ private: QStringList gameTypes; QMap games; private slots: + void doCreateGame(const QString &description, const QString &password, int maxPlayers, const QList &_gameTypes, bool onlyBuddies, bool onlyRegistered, bool spectatorsAllowed, bool spectatorsNeedPassword, bool spectatorsCanTalk, bool spectatorsSeeEverything, Server_ProtocolHandler *creator); void removeGame(); public: Server_Room(int _id, const QString &_name, const QString &_description, bool _autoJoin, const QString &_joinMessage, const QStringList &_gameTypes, Server *parent); @@ -44,7 +47,7 @@ public: void removeClient(Server_ProtocolHandler *client); void say(Server_ProtocolHandler *client, const QString &s); void broadcastGameListUpdate(Server_Game *game); - Server_Game *createGame(const QString &description, const QString &password, int maxPlayers, const QList &_gameTypes, bool onlyBuddies, bool onlyRegistered, bool spectatorsAllowed, bool spectatorsNeedPassword, bool spectatorsCanTalk, bool spectatorsSeeEverything, Server_ProtocolHandler *creator); + void createGame(const QString &description, const QString &password, int maxPlayers, const QList &_gameTypes, bool onlyBuddies, bool onlyRegistered, bool spectatorsAllowed, bool spectatorsNeedPassword, bool spectatorsCanTalk, bool spectatorsSeeEverything, Server_ProtocolHandler *creator); void sendRoomEvent(RoomEvent *event); }; diff --git a/servatrice/servatrice.pro b/servatrice/servatrice.pro index 86f1d960..a8de8ad3 100755 --- a/servatrice/servatrice.pro +++ b/servatrice/servatrice.pro @@ -17,6 +17,7 @@ HEADERS += src/main.h \ src/servatrice.h \ src/serversocketinterface.h \ src/server_logger.h \ + src/serversocketthread.h \ ../common/color.h \ ../common/serializable_item.h \ ../common/decklist.h \ @@ -40,6 +41,7 @@ SOURCES += src/main.cpp \ src/servatrice.cpp \ src/serversocketinterface.cpp \ src/server_logger.cpp \ + src/serversocketthread.cpp \ ../common/serializable_item.cpp \ ../common/decklist.cpp \ ../common/protocol.cpp \ diff --git a/servatrice/src/main.cpp b/servatrice/src/main.cpp index 8dcd95fb..9c4c25e5 100644 --- a/servatrice/src/main.cpp +++ b/servatrice/src/main.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "servatrice.h" #include "server_logger.h" #include "rng_sfmt.h" @@ -65,14 +67,27 @@ void testRNG() std::cerr << std::endl << std::endl; } +void myMessageOutput(QtMsgType /*type*/, const char *msg) +{ + logger->logMessage(msg); +} + int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); app.setOrganizationName("Cockatrice"); app.setApplicationName("Servatrice"); + QStringList args = app.arguments(); + bool testRandom = args.contains("--test-random"); + + qRegisterMetaType >("QList"); + QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); - logger = new ServerLogger; + + QSettings *settings = new QSettings("servatrice.ini", QSettings::IniFormat); + logger = new ServerLogger(settings->value("server/logfile").toString()); + qInstallMsgHandler(myMessageOutput); #ifdef Q_OS_UNIX struct sigaction hup; hup.sa_handler = ServerLogger::hupSignalHandler; @@ -85,10 +100,11 @@ int main(int argc, char *argv[]) std::cerr << "Servatrice " << Servatrice::versionString.toStdString() << " starting." << std::endl; std::cerr << "-------------------------" << std::endl; - - testRNG(); - Servatrice server; + if (testRandom) + testRNG(); + + Servatrice server(settings); std::cerr << "-------------------------" << std::endl; std::cerr << "Server initialized." << std::endl; @@ -96,6 +112,7 @@ int main(int argc, char *argv[]) int retval = app.exec(); delete rng; + delete settings; return retval; } diff --git a/servatrice/src/servatrice.cpp b/servatrice/src/servatrice.cpp index 8558de40..5eb45bbd 100644 --- a/servatrice/src/servatrice.cpp +++ b/servatrice/src/servatrice.cpp @@ -24,10 +24,18 @@ #include "servatrice.h" #include "server_room.h" #include "serversocketinterface.h" +#include "serversocketthread.h" #include "protocol.h" -Servatrice::Servatrice(QObject *parent) - : Server(parent), uptime(0) +void Servatrice_TcpServer::incomingConnection(int socketDescriptor) +{ + ServerSocketThread *sst = new ServerSocketThread(socketDescriptor, server, this); + connect(sst, SIGNAL(clientAdded(ServerSocketInterface *)), this, SIGNAL(clientAdded(ServerSocketInterface *))); + sst->start(); +} + +Servatrice::Servatrice(QSettings *_settings, QObject *parent) + : Server(parent), dbMutex(QMutex::Recursive), settings(_settings), uptime(0) { pingClock = new QTimer(this); connect(pingClock, SIGNAL(timeout()), this, SIGNAL(pingClockTimeout())); @@ -38,7 +46,6 @@ Servatrice::Servatrice(QObject *parent) banTimeoutClock->start(60000); ProtocolItem::initializeHash(); - settings = new QSettings("servatrice.ini", QSettings::IniFormat, this); int statusUpdateTime = settings->value("server/statusupdate").toInt(); statusUpdateClock = new QTimer(this); @@ -48,8 +55,8 @@ Servatrice::Servatrice(QObject *parent) statusUpdateClock->start(statusUpdateTime); } - tcpServer = new QTcpServer(this); - connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection())); + tcpServer = new Servatrice_TcpServer(this); + connect(tcpServer, SIGNAL(clientAdded(ServerSocketInterface *)), this, SLOT(newConnection(ServerSocketInterface *))); int port = settings->value("server/port", 4747).toInt(); qDebug() << "Starting server on port" << port; tcpServer->listen(QHostAddress::Any, port); @@ -133,6 +140,7 @@ bool Servatrice::openDatabase() void Servatrice::checkSql() { + QMutexLocker locker(&dbMutex); if (!QSqlDatabase::database().exec("select 1").isActive()) openDatabase(); } @@ -145,15 +153,14 @@ bool Servatrice::execSqlQuery(QSqlQuery &query) return false; } -void Servatrice::newConnection() +void Servatrice::newConnection(ServerSocketInterface *client) { - QTcpSocket *socket = tcpServer->nextPendingConnection(); - ServerSocketInterface *ssi = new ServerSocketInterface(this, socket); - addClient(ssi); + addClient(client); } AuthenticationResult Servatrice::checkUserPassword(const QString &user, const QString &password) { + QMutexLocker locker(&dbMutex); const QString method = settings->value("authentication/method").toString(); if (method == "none") return UnknownUser; @@ -181,6 +188,7 @@ AuthenticationResult Servatrice::checkUserPassword(const QString &user, const QS bool Servatrice::userExists(const QString &user) { + QMutexLocker locker(&dbMutex); const QString method = settings->value("authentication/method").toString(); if (method == "sql") { checkSql(); @@ -219,6 +227,7 @@ ServerInfo_User *Servatrice::evalUserQueryResult(const QSqlQuery &query, bool co ServerInfo_User *Servatrice::getUserData(const QString &name) { + QMutexLocker locker(&dbMutex); const QString method = settings->value("authentication/method").toString(); if (method == "sql") { checkSql(); @@ -248,6 +257,7 @@ int Servatrice::getUsersWithAddress(const QHostAddress &address) const QMap Servatrice::getBuddyList(const QString &name) { + QMutexLocker locker(&dbMutex); QMap result; const QString method = settings->value("authentication/method").toString(); @@ -270,6 +280,7 @@ QMap Servatrice::getBuddyList(const QString &name) QMap Servatrice::getIgnoreList(const QString &name) { + QMutexLocker locker(&dbMutex); QMap result; const QString method = settings->value("authentication/method").toString(); @@ -318,6 +329,7 @@ void Servatrice::updateBanTimer() void Servatrice::updateLoginMessage() { + QMutexLocker locker(&dbMutex); checkSql(); QSqlQuery query; query.prepare("select message from " + dbPrefix + "_servermessages order by timest desc limit 1"); @@ -336,6 +348,7 @@ void Servatrice::updateLoginMessage() void Servatrice::statusUpdate() { + QMutexLocker locker(&dbMutex); uptime += statusUpdateClock->interval() / 1000; checkSql(); diff --git a/servatrice/src/servatrice.h b/servatrice/src/servatrice.h index c53b5338..5ac3afec 100644 --- a/servatrice/src/servatrice.h +++ b/servatrice/src/servatrice.h @@ -21,6 +21,7 @@ #define SERVATRICE_H #include +#include #include "server.h" class QSqlDatabase; @@ -28,16 +29,33 @@ class QSettings; class QSqlQuery; class QTimer; +class Servatrice; +class ServerSocketInterface; + +class Servatrice_TcpServer : public QTcpServer { + Q_OBJECT +private: + Servatrice *server; +public: + Servatrice_TcpServer(Servatrice *_server, QObject *parent = 0) + : QTcpServer(parent), server(_server) { } +protected: + void incomingConnection(int socketDescriptor); +signals: + void clientAdded(ServerSocketInterface *client); +}; + class Servatrice : public Server { Q_OBJECT private slots: - void newConnection(); + void newConnection(ServerSocketInterface *client); void statusUpdate(); void updateBanTimer(); public: + QMutex dbMutex; static const QString versionString; - Servatrice(QObject *parent = 0); + Servatrice(QSettings *_settings, QObject *parent = 0); ~Servatrice(); bool openDatabase(); void checkSql(); diff --git a/servatrice/src/server_logger.cpp b/servatrice/src/server_logger.cpp index 60a056af..b0665279 100644 --- a/servatrice/src/server_logger.cpp +++ b/servatrice/src/server_logger.cpp @@ -2,19 +2,27 @@ #include #include #include +#include +#include +#include +#ifdef Q_OS_UNIX #include #include +#endif -ServerLogger::ServerLogger(QObject *parent) +ServerLogger::ServerLogger(const QString &logFileName, QObject *parent) : QObject(parent) { - logFile = new QFile("server.log", this); - logFile->open(QIODevice::Append); + if (!logFileName.isEmpty()) { + logFile = new QFile(logFileName, this); + logFile->open(QIODevice::Append); #ifdef Q_OS_UNIX - ::socketpair(AF_UNIX, SOCK_STREAM, 0, sigHupFD); + ::socketpair(AF_UNIX, SOCK_STREAM, 0, sigHupFD); #endif - snHup = new QSocketNotifier(sigHupFD[1], QSocketNotifier::Read, this); - connect(snHup, SIGNAL(activated(int)), this, SLOT(handleSigHup())); + snHup = new QSocketNotifier(sigHupFD[1], QSocketNotifier::Read, this); + connect(snHup, SIGNAL(activated(int)), this, SLOT(handleSigHup())); + } else + logFile = 0; } ServerLogger::~ServerLogger() @@ -23,18 +31,30 @@ ServerLogger::~ServerLogger() void ServerLogger::logMessage(QString message) { + if (!logFile) + return; + + static QMutex mutex; + mutex.lock(); QTextStream stream(logFile); - stream << message << "\n"; + stream << QDateTime::currentDateTime().toString() << " " << ((void *) QThread::currentThread()) << " " << message << "\n"; + mutex.unlock(); } void ServerLogger::hupSignalHandler(int /*unused*/) { + if (!logFile) + return; + char a = 1; ::write(sigHupFD[0], &a, sizeof(a)); } void ServerLogger::handleSigHup() { + if (!logFile) + return; + snHup->setEnabled(false); char tmp; ::read(sigHupFD[1], &tmp, sizeof(tmp)); diff --git a/servatrice/src/server_logger.h b/servatrice/src/server_logger.h index a9bd3eec..c1fb7872 100644 --- a/servatrice/src/server_logger.h +++ b/servatrice/src/server_logger.h @@ -9,7 +9,7 @@ class QFile; class ServerLogger : public QObject { Q_OBJECT public: - ServerLogger(QObject *parent = 0); + ServerLogger(const QString &logFileName, QObject *parent = 0); ~ServerLogger(); static void hupSignalHandler(int unused); public slots: diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 115457b4..2a65e8b6 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -101,14 +101,18 @@ void ServerSocketInterface::catchSocketError(QAbstractSocket::SocketError socket void ServerSocketInterface::sendProtocolItem(ProtocolItem *item, bool deleteItem) { + static QMutex mutex; + mutex.lock(); item->write(xmlWriter); socket->flush(); + mutex.unlock(); if (deleteItem) delete item; } int ServerSocketInterface::getUserIdInDB(const QString &name) const { + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("select id from " + servatrice->getDbPrefix() + "_users where name = :name"); query.bindValue(":name", name); @@ -142,6 +146,7 @@ ResponseCode ServerSocketInterface::cmdAddToList(Command_AddToList *cmd, Command if (id1 == id2) return RespContextError; + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("insert into " + servatrice->getDbPrefix() + "_" + list + "list (id_user1, id_user2) values(:id1, :id2)"); query.bindValue(":id1", id1); @@ -180,6 +185,7 @@ ResponseCode ServerSocketInterface::cmdRemoveFromList(Command_RemoveFromList *cm if (id2 < 0) return RespNameNotFound; + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("delete from " + servatrice->getDbPrefix() + "_" + list + "list where id_user1 = :id1 and id_user2 = :id2"); query.bindValue(":id1", id1); @@ -206,6 +212,7 @@ int ServerSocketInterface::getDeckPathId(int basePathId, QStringList path) if (path[0].isEmpty()) return 0; + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("select id from " + servatrice->getDbPrefix() + "_decklist_folders where id_parent = :id_parent and name = :name and user = :user"); query.bindValue(":id_parent", basePathId); @@ -229,6 +236,7 @@ int ServerSocketInterface::getDeckPathId(const QString &path) bool ServerSocketInterface::deckListHelper(DeckList_Directory *folder) { + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("select id, name from " + servatrice->getDbPrefix() + "_decklist_folders where id_parent = :id_parent and user = :user"); query.bindValue(":id_parent", folder->getId()); @@ -288,6 +296,7 @@ ResponseCode ServerSocketInterface::cmdDeckNewDir(Command_DeckNewDir *cmd, Comma if (folderId == -1) return RespNameNotFound; + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_folders (id_parent, user, name) values(:id_parent, :user, :name)"); query.bindValue(":id_parent", folderId); @@ -302,6 +311,7 @@ void ServerSocketInterface::deckDelDirHelper(int basePathId) { servatrice->checkSql(); + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("select id from " + servatrice->getDbPrefix() + "_decklist_folders where id_parent = :id_parent"); @@ -340,6 +350,7 @@ ResponseCode ServerSocketInterface::cmdDeckDel(Command_DeckDel *cmd, CommandCont servatrice->checkSql(); + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("select id from " + servatrice->getDbPrefix() + "_decklist_files where id = :id and user = :user"); @@ -379,6 +390,7 @@ ResponseCode ServerSocketInterface::cmdDeckUpload(Command_DeckUpload *cmd, Comma if (deckName.isEmpty()) deckName = "Unnamed deck"; + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("insert into " + servatrice->getDbPrefix() + "_decklist_files (id_folder, user, name, upload_time, content) values(:id_folder, :user, :name, NOW(), :content)"); query.bindValue(":id_folder", folderId); @@ -395,6 +407,7 @@ DeckList *ServerSocketInterface::getDeckFromDatabase(int deckId) { servatrice->checkSql(); + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("select content from " + servatrice->getDbPrefix() + "_decklist_files where id = :id and user = :user"); @@ -447,6 +460,7 @@ ResponseCode ServerSocketInterface::cmdBanFromServer(Command_BanFromServer *cmd, if (user->getUserInfo()->getUserLevel() & ServerInfo_User::IsRegistered) { // Registered users can be banned by name. if (minutes == 0) { + QMutexLocker locker(&servatrice->dbMutex); QSqlQuery query; query.prepare("update " + servatrice->getDbPrefix() + "_users set banned=1 where name = :name"); query.bindValue(":name", userName);