# HG changeset patch # User unc0rr # Date 1557607195 -7200 # Node ID a3ad06ac390e787927251aa20f8dfb2b9f7b1417 # Parent 9ab78e08a34c70fb51a418265c74164d4961fb18 Proof of concept for new net game client diff -r 9ab78e08a34c -r a3ad06ac390e qmlfrontend/CMakeLists.txt --- a/qmlfrontend/CMakeLists.txt Sat May 11 20:43:12 2019 +0200 +++ b/qmlfrontend/CMakeLists.txt Sat May 11 22:39:55 2019 +0200 @@ -17,6 +17,7 @@ "preview_image_provider.cpp" "preview_image_provider.h" "engine_interface.h" "preview_acceptor.cpp" "preview_acceptor.h" + "net_session.cpp" "net_session.h" ) -target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Quick) +target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Network Qt5::Quick) diff -r 9ab78e08a34c -r a3ad06ac390e qmlfrontend/Page1.qml --- a/qmlfrontend/Page1.qml Sat May 11 20:43:12 2019 +0200 +++ b/qmlfrontend/Page1.qml Sat May 11 22:39:55 2019 +0200 @@ -5,6 +5,7 @@ focus: true property HWEngine hwEngine + property NetSession netSession Component { id: hwEngineComponent @@ -17,6 +18,15 @@ } } + Component { + id: netSessionComponent + + NetSession { + nickname: "test0272" + url: "hwnet://gameserver.hedgewars.org:46632" + } + } + Component.onCompleted: { hwEngine = hwEngineComponent.createObject() } @@ -40,6 +50,10 @@ hwEngine.getPreview() } } + netButton.onClicked: { + netSession = netSessionComponent.createObject() + netSession.open() + } Keys.onPressed: { if (event.key === Qt.Key_Enter) diff -r 9ab78e08a34c -r a3ad06ac390e qmlfrontend/Page1Form.ui.qml --- a/qmlfrontend/Page1Form.ui.qml Sat May 11 20:43:12 2019 +0200 +++ b/qmlfrontend/Page1Form.ui.qml Sat May 11 22:39:55 2019 +0200 @@ -5,11 +5,13 @@ import Hedgewars.Engine 1.0 Item { + id: element property alias button1: button1 property alias previewImage: previewImage property alias gameButton: gameButton width: 1024 height: 800 + property alias netButton: netButton property alias tickButton: tickButton property alias gameView: gameView @@ -80,4 +82,13 @@ Layout.fillHeight: true } } + + Button { + id: netButton + text: qsTr("Net") + anchors.bottom: parent.bottom + anchors.bottomMargin: 8 + anchors.left: parent.left + anchors.leftMargin: 8 + } } diff -r 9ab78e08a34c -r a3ad06ac390e qmlfrontend/main.cpp --- a/qmlfrontend/main.cpp Sat May 11 20:43:12 2019 +0200 +++ b/qmlfrontend/main.cpp Sat May 11 22:39:55 2019 +0200 @@ -6,6 +6,7 @@ #include "engine_interface.h" #include "game_view.h" #include "hwengine.h" +#include "net_session.h" #include "preview_acceptor.h" static QObject* previewacceptor_singletontype_provider( @@ -32,6 +33,7 @@ previewacceptor_singletontype_provider); qmlRegisterType("Hedgewars.Engine", 1, 0, "HWEngine"); qmlRegisterType("Hedgewars.Engine", 1, 0, "GameView"); + qmlRegisterType("Hedgewars.Engine", 1, 0, "NetSession"); qmlRegisterUncreatableType("Hedgewars.Engine", 1, 0, "EngineInstance", "Create by HWEngine run methods"); diff -r 9ab78e08a34c -r a3ad06ac390e qmlfrontend/net_session.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlfrontend/net_session.cpp Sat May 11 22:39:55 2019 +0200 @@ -0,0 +1,137 @@ +#include "net_session.h" + +NetSession::NetSession(QObject *parent) : QObject(parent) {} + +QUrl NetSession::url() const { return m_url; } + +QAbstractSocket::SocketState NetSession::state() const { + if (m_socket) + return m_socket->state(); + else + return QAbstractSocket::UnconnectedState; +} + +void NetSession::open() { + m_socket.reset(new QTcpSocket()); + + connect(m_socket.data(), &QAbstractSocket::stateChanged, this, + &NetSession::stateChanged); + connect(m_socket.data(), &QTcpSocket::readyRead, this, + &NetSession::onReadyRead); + + m_socket->connectToHost(m_url.host(), + static_cast(m_url.port(46631))); +} + +QString NetSession::nickname() const { return m_nickname; } + +QString NetSession::password() const { return m_password; } + +NetSession::SessionState NetSession::sessionState() const { + return m_sessionState; +} + +void NetSession::setUrl(const QUrl &url) { + if (m_url == url) return; + + m_url = url; + emit urlChanged(m_url); +} + +void NetSession::setNickname(const QString &nickname) { + if (m_nickname == nickname) return; + + m_nickname = nickname; + emit nicknameChanged(m_nickname); +} + +void NetSession::setPassword(const QString &password) { + if (m_password == password) return; + + m_password = password; + emit passwordChanged(m_password); +} + +void NetSession::close() { + if (!m_socket.isNull()) { + m_socket->disconnectFromHost(); + m_socket.clear(); + + setSessionState(NotConnected); + } +} + +void NetSession::onReadyRead() { + while (m_socket->canReadLine()) { + auto line = QString::fromUtf8(m_socket->readLine().simplified()); + + if (line.isEmpty()) { + parseNetMessage(m_buffer); + m_buffer.clear(); + } else { + m_buffer.append(line); + } + } +} + +void NetSession::parseNetMessage(const QStringList &message) { + if (message.isEmpty()) { + qWarning() << "Empty net message received"; + return; + } + + qDebug() << "[SERVER]" << message; + + using Handler = std::function; + static QMap commandsMap{ + {"CONNECTED", &NetSession::handleConnected}, + {"PING", &NetSession::handlePing}, + {"BYE", &NetSession::handleBye}}; + + auto handler = + commandsMap.value(message[0], &NetSession::handleUnknownCommand); + + handler(this, message.mid(1)); +} + +void NetSession::handleConnected(const QStringList ¶meters) { + setSessionState(Login); +} + +void NetSession::handlePing(const QStringList ¶meters) { + send("PONG", parameters); +} + +void NetSession::handleBye(const QStringList ¶meters) { close(); } + +void NetSession::handleUnknownCommand(const QStringList ¶meters) { + Q_UNUSED(parameters); + + qWarning() << "Command is not recognized"; +} + +void NetSession::send(const QString &message) { send(QStringList(message)); } + +void NetSession::send(const QString &message, const QString ¶m) { + send(QStringList{message, param}); +} + +void NetSession::send(const QString &message, const QStringList ¶meters) { + send(QStringList(message) + parameters); +} + +void NetSession::send(const QStringList &message) { + Q_ASSERT(!m_socket.isNull()); + + qDebug() << "[CLIENT]" << message; + + m_socket->write(message.join('\n').toUtf8() + "\n\n"); +} + +void NetSession::setSessionState(NetSession::SessionState sessionState) { + if (m_sessionState == sessionState) return; + + m_sessionState = sessionState; + + emit sessionStateChanged(sessionState); +} diff -r 9ab78e08a34c -r a3ad06ac390e qmlfrontend/net_session.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qmlfrontend/net_session.h Sat May 11 22:39:55 2019 +0200 @@ -0,0 +1,70 @@ +#ifndef NET_SESSION_H +#define NET_SESSION_H + +#include +#include +#include +#include + +class NetSession : public QObject { + Q_OBJECT + + // clang-format off + Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QAbstractSocket::SocketState state READ state NOTIFY stateChanged) + Q_PROPERTY(QString nickname READ nickname WRITE setNickname NOTIFY nicknameChanged) + Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) + Q_PROPERTY(SessionState sessionState READ sessionState NOTIFY sessionStateChanged) + // clang-format on + + public: + enum SessionState { NotConnected, Login, Lobby, Room, Game }; + Q_ENUMS(SessionState) + + explicit NetSession(QObject *parent = nullptr); + + QUrl url() const; + QAbstractSocket::SocketState state() const; + + Q_INVOKABLE void open(); + + QString nickname() const; + QString password() const; + SessionState sessionState() const; + + public slots: + void setUrl(const QUrl &url); + void setNickname(const QString &nickname); + void setPassword(const QString &password); + void close(); + + signals: + void urlChanged(const QUrl url); + void stateChanged(QAbstractSocket::SocketState state); + void nicknameChanged(const QString &nickname); + void passwordChanged(const QString &password); + void sessionStateChanged(SessionState sessionState); + + private slots: + void onReadyRead(); + void parseNetMessage(const QStringList &message); + void handleConnected(const QStringList ¶meters); + void handlePing(const QStringList ¶meters); + void handleBye(const QStringList ¶meters); + void handleUnknownCommand(const QStringList ¶meters); + void send(const QString &message); + void send(const QString &message, const QString ¶m); + void send(const QString &message, const QStringList ¶meters); + void send(const QStringList &message); + void setSessionState(SessionState sessionState); + + private: + QUrl m_url; + QSharedPointer m_socket; + QString m_nickname; + QString m_password; + QStringList m_buffer; + SessionState m_sessionState; +}; + +#endif // NET_SESSION_H