traintastic/client/src/dialog/connectdialog.cpp
Reinder Feenstra e4666e3fae Added extra info to display stage of connecting
This will give some more insight to solve #33
2023-11-15 23:23:32 +01:00

226 Zeilen
7.1 KiB
C++

/**
* client/src/dialog/connectdialog.cpp
*
* This file is part of the traintastic source code.
*
* Copyright (C) 2019-2021 Reinder Feenstra
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "connectdialog.hpp"
#include <QUdpSocket>
#include <QNetworkInterface>
#include <QFormLayout>
#include <QComboBox>
#include <QLineEdit>
#include <QLabel>
#include <QPushButton>
#include <QUrl>
#include <QTimer>
#include "../network/connection.hpp"
#include "../network/object.hpp"
#include <traintastic/locale/locale.hpp>
ConnectDialog::ConnectDialog(QWidget* parent, const QString& url) :
QDialog(parent, Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
m_connection{std::make_shared<Connection>()},
m_udpSocket{new QUdpSocket(this)},
m_server{new QComboBox()},
m_username{new QLineEdit()},
m_password{new QLineEdit()},
m_status{new QLabel()},
m_connect{new QPushButton(Locale::tr("qtapp.connect_dialog:connect"))}
{
setWindowTitle(Locale::tr("qtapp.connect_dialog:connect_to_server"));
m_server->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow);
m_server->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_server->setEditable(true);
m_password->setEchoMode(QLineEdit::Password);
m_status->setAlignment(Qt::AlignCenter);
m_status->setMinimumWidth(400);
QFormLayout* formLayout = new QFormLayout();
formLayout->setContentsMargins(0, 0, 0, 0);
formLayout->addRow(Locale::tr("qtapp.connect_dialog:server"), m_server);
/*
formLayout->addRow(Locale::tr("qtapp.connect_dialog:username"), m_username);
formLayout->addRow(Locale::tr("qtapp.connect_dialog:password"), m_password);
*/
QVBoxLayout* layout = new QVBoxLayout();
layout->addLayout(formLayout);
layout->addWidget(m_status);
layout->addWidget(m_connect, 0, Qt::AlignCenter);
layout->setSizeConstraint(QLayout::SetFixedSize);
setLayout(layout);
connect(m_udpSocket, &QUdpSocket::readyRead, this, &ConnectDialog::socketReadyRead);
connect(&m_broadcastTimer, &QTimer::timeout, this, &ConnectDialog::broadcast);
connect(m_connection.get(), &Connection::stateChanged, this, &ConnectDialog::stateChanged);
connect(m_server, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ConnectDialog::serverIndexChanged);
connect(m_server, &QComboBox::currentTextChanged, this, &ConnectDialog::serverTextChanged);
connect(m_connect, &QPushButton::clicked, this, &ConnectDialog::connectClick);
if(!url.isEmpty())
{
m_server->setCurrentText(url);
QTimer::singleShot(100, this, &ConnectDialog::connectClick);
}
m_broadcastTimer.start(1000);
broadcast();
}
void ConnectDialog::setControlsEnabled(bool value)
{
m_server->setEnabled(value);
m_username->setEnabled(value);
m_password->setEnabled(value);
m_connect->setEnabled(value);
}
void ConnectDialog::broadcast()
{
// decrement TTL of all discovered servers:
for(Servers::iterator it = m_servers.begin(); it != m_servers.end(); )
{
if(--it.value().second == 0)
{
for(int i = 0; i < m_server->count(); i++)
if(m_server->itemData(i) == it.key())
{
m_server->removeItem(i);
break;
}
it = m_servers.erase(it);
}
else
it++;
}
// send discovery request on all networks:
for(auto& interface : QNetworkInterface::allInterfaces())
for(auto& addressEntry : interface.addressEntries())
if(!addressEntry.broadcast().isNull())
{
auto message = Message::newRequest(Message::Command::Discover);
m_udpSocket->writeDatagram(reinterpret_cast<const char*>(**message), message->size(), addressEntry.broadcast(), Connection::defaultPort);
}
}
void ConnectDialog::socketReadyRead()
{
while(m_udpSocket->hasPendingDatagrams())
{
Message message(m_udpSocket->pendingDatagramSize());
QHostAddress host;
quint16 port;
m_udpSocket->readDatagram(static_cast<char*>(*message), message.size(), &host, &port);
if(message.command() == Message::Command::Discover && message.isResponse() && !message.isError())
{
QString name = QString::fromUtf8(message.read<QByteArray>());
QUrl url;
url.setHost(host.toString());
url.setPort(port);
auto it = m_servers.find(url);
if(it == m_servers.end())
{
m_servers[url] = {name, defaultTTL};
m_server->addItem(url.host() + (url.port() != Connection::defaultPort ? ":" + QString::number(url.port()) : "") + " (" + name + ")", url);
}
else
it->second = defaultTTL;
}
}
}
void ConnectDialog::stateChanged()
{
switch(m_connection->state())
{
case Connection::State::Disconnected:
m_status->setText("");
break;
case Connection::State::Disconnecting:
m_status->setText(Locale::tr("qtapp.connect_dialog:disconnecting"));
break;
case Connection::State::Connecting:
m_status->setText(Locale::tr("qtapp.connect_dialog:connecting"));
break;
case Connection::State::Connected:
m_status->setText(Locale::tr("qtapp.connect_dialog:connected"));
QTimer::singleShot(300, this, &ConnectDialog::accept);
break;
case Connection::State::Authenticating:
m_status->setText(Locale::tr("qtapp.connect_dialog:authenticating"));
break;
case Connection::State::CreatingSession:
m_status->setText(Locale::tr("qtapp.connect_dialog:creating_session"));
break;
case Connection::State::FetchingWorld:
m_status->setText(Locale::tr("qtapp.connect_dialog:fetching_world"));
break;
case Connection::State::SocketError:
m_status->setText(m_connection->errorString());
setControlsEnabled(true);
break;
case Connection::State::ErrorAuthenticationFailed:
m_status->setText(Locale::tr("qtapp.connect_dialog:authentication_failed"));
break;
case Connection::State::ErrorNewSessionFailed:
m_status->setText(Locale::tr("qtapp.connect_dialog:create_session_failed"));
break;
}
}
void ConnectDialog::serverIndexChanged(int index)
{
m_url = m_server->itemData(index).toUrl();
m_connect->setEnabled(m_url.isValid());
}
void ConnectDialog::serverTextChanged(const QString& text)
{
QString url{text};
m_url = QUrl::fromUserInput(url.remove(QRegularExpression("\\s*\\(.*\\)$")));
m_connect->setEnabled(m_url.isValid());
}
void ConnectDialog::connectClick()
{
if(!m_url.isValid())
return;
setControlsEnabled(false);
m_connection->connectToHost(m_url, m_username->text(), m_password->text());
}