From 6861201f2c3fb7856d14ae0fe9e6da4de2a9b55b Mon Sep 17 00:00:00 2001 From: skyjake Date: Sun, 27 Jan 2013 18:09:24 +0200 Subject: [PATCH] Shell|libdeng2: Defining libshell's network protocol Starting out with minimal functionality: console commands and log entries. --- doomsday/libdeng2/include/de/net/protocol.h | 1 + .../shell/libshell/include/de/shell/link.h | 15 +++- .../libshell/include/de/shell/protocol.h | 60 ++++++++++++++ doomsday/tools/shell/libshell/src/link.cpp | 73 ++++++++++++---- .../tools/shell/libshell/src/protocol.cpp | 83 ++++++++++++++++++- 5 files changed, 212 insertions(+), 20 deletions(-) diff --git a/doomsday/libdeng2/include/de/net/protocol.h b/doomsday/libdeng2/include/de/net/protocol.h index 5cbf05ac81..cabc28a023 100644 --- a/doomsday/libdeng2/include/de/net/protocol.h +++ b/doomsday/libdeng2/include/de/net/protocol.h @@ -21,6 +21,7 @@ #define LIBDENG2_PROTOCOL_H #include "../libdeng2.h" +#include #include diff --git a/doomsday/tools/shell/libshell/include/de/shell/link.h b/doomsday/tools/shell/libshell/include/de/shell/link.h index 0ac2cb989c..1848b20635 100644 --- a/doomsday/tools/shell/libshell/include/de/shell/link.h +++ b/doomsday/tools/shell/libshell/include/de/shell/link.h @@ -20,6 +20,7 @@ #define LIBSHELL_LINK_H #include +#include #include #include #include @@ -51,6 +52,13 @@ class DENG2_PUBLIC Link : public QObject, public Transmitter */ Link(Address const &address); + /** + * Takes over an existing socket. + * + * @param openSocket Socket. Link takes ownership. + */ + Link(Socket *openSocket); + virtual ~Link(); /** @@ -68,6 +76,11 @@ class DENG2_PUBLIC Link : public QObject, public Transmitter */ Time connectedAt() const; + /** + * Shell protocol for constructing and interpreting packets. + */ + Protocol &protocol(); + /** * Returns the next received packet. * @@ -82,10 +95,10 @@ class DENG2_PUBLIC Link : public QObject, public Transmitter protected slots: void socketConnected(); void socketDisconnected(); + void handleIncomingPackets(); signals: void connected(); - void packetsReady(); void disconnected(); private: diff --git a/doomsday/tools/shell/libshell/include/de/shell/protocol.h b/doomsday/tools/shell/libshell/include/de/shell/protocol.h index fadf4e1244..87a73598cc 100644 --- a/doomsday/tools/shell/libshell/include/de/shell/protocol.h +++ b/doomsday/tools/shell/libshell/include/de/shell/protocol.h @@ -20,17 +20,77 @@ #define LIBSHELL_PROTOCOL_H #include +#include +#include namespace de { namespace shell { +/** + * Packet with one or more log entries. + */ +class LogEntryPacket : public Packet +{ +public: + LogEntryPacket(); + ~LogEntryPacket(); + + void clear(); + void execute() const; + + // Implements ISerializable. + void operator >> (Writer &to) const; + void operator << (Reader &from); + + static Packet *fromBlock(Block const &block); + +private: + QList _entries; +}; + /** * Network protocol for communicating with a server. */ class Protocol : public de::Protocol { +public: + /// Type of provided packet is incorrect. @ingroup errors + DENG2_ERROR(TypeError); + + enum PacketType + { + Unknown, + Command, ///< Console command (only to server). + LogEntries, ///< Log entries. + ConsoleLexicon, ///< Known words for command line completion. + GameState, ///< Current state of the game (mode, map). + Leaderboard, ///< Frags leaderboard. + MapOutline, ///< Sectors of the map for visual overview. + PlayerPositions ///< Current player positions. + }; + public: Protocol(); + + /** + * Detects the type of a packet. + * + * @param packet Any packet. + * + * @return Type of the packet. + */ + PacketType recognize(Packet const *packet); + + /** + * Constructs a console command packet. + * + * @param command Command to execute on the server. + * + * @return Packet. Caller gets ownership. + */ + RecordPacket *newCommand(String const &command); + + String command(Packet const &commandPacket); }; } // namespace shell diff --git a/doomsday/tools/shell/libshell/src/link.cpp b/doomsday/tools/shell/libshell/src/link.cpp index ccfbe82a7a..8e601e32e1 100644 --- a/doomsday/tools/shell/libshell/src/link.cpp +++ b/doomsday/tools/shell/libshell/src/link.cpp @@ -29,33 +29,50 @@ namespace shell { struct Link::Instance { Link &self; - Address serverAddress; - Socket socket; + Address peerAddress; + Socket *socket; Protocol protocol; Status status; Time connectedAt; Instance(Link &i) : self(i), + socket(0), status(Disconnected), connectedAt(Time::invalidTime()) {} - ~Instance() {} + ~Instance() + { + delete socket; + } }; Link::Link(Address const &address) : d(new Instance(*this)) { - d->serverAddress = address; + d->peerAddress = address; + d->socket = new Socket; - connect(&d->socket, SIGNAL(connected()), this, SLOT(socketConnected())); - connect(&d->socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); - connect(&d->socket, SIGNAL(messagesReady()), this, SIGNAL(packetsReady())); + connect(d->socket, SIGNAL(connected()), this, SLOT(socketConnected())); + connect(d->socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + connect(d->socket, SIGNAL(messagesReady()), this, SLOT(handleIncomingPackets())); - d->socket.connect(address); + d->socket->connect(address); d->status = Connecting; } +Link::Link(Socket *openSocket) : d(new Instance(*this)) +{ + d->peerAddress = openSocket->peerAddress(); + d->socket = openSocket; + + connect(d->socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + connect(d->socket, SIGNAL(messagesReady()), this, SLOT(handleIncomingPackets())); + + d->status = Connected; + d->connectedAt = Time(); +} + Link::~Link() { delete d; @@ -63,8 +80,8 @@ Link::~Link() Address Link::address() const { - if(d->socket.isOpen()) return d->socket.peerAddress(); - return d->serverAddress; + if(d->socket->isOpen()) return d->socket->peerAddress(); + return d->peerAddress; } Link::Status Link::status() const @@ -77,23 +94,37 @@ Time Link::connectedAt() const return d->connectedAt; } -Packet *Link::nextPacket() +Protocol &Link::protocol() { - if(!d->socket.hasIncoming()) return 0; + return d->protocol; +} - std::auto_ptr data(d->socket.receive()); - return d->protocol.interpret(*data.get()); +Packet *Link::nextPacket() +{ + if(!d->socket->hasIncoming()) return 0; + + std::auto_ptr data(d->socket->receive()); + Packet *packet = d->protocol.interpret(*data.get()); + try + { + packet->setFrom(d->socket->peerAddress()); + } + catch(Socket::PeerError const &) + { + // Socket must already be closed. + } + return packet; } void Link::send(IByteArray const &data) { - d->socket.send(data); + d->socket->send(data); } void Link::socketConnected() { LOG_AS("Link"); - LOG_VERBOSE("Successfully connected to %s") << d->socket.peerAddress(); + LOG_VERBOSE("Successfully connected to %s") << d->socket->peerAddress(); d->status = Connected; d->connectedAt = Time(); @@ -104,7 +135,7 @@ void Link::socketConnected() void Link::socketDisconnected() { LOG_AS("Link"); - LOG_INFO("Disconnected from %s") << d->serverAddress; + LOG_INFO("Disconnected from %s") << d->peerAddress; d->status = Disconnected; @@ -115,5 +146,13 @@ void Link::socketDisconnected() d->connectedAt = Time::invalidTime(); } +void Link::handleIncomingPackets() +{ + while(Packet *ptr = nextPacket()) + { + QScopedPointer packet(ptr); + } +} + } // namespace shell } // namespace de diff --git a/doomsday/tools/shell/libshell/src/protocol.cpp b/doomsday/tools/shell/libshell/src/protocol.cpp index 7c58e71d9a..a08cf6505c 100644 --- a/doomsday/tools/shell/libshell/src/protocol.cpp +++ b/doomsday/tools/shell/libshell/src/protocol.cpp @@ -17,14 +17,93 @@ */ #include "de/shell/Protocol" +#include +#include +#include namespace de { namespace shell { +static String const PT_COMMAND = "shell.command"; + +// LogEntryPacket ------------------------------------------------------------ + +static char const *LOG_ENTRY_PACKET_TYPE = "LOGE"; + +LogEntryPacket::LogEntryPacket() : Packet(LOG_ENTRY_PACKET_TYPE) +{} + +LogEntryPacket::~LogEntryPacket() +{ + clear(); +} + +void LogEntryPacket::clear() +{ + foreach(LogEntry *e, _entries) delete e; + _entries.clear(); +} + +void LogEntryPacket::execute() const +{ + // Copies of all entries in the packet are added to the LogBuffer. + LogBuffer &buf = LogBuffer::appBuffer(); + foreach(LogEntry *e, _entries) + { + buf.add(new LogEntry(*e)); + } +} + +void LogEntryPacket::operator >> (Writer &to) const +{ + Packet::operator >> (to); + to.writeObjects(_entries); +} + +void LogEntryPacket::operator << (Reader &from) +{ + _entries.clear(); + + Packet::operator << (from); + from.readObjects(_entries); +} + +Packet *LogEntryPacket::fromBlock(Block const &block) +{ + return constructFromBlock(block, LOG_ENTRY_PACKET_TYPE); +} + +// Protocol ------------------------------------------------------------------ + Protocol::Protocol() +{} + +Protocol::PacketType Protocol::recognize(Packet const *packet) { - // Register all the known packet types. - //define(); + RecordPacket const *rec = dynamic_cast(packet); + if(rec) + { + if(rec->name() == PT_COMMAND) + return Command; + } + return Unknown; +} + +RecordPacket *Protocol::newCommand(String const &command) +{ + RecordPacket *cmd = new RecordPacket(PT_COMMAND); + cmd->record().addText("cmd", command); + return cmd; +} + +String Protocol::command(Packet const &commandPacket) +{ + DENG2_ASSERT(recognize(&commandPacket) == Command); + + RecordPacket const *rec = dynamic_cast(&commandPacket); + DENG2_ASSERT(rec != 0); + + return (*rec)["cmd"].value().asText(); } } // namespace shell