diff --git a/cockatrice/src/abstractclient.cpp b/cockatrice/src/abstractclient.cpp new file mode 100644 index 000000000..bac380522 --- /dev/null +++ b/cockatrice/src/abstractclient.cpp @@ -0,0 +1,70 @@ +#include "abstractclient.h" +#include "protocol.h" +#include "protocol_items.h" + +AbstractClient::AbstractClient(QObject *parent) + : QObject(parent), status(StatusDisconnected) +{ +} + +AbstractClient::~AbstractClient() +{ +} + +void AbstractClient::processProtocolItem(ProtocolItem *item) +{ + ProtocolResponse *response = qobject_cast(item); + if (response) { + CommandContainer *cmdCont = pendingCommands.value(response->getCmdId(), 0); + if (!cmdCont) + return; + + pendingCommands.remove(cmdCont->getCmdId()); + cmdCont->processResponse(response); + delete response; + delete cmdCont; + + return; + } + + GenericEvent *genericEvent = qobject_cast(item); + if (genericEvent) { + switch (genericEvent->getItemId()) { + case ItemId_Event_ListGames: emit listGamesEventReceived(qobject_cast(item)); break; + case ItemId_Event_ServerMessage: emit serverMessageEventReceived(qobject_cast(item)); break; + case ItemId_Event_ListChatChannels: emit listChatChannelsEventReceived(qobject_cast(item)); break; + case ItemId_Event_GameJoined: emit gameJoinedEventReceived(qobject_cast(item)); break; + } + delete genericEvent; + return; + } + + GameEventContainer *gameEventContainer = qobject_cast(item); + if (gameEventContainer) { + emit gameEventContainerReceived(gameEventContainer); + delete gameEventContainer; + return; + } + + ChatEvent *chatEvent = qobject_cast(item); + if (chatEvent) { + emit chatEventReceived(chatEvent); + delete chatEvent; + return; + } +} + + +void AbstractClient::setStatus(const ClientStatus _status) +{ + if (_status != status) { + status = _status; + emit statusChanged(_status); + } +} + +void AbstractClient::sendCommand(Command *cmd) +{ + sendCommandContainer(new CommandContainer(QList() << cmd)); +} + diff --git a/cockatrice/src/abstractclient.h b/cockatrice/src/abstractclient.h new file mode 100644 index 000000000..47f378b64 --- /dev/null +++ b/cockatrice/src/abstractclient.h @@ -0,0 +1,60 @@ +#ifndef ABSTRACTCLIENT_H +#define ABSTRACTCLIENT_H + +#include +#include "protocol_datastructures.h" + +class Command; +class CommandContainer; +class ProtocolItem; +class ProtocolResponse; +class TopLevelProtocolItem; +class CommandContainer; +class ChatEvent; +class GameEventContainer; +class Event_ListGames; +class Event_ServerMessage; +class Event_ListChatChannels; +class Event_GameJoined; + +enum ClientStatus { + StatusDisconnected, + StatusDisconnecting, + StatusConnecting, + StatusAwaitingWelcome, + StatusLoggingIn, + StatusLoggedIn, +}; + +class AbstractClient : public QObject { + Q_OBJECT +signals: + void statusChanged(ClientStatus _status); + void serverError(ResponseCode resp); + + // Chat events + void chatEventReceived(ChatEvent *event); + // Game events + void gameEventContainerReceived(GameEventContainer *event); + // Generic events + void listGamesEventReceived(Event_ListGames *event); + void serverMessageEventReceived(Event_ServerMessage *event); + void listChatChannelsEventReceived(Event_ListChatChannels *event); + void gameJoinedEventReceived(Event_GameJoined *event); +protected slots: + void processProtocolItem(ProtocolItem *item); +protected: + QMap pendingCommands; + ClientStatus status; + QString userName, password; + void setStatus(ClientStatus _status); +public: + AbstractClient(QObject *parent = 0); + ~AbstractClient(); + + ClientStatus getStatus() const { return status; } + virtual void sendCommand(Command *cmd); + virtual void sendCommandContainer(CommandContainer *cont) = 0; +}; + +#endif \ No newline at end of file diff --git a/cockatrice/src/localclient.cpp b/cockatrice/src/localclient.cpp new file mode 100644 index 000000000..ad0dbb8d5 --- /dev/null +++ b/cockatrice/src/localclient.cpp @@ -0,0 +1,23 @@ +#include "localclient.h" +#include "localserverinterface.h" +#include "protocol.h" + +LocalClient::LocalClient(LocalServerInterface *_lsi, QObject *parent) + : AbstractClient(parent), lsi(_lsi) +{ + connect(lsi, SIGNAL(itemToClient(ProtocolItem *)), this, SLOT(itemFromServer(ProtocolItem *))); +} + +LocalClient::~LocalClient() +{ +} + +void LocalClient::sendCommandContainer(CommandContainer *cont) +{ + lsi->itemFromClient(cont); +} + +void LocalClient::itemFromServer(ProtocolItem *item) +{ + processProtocolItem(item); +} diff --git a/cockatrice/src/localclient.h b/cockatrice/src/localclient.h new file mode 100644 index 000000000..bc3ae50d4 --- /dev/null +++ b/cockatrice/src/localclient.h @@ -0,0 +1,24 @@ +#ifndef LOCALCLIENT_H +#define LOCALCLIENT_H + +#include "abstractclient.h" + +class LocalServerInterface; + +class LocalClient : public AbstractClient { + Q_OBJECT +private: + LocalServerInterface *lsi; +public: + LocalClient(LocalServerInterface *_lsi, QObject *parent = 0); + ~LocalClient(); + + void sendCommandContainer(CommandContainer *cont); + +private slots: + void itemFromServer(ProtocolItem *item); +signals: + void itemToServer(ProtocolItem *item); +}; + +#endif \ No newline at end of file diff --git a/cockatrice/src/localserver.cpp b/cockatrice/src/localserver.cpp new file mode 100644 index 000000000..dabf2c381 --- /dev/null +++ b/cockatrice/src/localserver.cpp @@ -0,0 +1,17 @@ +#include "localserver.h" +#include "localserverinterface.h" + +LocalServer::LocalServer(QObject *parent) + : Server(parent) +{ +} + +LocalServer::~LocalServer() +{ +} + +LocalServerInterface *LocalServer::newConnection() +{ + LocalServerInterface *lsi = new LocalServerInterface(this); + return lsi; +} diff --git a/cockatrice/src/localserver.h b/cockatrice/src/localserver.h new file mode 100644 index 000000000..a93b51ef2 --- /dev/null +++ b/cockatrice/src/localserver.h @@ -0,0 +1,22 @@ +#ifndef LOCALSERVER_H +#define LOCALSERVER_H + +#include "server.h" + +class LocalServerInterface; + +class LocalServer : public Server +{ + Q_OBJECT +public: + LocalServer(QObject *parent = 0); + ~LocalServer(); + AuthenticationResult checkUserPassword(const QString & /*user*/, const QString & /*password*/) { return UnknownUser; } + QString getLoginMessage() const { return QString(); } + int getMaxGameInactivityTime() const { return 9999999; } + int getMaxPlayerInactivityTime() const { return 9999999; } + + LocalServerInterface *newConnection(); +}; + +#endif \ No newline at end of file diff --git a/cockatrice/src/localserverinterface.cpp b/cockatrice/src/localserverinterface.cpp new file mode 100644 index 000000000..16960651b --- /dev/null +++ b/cockatrice/src/localserverinterface.cpp @@ -0,0 +1,22 @@ +#include "localserverinterface.h" +#include "localserver.h" + +LocalServerInterface::LocalServerInterface(LocalServer *_server) + : Server_ProtocolHandler(_server, _server) +{ +} + +LocalServerInterface::~LocalServerInterface() +{ +} + +bool LocalServerInterface::sendProtocolItem(ProtocolItem *item, bool deleteItem) +{ + emit itemToClient(item); + return false; +} + +void LocalServerInterface::itemFromClient(ProtocolItem *item) +{ + processCommandContainer(static_cast(item)); +} diff --git a/cockatrice/src/localserverinterface.h b/cockatrice/src/localserverinterface.h new file mode 100644 index 000000000..a057e487d --- /dev/null +++ b/cockatrice/src/localserverinterface.h @@ -0,0 +1,31 @@ +#ifndef LOCALSERVERINTERFACE_H +#define LOCALSERVERINTERFACE_H + +#include "server_protocolhandler.h" + +class LocalServer; + +class LocalServerInterface : public Server_ProtocolHandler +{ + Q_OBJECT +private: + DeckList *getDeckFromDatabase(int /*deckId*/) { return 0; } + ResponseCode cmdDeckList(Command_DeckList * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } + ResponseCode cmdDeckNewDir(Command_DeckNewDir * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } + ResponseCode cmdDeckDelDir(Command_DeckDelDir * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } + ResponseCode cmdDeckDel(Command_DeckDel * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } + ResponseCode cmdDeckUpload(Command_DeckUpload * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } + ResponseCode cmdDeckDownload(Command_DeckDownload * /*cmd*/, CommandContainer * /*cont*/) { return RespFunctionNotAllowed; } +public: + LocalServerInterface(LocalServer *_server); + ~LocalServerInterface(); + + bool sendProtocolItem(ProtocolItem *item, bool deleteItem = true); + +signals: + void itemToClient(ProtocolItem *item); +public slots: + void itemFromClient(ProtocolItem *item); +}; + +#endif \ No newline at end of file diff --git a/cockatrice/src/remoteclient.cpp b/cockatrice/src/remoteclient.cpp new file mode 100644 index 000000000..4e3f9724b --- /dev/null +++ b/cockatrice/src/remoteclient.cpp @@ -0,0 +1,138 @@ +#include +#include +#include +#include "remoteclient.h" +#include "protocol.h" +#include "protocol_items.h" + +RemoteClient::RemoteClient(QObject *parent) + : AbstractClient(parent), topLevelItem(0) +{ + ProtocolItem::initializeHash(); + + timer = new QTimer(this); + timer->setInterval(1000); + connect(timer, SIGNAL(timeout()), this, SLOT(ping())); + + socket = new QTcpSocket(this); + connect(socket, SIGNAL(connected()), this, SLOT(slotConnected())); + connect(socket, SIGNAL(readyRead()), this, SLOT(readData())); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotSocketError(QAbstractSocket::SocketError))); + + xmlReader = new QXmlStreamReader; + xmlWriter = new QXmlStreamWriter; + xmlWriter->setAutoFormatting(true); + xmlWriter->setDevice(socket); +} + +RemoteClient::~RemoteClient() +{ + disconnectFromServer(); +} + +void RemoteClient::slotSocketError(QAbstractSocket::SocketError /*error*/) +{ + emit socketError(socket->errorString()); + disconnectFromServer(); +} + +void RemoteClient::slotConnected() +{ + timer->start(); + setStatus(StatusAwaitingWelcome); +} + +void RemoteClient::loginResponse(ResponseCode response) +{ + if (response == RespOk) + setStatus(StatusLoggedIn); + else { + emit serverError(response); + setStatus(StatusDisconnecting); + } +} + +void RemoteClient::readData() +{ + QByteArray data = socket->readAll(); + qDebug() << data; + xmlReader->addData(data); + + while (!xmlReader->atEnd()) { + xmlReader->readNext(); + if (topLevelItem) + topLevelItem->readElement(xmlReader); + else if (xmlReader->isStartElement() && (xmlReader->name().toString() == "cockatrice_server_stream")) { + int serverVersion = xmlReader->attributes().value("version").toString().toInt(); + if (serverVersion != ProtocolItem::protocolVersion) { + emit protocolVersionMismatch(ProtocolItem::protocolVersion, serverVersion); + disconnectFromServer(); + return; + } + xmlWriter->writeStartDocument(); + xmlWriter->writeStartElement("cockatrice_client_stream"); + xmlWriter->writeAttribute("version", QString::number(ProtocolItem::protocolVersion)); + + topLevelItem = new TopLevelProtocolItem; + connect(topLevelItem, SIGNAL(protocolItemReceived(ProtocolItem *)), this, SLOT(processProtocolItem(ProtocolItem *))); + + setStatus(StatusLoggingIn); + Command_Login *cmdLogin = new Command_Login(userName, password); + connect(cmdLogin, SIGNAL(finished(ResponseCode)), this, SLOT(loginResponse(ResponseCode))); + sendCommand(cmdLogin); + } + } + if (status == StatusDisconnecting) + disconnectFromServer(); +} + +void RemoteClient::sendCommandContainer(CommandContainer *cont) +{ + cont->write(xmlWriter); + pendingCommands.insert(cont->getCmdId(), cont); +} + +void RemoteClient::connectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password) +{ + disconnectFromServer(); + + userName = _userName; + password = _password; + socket->connectToHost(hostname, port); + setStatus(StatusConnecting); +} + +void RemoteClient::disconnectFromServer() +{ + delete topLevelItem; + topLevelItem = 0; + + xmlReader->clear(); + + timer->stop(); + + QList pc = pendingCommands.values(); + for (int i = 0; i < pc.size(); i++) + delete pc[i]; + pendingCommands.clear(); + + setStatus(StatusDisconnected); + socket->close(); +} + +void RemoteClient::ping() +{ + int maxTime = 0; + QMapIterator i(pendingCommands); + while (i.hasNext()) { + int time = i.next().value()->tick(); + if (time > maxTime) + maxTime = time; + } + emit maxPingTime(maxTime, maxTimeout); + if (maxTime >= maxTimeout) { + emit serverTimeout(); + disconnectFromServer(); + } else + sendCommand(new Command_Ping); +} diff --git a/cockatrice/src/remoteclient.h b/cockatrice/src/remoteclient.h new file mode 100644 index 000000000..10d7c417b --- /dev/null +++ b/cockatrice/src/remoteclient.h @@ -0,0 +1,45 @@ +#ifndef REMOTECLIENT_H +#define REMOTECLIENT_H + +#include +#include "protocol_datastructures.h" +#include "abstractclient.h" + +class QTimer; +class QXmlStreamReader; +class QXmlStreamWriter; + +class RemoteClient : public AbstractClient { + Q_OBJECT +signals: + void maxPingTime(int seconds, int maxSeconds); + void serverTimeout(); + void socketError(const QString &errorString); + void protocolVersionMismatch(int clientVersion, int serverVersion); + void protocolError(); +private slots: + void slotConnected(); + void readData(); + void slotSocketError(QAbstractSocket::SocketError error); + void ping(); + void loginResponse(ResponseCode response); +private: + static const int maxTimeout = 10; + + QTimer *timer; + QTcpSocket *socket; + QXmlStreamReader *xmlReader; + QXmlStreamWriter *xmlWriter; + TopLevelProtocolItem *topLevelItem; +public: + RemoteClient(QObject *parent = 0); + ~RemoteClient(); + QString peerName() const { return socket->peerName(); } + + void connectToServer(const QString &hostname, unsigned int port, const QString &_userName, const QString &_password); + void disconnectFromServer(); + + void sendCommandContainer(CommandContainer *cont); +}; + +#endif