From: Drew Fisher Date: Thu, 7 Oct 2010 03:00:21 +0000 (-0700) Subject: Action improvements and connection error clarity. X-Git-Url: http://git.zarvox.org/shortlog/widgets.js?a=commitdiff_plain;h=03539232a634227a95537286676ec2f501b87259;p=shareboard.git Action improvements and connection error clarity. This patchset reworks the structure of Action and its subclasses. It also adds polymorphic receiving of some types of actions over the network. Lastly, connection errors are now reported to the user. --- diff --git a/action.cpp b/action.cpp index fe82a21..ed5df1c 100644 --- a/action.cpp +++ b/action.cpp @@ -2,14 +2,14 @@ Action::Action() { type = INVALID; - id = 0; - approved = false; + mesgID = 0; + userID = 0; } Action::Action(Type t) { type = t; - id = 0; - approved = false; + mesgID = 0; + userID = 0; } Action::~Action() { @@ -41,8 +41,18 @@ DrawLineAction::DrawLineAction() : Action(Action::DrawLine) { DrawLineAction::~DrawLineAction() { } +AddImageAction::AddImageAction() { +} +AddImageAction::~AddImageAction() { +} + +UserChatAction::UserChatAction() { +} +UserChatAction::~UserChatAction() { +} + QTextStream& operator<<(QTextStream& out, const Action& action) { - out << "0 " << action.id << " 0 " << static_cast(action.type) ; // mesgID, userID, timestamp, mesgType + out << "0 " << action.userID << " " << action.timestamp.toString("yyyy-MM-ddThh:mm:ss.zzz") << " " << static_cast(action.type) ; // mesgID, userID, timestamp, mesgType switch(action.type) { case Action::INVALID: break; case Action::UserJoin: @@ -82,11 +92,7 @@ QTextStream& operator<<(QTextStream& out, const Action& action) { return out; } -/*QTextStream& operator>>(QTextStream& in, Action& action){ - quint16 k; - in >> k >> action.id >> action.approved >> action.when; - action.type = static_cast(k); - return in; -}*/ - +// operator>> can't be implemented in this manner since we need to handle +// different types and operator>> works on a reference to the base class. +// Thus, I'm pushing this sort of functionality into the connection manager. - Drew diff --git a/action.h b/action.h index 3f2baf2..9e25722 100644 --- a/action.h +++ b/action.h @@ -26,10 +26,9 @@ public: virtual ~Action(); Type type; - // UUID uuid; // Unique id for this - int id; // Which user does this refer to? - bool approved; // Has the server approved this event yet? - QDateTime when; // Time at which the server made this event official + qint32 mesgID; // Unique id for this + qint32 userID; // Which user does this refer to? + QDateTime timestamp; // Time at which the server made this event official }; diff --git a/connectionmanager.cpp b/connectionmanager.cpp index 515a46c..db02dfe 100644 --- a/connectionmanager.cpp +++ b/connectionmanager.cpp @@ -10,6 +10,7 @@ ConnectionManager::ConnectionManager(QObject* parent) : QObject(parent) { QObject::connect(sock, SIGNAL(connected()), this, SLOT(onConnect())); QObject::connect(sock, SIGNAL(disconnected()), this, SLOT(onDisconnect())); QObject::connect(sock, SIGNAL(readyRead()), this, SLOT(haveData())); + QObject::connect(sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onError())); } ConnectionManager::~ConnectionManager() { @@ -24,22 +25,141 @@ void ConnectionManager::joinServer(QString _username, QString host) { } void ConnectionManager::sendAction(Action* action) { - switch(action->type) { // Handle serializing all actions - default: - break; - } - + QByteArray output; + QTextStream t(&output); + t << (*action); + qDebug() << "Sending" << output.size() << "bytes for action of type" << action->type; + sock->write(output); } void ConnectionManager::haveData() { - data.append(sock->readAll()); - // Check if we have a full action waiting, if so, dispatch - // Event structure: - // uint32 num_bytes (we don't support things larger than 4GB, kthx - // uint16 type - // char approved - // QDateTime when - // [rest of details] - qDebug() << data.size() << " bytes read so far"; + while(sock->canReadLine()) { + QByteArray data = sock->readLine(0); // Read a line, however many bytes it takes + QTextStream textstream(&data); + qint32 mesgID = -1; + qint32 userID = -1; + quint16 t = 0; // Type + QString timestring; + textstream >> mesgID >> userID >> timestring >> t; + + // Validate input + QDateTime timestamp = QDateTime::fromString(timestring, "yyyy-MM-ddTHH-mm-ss.zzz"); + if(mesgID == -1 || userID == -1 || !timestamp.isValid() ) { + qWarning() << "Received invalid message from server; discarding."; + continue; + } + + // Abstract factory go! + Action::Type type = static_cast(t); + Action* act; + switch(type) { + case Action::INVALID: // We should never get an invalid action from the server... + continue; + case Action::UserJoin: + { + act = new UserJoinAction(); + UserJoinAction* action = static_cast(act); + QString username; + textstream >> username; + if(textstream.status() != QTextStream::Ok) { + qWarning() << "Got invalid username from server :("; + delete action; + continue; + } + action->username = username; + action->mesgID = mesgID; + action->userID = userID; + action->timestamp = timestamp; + break; + } + case Action::UserPart: + { + act = new UserPartAction(); + UserPartAction* action = static_cast(act); + action->mesgID = mesgID; + action->userID = userID; + action->timestamp = timestamp; + break; + } + case Action::MouseMove: + { + act = new MouseMoveAction(); + qreal x = 0; + qreal y = 0; + textstream >> x >> y; + // Ignore invalid mouse positions; replace with 0,0 + MouseMoveAction* action = static_cast(act); + action->pos = QPointF(x,y); + action->mesgID = mesgID; + action->userID = userID; + action->timestamp = timestamp; + break; + } + case Action::DrawLine: + { + /* + act = new DrawLineAction(); + action = static_cast(act); + int red = 0; + int green = 0; + int blue = 0; + int width = 1; + textstream >> red >> green >> blue >> width; + if(textstream.status() != QTextStream::Ok){ + qWarning() << "Received invalid line data from server :("; + delete action; + continue; + } + do { + // some stuff with the points + } while(textstream.status() == QTextStream::Ok); + action->color = QColor::fromRgb(red, green, blue); + action->width = width; + action->mesgID = mesgID; + action->userID = userID; + action->timestamp = timestamp; + */ + break; + } + case Action::AddImage: + { + /* + act = new AddImageAction(); + AddImageAction* action = static_cast(act); + // do somethin with the top-left point + // handle reading image data as base64-encoded binary, bleah + action->mesgID = mesgID; + action->userID = userID; + action->timestamp = timestamp; + */ + break; + } + case Action::UserChat: + { + act = new UserChatAction(); + UserChatAction* action = static_cast(act); + QString chat; + textstream >> chat; + if(textstream.status() != QTextStream::Ok) { + qWarning() << "Got invalid text chat from server :("; + delete action; + continue; + } + action->mesgID = mesgID; + action->userID = userID; + action->timestamp = timestamp; + action->text = chat; + break; + } + default: + qWarning() << "Received invalid type from server :("; + continue; + } + Q_ASSERT(act != NULL); + + // HistoryManager::instance()->newAction(action); + + qDebug() << "Dispatched action with type " << act->type << " that was " << data.size() << " bytes long"; + } } void ConnectionManager::onConnect() { @@ -53,3 +173,9 @@ void ConnectionManager::onDisconnect() { qDebug() << "Disconnected"; emit disconnected(); } + +void ConnectionManager::onError() { + QString e = sock->errorString(); + qDebug() << "connection error: " << e; + emit error(e); +} diff --git a/connectionmanager.h b/connectionmanager.h index 9b1e37b..74f981f 100644 --- a/connectionmanager.h +++ b/connectionmanager.h @@ -5,6 +5,7 @@ #include #include #include +#include class Action; class QTcpSocket; @@ -19,12 +20,14 @@ class ConnectionManager : public QObject{ //void actionRecieved(Action* act); // act is freed by recipient of signal void connected(); void disconnected(); + void error(QString e); public slots: void joinServer(QString _username, QString host); void sendAction(Action* action); + void onError(); private: QString username; - QByteArray data; + //QByteArray data; QTcpSocket* sock; QTextStream* textStream; private slots: diff --git a/mainwindow.cpp b/mainwindow.cpp index d9f1848..872d247 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -23,6 +23,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { QObject::connect(board, SIGNAL(connectToServer(QString, QString)), connMan, SLOT(joinServer(QString, QString))); QObject::connect(connMan, SIGNAL(connected()), this, SLOT(switchToBoard())); QObject::connect(connMan, SIGNAL(disconnected()), this, SLOT(switchToConnect())); + QObject::connect(connMan, SIGNAL(error(QString)), this, SLOT(showError(QString))); statusBar()->showMessage("Ready"); } @@ -73,6 +74,10 @@ void MainWindow::switchToConnect() { statusBar()->showMessage(QString("Switched to connection prompt")); } +void MainWindow::showError(QString error) { + statusBar()->showMessage(error); +} + void MainWindow::createActions() { //joinAction = new QAction("Joi&n a board online", this); loadPastAction = new QAction("&Open a board", this); diff --git a/mainwindow.h b/mainwindow.h index e5dff43..8f06f98 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -21,6 +21,7 @@ public slots: void quit(); void switchToBoard(); void switchToConnect(); + void showError(QString error); private: void createActions(); void createMenus();