[FSD] Threaded (worker) FSD client, based on CContinuousWorker

* locks
* call functions in own thread
This commit is contained in:
Klaus Basan
2019-11-19 19:13:40 +01:00
committed by Mat Sutcliffe
parent f620147fe6
commit 4ef17fed6a
2 changed files with 259 additions and 185 deletions

View File

@@ -1,4 +1,4 @@
/* Copyright (C) 2019
/* Copyright (C) 2019
* swift project community / contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
@@ -7,6 +7,7 @@
*/
#include "fsdclient.h"
#include "fsdclient.h"
#include "blackcore/application.h"
#include "blackcore/fsd/addatc.h"
@@ -36,6 +37,7 @@
#include "blackmisc/aviation/flightplan.h"
#include "blackmisc/network/rawfsdmessage.h"
#include "blackmisc/threadutils.h"
#include "blackmisc/logmessage.h"
#include "blackmisc/range.h"
#include "blackmisc/verify.h"
@@ -78,11 +80,11 @@ namespace BlackCore
return escaped;
}
CFSDClient::CFSDClient(IClientProvider *clientProvider,
IOwnAircraftProvider *ownAircraftProvider,
CFSDClient::CFSDClient(IClientProvider *clientProvider,
IOwnAircraftProvider *ownAircraftProvider,
IRemoteAircraftProvider *remoteAircraftProvider,
QObject *parent)
: QObject(parent),
QObject *owner)
: CContinuousWorker(owner, "FSDClient"),
CClientAware(clientProvider),
COwnAircraftAware(ownAircraftProvider),
CRemoteAircraftAware(remoteAircraftProvider),
@@ -117,58 +119,60 @@ namespace BlackCore
void CFSDClient::setClientIdAndKey(quint16 id, const QByteArray &key)
{
clientAuth = vatsim_auth_create(id, qPrintable(key));
serverAuth = vatsim_auth_create(id, qPrintable(key));
QWriteLocker l(&m_lockUserClientBuffered);
m_clientAuth = vatsim_auth_create(id, qPrintable(key));
m_serverAuth = vatsim_auth_create(id, qPrintable(key));
}
void CFSDClient::setServer(const CServer &server)
{
Q_ASSERT_X(m_connectionStatus.isDisconnected(), Q_FUNC_INFO, "Can't change server details while still connected");
m_server = server;
if (m_server.getServerType() == CServer::FSDServerVatsim)
{
m_protocolRevision = PROTOCOL_REVISION_VATSIM_AUTH;
}
else
{
m_protocolRevision = PROTOCOL_REVISION_CLASSIC;
}
Q_ASSERT_X(this->getConnectionStatus().isDisconnected(), Q_FUNC_INFO, "Can't change server details while still connected");
const QString codecName(server.getFsdSetup().getTextCodec());
Q_ASSERT_X(!codecName.isEmpty(), Q_FUNC_INFO, "Missing code name");
m_fsdTextCodec = QTextCodec::codecForName(codecName.toLocal8Bit());
if (!m_fsdTextCodec) { m_fsdTextCodec = QTextCodec::codecForName("utf-8"); }
QTextCodec *textCodec = QTextCodec::codecForName(codecName.toLocal8Bit());
if (!textCodec) { textCodec = QTextCodec::codecForName("utf-8"); }
const int protocolRev = (server.getServerType() == CServer::FSDServerVatsim) ? PROTOCOL_REVISION_VATSIM_AUTH : PROTOCOL_REVISION_CLASSIC;
QWriteLocker l(&m_lockUserClientBuffered);
m_server = server;
m_protocolRevision = protocolRev;
m_fsdTextCodec = textCodec;
}
void CFSDClient::setSimulatorInfo(const CSimulatorPluginInfo &simInfo)
{
Q_ASSERT_X(m_connectionStatus.isDisconnected(), Q_FUNC_INFO, "Can't change server details while still connected");
Q_ASSERT_X(this->getConnectionStatus().isDisconnected(), Q_FUNC_INFO, "Can't change server details while still connected");
QWriteLocker l(&m_lockUserClientBuffered);
m_simulatorInfo = simInfo;
}
void CFSDClient::setCallsign(const CCallsign &callsign)
{
Q_ASSERT_X(m_connectionStatus.isDisconnected(), Q_FUNC_INFO, "Can't change callsign while still connected");
m_ownCallsign = callsign;
Q_ASSERT_X(this->getConnectionStatus().isDisconnected(), Q_FUNC_INFO, "Can't change callsign while still connected");
updateOwnCallsign(callsign);
QWriteLocker l(&m_lockUserClientBuffered);
m_ownCallsign = callsign;
}
void CFSDClient::setIcaoCodes(const CSimulatedAircraft &ownAircraft)
{
Q_ASSERT_X(m_connectionStatus.isDisconnected(), Q_FUNC_INFO, "Can't change ICAO codes while still connected");
Q_ASSERT_X(this->getConnectionStatus().isDisconnected(), Q_FUNC_INFO, "Can't change ICAO codes while still connected");
updateOwnIcaoCodes(ownAircraft.getAircraftIcaoCode(), ownAircraft.getAirlineIcaoCode());
QWriteLocker l(&m_lockUserClientBuffered);
m_ownAircraftIcaoCode = ownAircraft.getAircraftIcaoCode();
m_ownAirlineIcaoCode = ownAircraft.getAirlineIcaoCode();
m_ownLivery = ownAircraft.getModel().getSwiftLiveryString();
m_ownModelString = ownAircraft.getModelString();
m_sendLiveryString = true;
m_sendMModelString = true;
updateOwnIcaoCodes(m_ownAircraftIcaoCode, m_ownAirlineIcaoCode);
}
void CFSDClient::setLiveryAndModelString(const QString &livery, bool sendLiveryString, const QString &modelString, bool sendModelString)
{
QWriteLocker l(&m_lockUserClientBuffered);
m_ownLivery = livery;
m_ownModelString = modelString;
m_sendLiveryString = sendLiveryString;
@@ -179,6 +183,8 @@ namespace BlackCore
{
//! \fixme Define recognized simulators somewhere */
const CSimulatorInfo::Simulator sim = simInfo.getSimulatorInfo().getSimulator();
QWriteLocker l(&m_lockUserClientBuffered);
switch (sim)
{
case CSimulatorInfo::FSX: m_simType = SimType::MSFSX; break;
@@ -192,6 +198,7 @@ namespace BlackCore
QStringList CFSDClient::getPresetValues() const
{
QReadLocker l(&m_lockUserClientBuffered);
const QStringList v =
{
m_ownModelString,
@@ -206,6 +213,7 @@ namespace BlackCore
void CFSDClient::connectToServer()
{
if (CThreadUtils::callInObjectThread(this, [ = ] { if (sApp && !sApp->isShuttingDown()) { this->connectToServer(); }})) { return; }
if (m_socket.isOpen()) { return; }
Q_ASSERT(!m_clientName.isEmpty());
Q_ASSERT((m_versionMajor + m_versionMinor) > 0);
@@ -217,9 +225,9 @@ namespace BlackCore
m_filterPasswordFromLogin = true;
m_loginSince = QDateTime::currentMSecsSinceEpoch();
const QPointer<CFSDClient> myself(this);
const qint64 timerMs = qRound(PendingConnectionTimeoutMs * 1.25);
const QPointer<CFSDClient> myself(this);
QTimer::singleShot(timerMs, this, [ = ]
{
if (!myself || !sApp || sApp->isShuttingDown()) { return; }
@@ -236,6 +244,8 @@ namespace BlackCore
void CFSDClient::disconnectFromServer()
{
if (this->isDisconnected()) { return; }
if (CThreadUtils::callInObjectThread(this, [ = ] { if (sApp && !sApp->isShuttingDown()) { this->disconnectFromServer(); }})) { return; }
this->stopPositionTimers();
this->updateConnectionStatus(CConnectionStatus::Disconnecting);
@@ -253,18 +263,19 @@ namespace BlackCore
void CFSDClient::sendLogin()
{
const QString cid = m_server.getUser().getId();
const QString cid = m_server.getUser().getId();
const QString password = m_server.getUser().getPassword();
const QString name = m_server.getUser().getRealNameAndHomeBase(); // m_server.getUser().getRealName();
const QString name = m_server.getUser().getRealNameAndHomeBase(); // m_server.getUser().getRealName();
const QString callsign = m_ownCallsign.asString();
if (m_loginMode.isPilot())
{
const AddPilot pilotLogin(m_ownCallsign.asString(), cid, password, m_pilotRating, m_protocolRevision, m_simType, name);
const AddPilot pilotLogin(callsign, cid, password, m_pilotRating, m_protocolRevision, m_simType, name);
sendQueudedMessage(pilotLogin);
}
else if (m_loginMode.isObserver())
{
const AddAtc addAtc(m_ownCallsign.asString(), name, cid, password, m_atcRating, m_protocolRevision);
const AddAtc addAtc(callsign, name, cid, password, m_atcRating, m_protocolRevision);
sendQueudedMessage(addAtc);
}
}
@@ -279,13 +290,13 @@ namespace BlackCore
void CFSDClient::sendDeleteAtc()
{
const QString cid = m_server.getUser().getId();
const DeleteAtc deleteAtc(m_ownCallsign.asString(), cid);
const DeleteAtc deleteAtc(getOwnCallsignAsString(), cid);
sendQueudedMessage(deleteAtc);
}
void CFSDClient::sendPilotDataUpdate()
{
if (m_connectionStatus.isDisconnected() && ! m_unitTestMode) { return; }
if (this->getConnectionStatus().isDisconnected() && ! m_unitTestMode) { return; }
const CSimulatedAircraft myAircraft(getOwnAircraft());
if (m_loginMode == CLoginMode::Observer)
{
@@ -294,7 +305,7 @@ namespace BlackCore
else
{
PilotDataUpdate pilotDataUpdate(myAircraft.getTransponderMode(),
m_ownCallsign.asString(),
getOwnCallsignAsString(),
static_cast<qint16>(myAircraft.getTransponderCode()),
PilotRating::Unknown,
myAircraft.latitude().value(CAngleUnit::deg()),
@@ -312,9 +323,9 @@ namespace BlackCore
void CFSDClient::sendInterimPilotDataUpdate()
{
if (m_connectionStatus.isDisconnected()) { return; }
if (this->getConnectionStatus().isDisconnected()) { return; }
const CSimulatedAircraft myAircraft(getOwnAircraft());
InterimPilotDataUpdate interimPilotDataUpdate(m_ownCallsign.asString(),
InterimPilotDataUpdate interimPilotDataUpdate(getOwnCallsignAsString(),
QString(),
myAircraft.latitude().value(CAngleUnit::deg()),
myAircraft.longitude().value(CAngleUnit::deg()),
@@ -335,20 +346,20 @@ namespace BlackCore
void CFSDClient::sendAtcDataUpdate(double latitude, double longitude)
{
const AtcDataUpdate atcDataUpdate(m_ownCallsign.asString(), 199998, CFacilityType::OBS, 300, AtcRating::Observer, latitude, longitude, 0);
const AtcDataUpdate atcDataUpdate(getOwnCallsignAsString(), 199998, CFacilityType::OBS, 300, AtcRating::Observer, latitude, longitude, 0);
sendQueudedMessage(atcDataUpdate);
}
void CFSDClient::sendPing(const QString &receiver)
{
const qint64 msecSinceEpoch = QDateTime::currentMSecsSinceEpoch();
const QString timeString = QString::number(msecSinceEpoch);
const QString timeString = QString::number(msecSinceEpoch);
const Ping ping(m_ownCallsign.asString(), receiver, timeString);
const Ping ping(getOwnCallsignAsString(), receiver, timeString);
sendQueudedMessage(ping);
// statistics
this->increaseStatisticsValue(QStringLiteral("sendPing"));
increaseStatisticsValue(QStringLiteral("sendPing"));
}
void CFSDClient::sendClientQueryIsValidAtc(const CCallsign &callsign)
@@ -396,36 +407,37 @@ namespace BlackCore
void CFSDClient::sendClientQuery(ClientQueryType queryType, const CCallsign &receiver, const QStringList &queryData)
{
if (queryType == ClientQueryType::Unknown) { return; }
if (CThreadUtils::callInObjectThread(this, [ = ] { if (sApp && !sApp->isShuttingDown()) { this->sendClientQuery(queryType, receiver, queryData); }})) { return; }
const QString reveiverCallsign = receiver.getFsdCallsignString();
if (queryType == ClientQueryType::IsValidATC)
{
const ClientQuery clientQuery(m_ownCallsign.asString(), "SERVER", ClientQueryType::IsValidATC, queryData);
const ClientQuery clientQuery(getOwnCallsignAsString(), "SERVER", ClientQueryType::IsValidATC, queryData);
sendQueudedMessage(clientQuery);
}
else if (queryType == ClientQueryType::Capabilities)
{
const ClientQuery clientQuery(m_ownCallsign.asString(), reveiverCallsign, ClientQueryType::Capabilities);
const ClientQuery clientQuery(getOwnCallsignAsString(), reveiverCallsign, ClientQueryType::Capabilities);
sendQueudedMessage(clientQuery);
}
else if (queryType == ClientQueryType::Com1Freq)
{
const ClientQuery clientQuery(m_ownCallsign.asString(), reveiverCallsign, ClientQueryType::Com1Freq);
const ClientQuery clientQuery(getOwnCallsignAsString(), reveiverCallsign, ClientQueryType::Com1Freq);
sendQueudedMessage(clientQuery);
}
else if (queryType == ClientQueryType::RealName)
{
const ClientQuery clientQuery(m_ownCallsign.asString(), reveiverCallsign, ClientQueryType::RealName);
const ClientQuery clientQuery(getOwnCallsignAsString(), reveiverCallsign, ClientQueryType::RealName);
sendQueudedMessage(clientQuery);
}
else if (queryType == ClientQueryType::Server)
{
ClientQuery clientQuery(m_ownCallsign.asString(), reveiverCallsign, ClientQueryType::Server);
ClientQuery clientQuery(getOwnCallsignAsString(), reveiverCallsign, ClientQueryType::Server);
sendQueudedMessage(clientQuery);
}
else if (queryType == ClientQueryType::ATIS)
{
const ClientQuery clientQuery(m_ownCallsign.asString(), reveiverCallsign, ClientQueryType::ATIS);
const ClientQuery clientQuery(getOwnCallsignAsString(), reveiverCallsign, ClientQueryType::ATIS);
sendQueudedMessage(clientQuery);
if (m_serverType != ServerType::Vatsim)
{
@@ -434,24 +446,24 @@ namespace BlackCore
}
else if (queryType == ClientQueryType::PublicIP)
{
const ClientQuery clientQuery(m_ownCallsign.asString(), reveiverCallsign, ClientQueryType::PublicIP);
const ClientQuery clientQuery(getOwnCallsignAsString(), reveiverCallsign, ClientQueryType::PublicIP);
sendQueudedMessage(clientQuery);
}
else if (queryType == ClientQueryType::INF)
{
const ClientQuery clientQuery(m_ownCallsign.asString(), reveiverCallsign, ClientQueryType::INF);
const ClientQuery clientQuery(getOwnCallsignAsString(), reveiverCallsign, ClientQueryType::INF);
sendQueudedMessage(clientQuery);
}
else if (queryType == ClientQueryType::FP)
{
if (queryData.size() == 0) { return; }
const ClientQuery clientQuery(m_ownCallsign.asString(), "SERVER", ClientQueryType::FP, queryData);
const ClientQuery clientQuery(getOwnCallsignAsString(), "SERVER", ClientQueryType::FP, queryData);
sendQueudedMessage(clientQuery);
}
else if (queryType == ClientQueryType::AircraftConfig)
{
if (queryData.size() == 0) { return; }
const ClientQuery clientQuery(m_ownCallsign.asString(), reveiverCallsign, ClientQueryType::AircraftConfig, queryData);
const ClientQuery clientQuery(getOwnCallsignAsString(), reveiverCallsign, ClientQueryType::AircraftConfig, queryData);
sendQueudedMessage(clientQuery);
}
@@ -461,16 +473,18 @@ namespace BlackCore
void CFSDClient::sendTextMessages(const CTextMessageList &messages)
{
if (messages.isEmpty()) { return; }
if (CThreadUtils::callInObjectThread(this, [ = ] { if (sApp && !sApp->isShuttingDown()) { this->sendTextMessages(messages); }})) { return; }
const CTextMessageList privateMessages = messages.getPrivateMessages().markedAsSent();
const QString ownCallsign = getOwnCallsignAsString();
for (const auto &message : privateMessages)
{
if (message.getRecipientCallsign().isEmpty()) { continue; }
const TextMessage textMessage(m_ownCallsign.asString(), message.getRecipientCallsign().getFsdCallsignString(), message.getMessage());
const TextMessage textMessage(ownCallsign, message.getRecipientCallsign().getFsdCallsignString(), message.getMessage());
sendQueudedMessage(textMessage);
emit this->textMessageSent(message);
this->increaseStatisticsValue(QStringLiteral("sendTextMessages"));
increaseStatisticsValue(QStringLiteral("sendTextMessages.PM"));
emit textMessageSent(message);
}
const CTextMessageList radioMessages = messages.getRadioMessages().markedAsSent();
@@ -488,25 +502,30 @@ namespace BlackCore
freqkHz = freqkHz / 10 * 10;
}
frequencies.push_back(freqkHz);
this->sendRadioMessage(frequencies, message.getMessage());
emit this->textMessageSent(message);
sendRadioMessage(frequencies, message.getMessage());
increaseStatisticsValue(QStringLiteral("sendTextMessages.FREQ"));
emit textMessageSent(message);
}
}
void CFSDClient::sendTextMessage(const CTextMessage &message)
{
if (message.isEmpty()) { return; }
sendTextMessages({message});
}
void CFSDClient::sendTextMessage(TextMessageGroups receiverGroup, const QString &message)
{
if (message.isEmpty()) { return; }
if (CThreadUtils::callInObjectThread(this, [ = ] { if (sApp && !sApp->isShuttingDown()) { this->sendTextMessage(receiverGroup, message); }})) { return; }
QString receiver;
if (receiverGroup == TextMessageGroups::AllClients) { receiver = '*'; }
else if (receiverGroup == TextMessageGroups::AllAtcClients) { receiver = QStringLiteral("*A"); }
else if (receiverGroup == TextMessageGroups::AllPilotClients) { receiver = QStringLiteral("*P"); }
else if (receiverGroup == TextMessageGroups::AllSups) { receiver = QStringLiteral("*S"); }
else { return; }
const TextMessage textMessage(m_ownCallsign.asString(), receiver, message);
const TextMessage textMessage(getOwnCallsignAsString(), receiver, message);
sendQueudedMessage(textMessage);
increaseStatisticsValue(QStringLiteral("sendTextMessages"));
}
@@ -525,13 +544,15 @@ namespace BlackCore
receivers.push_back(QStringLiteral("@%1").arg(frequency - 100000));
}
const TextMessage radioMessage(m_ownCallsign.asString(), receivers.join('&'), message);
this->sendQueudedMessage(radioMessage);
this->increaseStatisticsValue(QStringLiteral("sendTextMessages"));
const TextMessage radioMessage(getOwnCallsignAsString(), receivers.join('&'), message);
sendQueudedMessage(radioMessage);
increaseStatisticsValue(QStringLiteral("sendTextMessages"));
}
void CFSDClient::sendFlightPlan(const CFlightPlan &flightPlan)
{
if (CThreadUtils::callInObjectThread(this, [ = ] { if (sApp && !sApp->isShuttingDown()) { this->sendFlightPlan(flightPlan); }})) { return; }
// Removed with T353 although it is standard
// const QString route = QString(flightPlan.getRoute()).replace(" ", ".");
@@ -559,7 +580,7 @@ namespace BlackCore
const QList<int> timePartsEnroute = flightPlan.getEnrouteTime().getHrsMinSecParts();
const QList<int> timePartsFuel = flightPlan.getFuelTime().getHrsMinSecParts();
const FlightPlan fp(m_ownCallsign.asString(), "SERVER", flightType, act,
const FlightPlan fp(getOwnCallsignAsString(), "SERVER", flightType, act,
flightPlan.getCruiseTrueAirspeed().valueInteger(CSpeedUnit::kts()),
flightPlan.getOriginAirportIcao().asString(),
flightPlan.getTakeoffTimePlanned().toUTC().toString("hhmm").toInt(),
@@ -575,24 +596,30 @@ namespace BlackCore
route);
sendQueudedMessage(fp);
this->increaseStatisticsValue(QStringLiteral("sendFlightPlan"));
increaseStatisticsValue(QStringLiteral("sendFlightPlan"));
}
void CFSDClient::sendPlaneInfoRequest(const CCallsign &receiver)
{
const PlaneInfoRequest planeInfoRequest(m_ownCallsign.asString(), receiver.toQString());
if (CThreadUtils::callInObjectThread(this, [ = ] { if (sApp && !sApp->isShuttingDown()) { this->sendPlaneInfoRequest(receiver); }})) { return; }
const PlaneInfoRequest planeInfoRequest(getOwnCallsignAsString(), receiver.toQString());
sendQueudedMessage(planeInfoRequest);
increaseStatisticsValue(QStringLiteral("sendPlaneInfoRequest"));
}
void CFSDClient::sendPlaneInfoRequestFsinn(const CCallsign &callsign)
{
if (CThreadUtils::callInObjectThread(this, [ = ] { if (sApp && !sApp->isShuttingDown()) { this->sendPlaneInfoRequestFsinn(callsign); }})) { return; }
Q_ASSERT_X(isConnected(), Q_FUNC_INFO, "Can't send to server when disconnected");
const CSimulatedAircraft myAircraft(getOwnAircraft());
QString modelString = m_ownModelString.isEmpty() ? myAircraft.getModelString() : m_ownModelString;
QString modelString;
{
QReadLocker l(&m_lockUserClientBuffered);
modelString = m_ownModelString.isEmpty() ? myAircraft.getModelString() : m_ownModelString;
}
if (modelString.isEmpty()) { modelString = noModelString(); }
const PlaneInfoRequestFsinn planeInfoRequestFsinn(m_ownCallsign.asString(), callsign.toQString(),
const PlaneInfoRequestFsinn planeInfoRequestFsinn(getOwnCallsignAsString(), callsign.toQString(),
myAircraft.getAirlineIcaoCodeDesignator(),
myAircraft.getAircraftIcaoCodeDesignator(),
myAircraft.getAircraftIcaoCombinedType(),
@@ -603,18 +630,18 @@ namespace BlackCore
void CFSDClient::sendPlaneInformation(const QString &receiver, const QString &aircraft, const QString &airline, const QString &livery)
{
PlaneInformation planeInformation(m_ownCallsign.asString(), receiver, aircraft, airline, livery);
const PlaneInformation planeInformation(getOwnCallsignAsString(), receiver, aircraft, airline, livery);
sendQueudedMessage(planeInformation);
increaseStatisticsValue(QStringLiteral("sendPlaneInformation"));
}
void CFSDClient::sendPlaneInformationFsinn(const CCallsign &callsign)
{
if (m_connectionStatus.isDisconnected() && ! m_unitTestMode) { return; }
if (this->getConnectionStatus().isDisconnected() && ! m_unitTestMode) { return; }
const CSimulatedAircraft myAircraft(getOwnAircraft());
QString modelString = m_ownModelString.isEmpty() ? myAircraft.getModelString() : m_ownModelString;
if (modelString.isEmpty()) { modelString = noModelString(); }
const PlaneInformationFsinn planeInformationFsinn(m_ownCallsign.asString(), callsign.toQString(),
const PlaneInformationFsinn planeInformationFsinn(getOwnCallsignAsString(), callsign.toQString(),
myAircraft.getAirlineIcaoCodeDesignator(),
myAircraft.getAircraftIcaoCodeDesignator(),
myAircraft.getAircraftIcaoCombinedType(),
@@ -626,7 +653,7 @@ namespace BlackCore
void CFSDClient::sendAircraftConfiguration(const QString &receiver, const QString &aircraftConfigJson)
{
if (aircraftConfigJson.size() == 0) { return; }
const ClientQuery clientQuery(m_ownCallsign.asString(), receiver, ClientQueryType::AircraftConfig, { aircraftConfigJson });
const ClientQuery clientQuery(getOwnCallsignAsString(), receiver, ClientQueryType::AircraftConfig, { aircraftConfigJson });
sendQueudedMessage(clientQuery);
}
@@ -671,21 +698,21 @@ namespace BlackCore
void CFSDClient::sendAuthChallenge(const QString &challenge)
{
const AuthChallenge pduAuthChallenge(m_ownCallsign.asString(), "SERVER", challenge);
const AuthChallenge pduAuthChallenge(getOwnCallsignAsString(), "SERVER", challenge);
sendQueudedMessage(pduAuthChallenge);
increaseStatisticsValue(QStringLiteral("sendAuthChallenge"));
}
void CFSDClient::sendAuthResponse(const QString &response)
{
const AuthResponse pduAuthResponse(m_ownCallsign.asString(), "SERVER", response);
const AuthResponse pduAuthResponse(getOwnCallsignAsString(), "SERVER", response);
sendQueudedMessage(pduAuthResponse);
increaseStatisticsValue(QStringLiteral("sendAuthResponse"));
}
void CFSDClient::sendPong(const QString &receiver, const QString &timestamp)
{
const Pong pong(m_ownCallsign.asString(), receiver, timestamp);
const Pong pong(getOwnCallsignAsString(), receiver, timestamp);
sendQueudedMessage(pong);
increaseStatisticsValue(QStringLiteral("sendPong"));
}
@@ -699,9 +726,11 @@ namespace BlackCore
return;
}
this->increaseStatisticsValue(QStringLiteral("sendClientResponse"), toQString(queryType));
increaseStatisticsValue(QStringLiteral("sendClientResponse"), toQString(queryType));
QStringList responseData;
const QString ownCallsign = getOwnCallsignAsString();
if (queryType == ClientQueryType::Capabilities)
{
responseData.clear();
@@ -713,14 +742,14 @@ namespace BlackCore
if (m_capabilities & Capabilities::FastPos) responseData.push_back(toQString(Capabilities::FastPos) % "=1");
if (m_capabilities & Capabilities::Stealth) responseData.push_back(toQString(Capabilities::Stealth) % "=1");
if (m_capabilities & Capabilities::AircraftConfig) responseData.push_back(toQString(Capabilities::AircraftConfig) % "=1");
const ClientResponse clientResponse(m_ownCallsign.asString(), receiver, ClientQueryType::Capabilities, responseData);
const ClientResponse clientResponse(ownCallsign, receiver, ClientQueryType::Capabilities, responseData);
sendQueudedMessage(clientResponse);
}
else if (queryType == ClientQueryType::Com1Freq)
{
const QString com1Frequency = QString::number(getOwnAircraft().getCom1System().getFrequencyActive().value(CFrequencyUnit::MHz()), 'f', 3);
responseData.push_back(com1Frequency);
const ClientResponse pduClientResponse(m_ownCallsign.asString(), receiver, ClientQueryType::Com1Freq, responseData);
const ClientResponse pduClientResponse(ownCallsign, receiver, ClientQueryType::Com1Freq, responseData);
sendQueudedMessage(pduClientResponse);
}
else if (queryType == ClientQueryType::RealName)
@@ -734,13 +763,13 @@ namespace BlackCore
if (m_loginMode.isObserver()) { responseData.push_back(toQString(m_atcRating)); }
else { responseData.push_back(toQString(m_pilotRating)); }
const ClientResponse pduClientQueryResponse(m_ownCallsign.asString(), receiver, ClientQueryType::RealName, responseData);
const ClientResponse pduClientQueryResponse(ownCallsign, receiver, ClientQueryType::RealName, responseData);
sendQueudedMessage(pduClientQueryResponse);
}
else if (queryType == ClientQueryType::Server)
{
responseData.push_back(m_server.getAddress());
const ClientResponse pduClientQueryResponse(m_ownCallsign.asString(), receiver, ClientQueryType::Server, responseData);
const ClientResponse pduClientQueryResponse(ownCallsign, receiver, ClientQueryType::Server, responseData);
sendQueudedMessage(pduClientQueryResponse);
}
else if (queryType == ClientQueryType::ATIS)
@@ -754,12 +783,13 @@ namespace BlackCore
else if (queryType == ClientQueryType::INF)
{
const QString cid = m_server.getUser().getId();
const CSimulatedAircraft myAircraft(getOwnAircraft());
const double latitude = getOwnAircraftPosition().latitude().value(CAngleUnit::deg());
const double longitude = getOwnAircraftPosition().longitude().value(CAngleUnit::deg());
const int altitude = getOwnAircraft().getAltitude().valueInteger(CLengthUnit::ft());
const QString realName = m_server.getUser().getRealName();
const CAircraftSituation situation = this->getOwnAircraftPosition();
const double latitude = situation.latitude().value(CAngleUnit::deg());
const double longitude = situation.longitude().value(CAngleUnit::deg());
const int altitude = situation.getAltitude().valueInteger(CLengthUnit::ft());
std::array<char, 50> sysuid = {};
vatsim_get_system_unique_id(sysuid.data());
@@ -768,7 +798,7 @@ namespace BlackCore
" LO=" % QString::number(longitude) % " AL=" % QString::number(altitude) %
" " % realName;
const TextMessage textMessage(m_ownCallsign.asString(), receiver, userInfo);
const TextMessage textMessage(ownCallsign, receiver, userInfo);
sendQueudedMessage(textMessage);
}
else if (queryType == ClientQueryType::FP)
@@ -786,11 +816,11 @@ namespace BlackCore
std::array<char, 50> sysuid = {};
vatsim_get_system_unique_id(sysuid.data());
const QString cid = m_server.getUser().getId();
const ClientIdentification clientIdentification(m_ownCallsign.asString(), vatsim_auth_get_client_id(clientAuth), m_clientName, m_versionMajor, m_versionMinor, cid, sysuid.data(), fsdChallenge);
const ClientIdentification clientIdentification(getOwnCallsignAsString(), vatsim_auth_get_client_id(m_clientAuth), m_clientName, m_versionMajor, m_versionMinor, cid, sysuid.data(), fsdChallenge);
this->sendQueudedMessage(clientIdentification);
this->sendLogin();
this->updateConnectionStatus(CConnectionStatus::Connected);
this->increaseStatisticsValue(QStringLiteral("sendClientIdentification"));
increaseStatisticsValue(QStringLiteral("sendClientIdentification"));
}
void CFSDClient::sendIncrementalAircraftConfig()
@@ -846,7 +876,7 @@ namespace BlackCore
// Filter non-ATC like OBS stations, like pilots logging in as shared cockpit co-pilots.
if (atcDataUpdate.m_facility == CFacilityType::Unknown && !cs.isObserverCallsign()) { return; } // like in old version
if (atcDataUpdate.m_facility == CFacilityType::OBS && !cs.hasSuffix()) { return; }
if (atcDataUpdate.m_facility == CFacilityType::OBS && !cs.hasSuffix()) { return; }
CFrequency freq(atcDataUpdate.m_frequencykHz, CFrequencyUnit::kHz());
freq.switchUnit(CFrequencyUnit::MHz()); // we would not need to bother, but this makes it easier to identify
@@ -862,11 +892,11 @@ namespace BlackCore
{
const AuthChallenge authChallenge = AuthChallenge::fromTokens(tokens);
char response[33];
vatsim_auth_generate_response(clientAuth, qPrintable(authChallenge.m_challengeKey), response);
vatsim_auth_generate_response(m_clientAuth, qPrintable(authChallenge.m_challengeKey), response);
sendAuthResponse(QString(response));
char challenge[33];
vatsim_auth_generate_challenge(serverAuth, challenge);
vatsim_auth_generate_challenge(m_serverAuth, challenge);
m_lastServerAuthChallenge = QString(challenge);
sendAuthChallenge(m_lastServerAuthChallenge);
}
@@ -876,7 +906,7 @@ namespace BlackCore
const AuthResponse authResponse = AuthResponse::fromTokens(tokens);
char expectedResponse[33];
vatsim_auth_generate_response(serverAuth, qPrintable(m_lastServerAuthChallenge), expectedResponse);
vatsim_auth_generate_response(m_serverAuth, qPrintable(m_lastServerAuthChallenge), expectedResponse);
if (authResponse.m_response != QString(expectedResponse))
{
CLogMessage().error(u"The server you are connected to is not a VATSIM server. Disconnecting!");
@@ -994,9 +1024,9 @@ namespace BlackCore
void CFSDClient::handlePong(const QStringList &tokens)
{
Pong pong = Pong::fromTokens(tokens);
qint64 msecSinceEpoch = QDateTime::currentMSecsSinceEpoch();
qint64 elapsedTime = msecSinceEpoch - pong.m_timestamp.toLongLong();
const Pong pong = Pong::fromTokens(tokens);
const qint64 msecSinceEpoch = QDateTime::currentMSecsSinceEpoch();
const qint64 elapsedTime = msecSinceEpoch - pong.m_timestamp.toLongLong();
emit pongReceived(pong.sender(), elapsedTime);
}
@@ -1064,7 +1094,7 @@ namespace BlackCore
fp.m_destAirport,
fp.m_altAirport,
fromStringUtc(depTimePlanned, "hhmm"),
fromStringUtc(depTimeActual, "hhmm"),
fromStringUtc(depTimeActual, "hhmm"),
CTime(fp.m_hoursEnroute * 60 + fp.m_minutesEnroute, CTimeUnit::min()),
CTime(fp.m_fuelAvailHours * 60 + fp.m_fuelAvailMinutes, CTimeUnit::min()),
cruiseAlt,
@@ -1079,7 +1109,7 @@ namespace BlackCore
void CFSDClient::handleClientQuery(const QStringList &tokens)
{
ClientQuery clientQuery = ClientQuery::fromTokens(tokens);
const ClientQuery clientQuery = ClientQuery::fromTokens(tokens);
if (clientQuery.m_queryType == ClientQueryType::Unknown) { return; }
if (clientQuery.m_queryType == ClientQueryType::IsValidATC)
@@ -1369,11 +1399,11 @@ namespace BlackCore
if (m_protocolRevision >= PROTOCOL_REVISION_VATSIM_AUTH)
{
const FSDIdentification fsdIdentification = FSDIdentification::fromTokens(tokens);
vatsim_auth_set_initial_challenge(clientAuth, qPrintable(fsdIdentification.m_initialChallenge));
vatsim_auth_set_initial_challenge(m_clientAuth, qPrintable(fsdIdentification.m_initialChallenge));
char fsdChallenge[33];
vatsim_auth_generate_challenge(serverAuth, fsdChallenge);
vatsim_auth_set_initial_challenge(serverAuth, fsdChallenge);
vatsim_auth_generate_challenge(m_serverAuth, fsdChallenge);
vatsim_auth_set_initial_challenge(m_serverAuth, fsdChallenge);
sendClientIdentification(QString(fsdChallenge));
}
else
@@ -1424,30 +1454,40 @@ namespace BlackCore
void CFSDClient::updateConnectionStatus(CConnectionStatus newStatus)
{
if (m_connectionStatus != newStatus)
if (this->getConnectionStatus() == newStatus) { return; }
if (newStatus.isConnected())
{
if (newStatus.isConnected())
CEcosystem ecoSystem;
{
QWriteLocker l(&m_lockUserClientBuffered);
m_server.setConnectedSinceNow();
this->setCurrentEcosystem(m_server.getEcosystem());
ecoSystem = m_server.getEcosystem();
}
else
{
m_server.markAsDisconnected();
}
if (newStatus.isDisconnected())
{
this->stopPositionTimers();
this->clearState();
this->setLastEcosystem(m_server.getEcosystem());
this->setCurrentEcosystem(CEcosystem::NoSystem);
this->saveNetworkStatistics(m_server.getName());
}
emit this->connectionStatusChanged(m_connectionStatus, newStatus);
qSwap(m_connectionStatus, newStatus);
this->setCurrentEcosystem(ecoSystem);
}
else
{
QWriteLocker l(&m_lockUserClientBuffered);
m_server.markAsDisconnected();
}
if (newStatus.isDisconnected())
{
this->stopPositionTimers();
this->clearState();
this->setLastEcosystem(m_server.getEcosystem());
this->setCurrentEcosystem(CEcosystem::NoSystem);
this->saveNetworkStatistics(m_server.getName());
}
CConnectionStatus oldStatus;
{
QWriteLocker l(&m_lockConnectionStatus);
oldStatus = m_connectionStatus;
m_connectionStatus = newStatus;
}
emit this->connectionStatusChanged(oldStatus, newStatus);
}
void CFSDClient::consolidateTextMessage(const CTextMessage &textMessage)
@@ -1649,11 +1689,18 @@ namespace BlackCore
m_interimPositionReceivers = interimPositionReceivers;
}
bool CFSDClient::isPendingConnection() const
{
return m_connectionStatus.isConnecting() || m_connectionStatus.isDisconnecting();
}
int CFSDClient::increaseStatisticsValue(const QString &identifier, const QString &appendix)
{
if (identifier.isEmpty() || !m_statistics) { return -1; }
QWriteLocker l(&m_lockStatistics);
const QString i = appendix.isEmpty() ? identifier : identifier % u"." % appendix;
int &v = m_callStatistics[i];
int &v = m_callStatistics[i];
v++;
constexpr int MaxTimeValues = 50;
@@ -1664,11 +1711,12 @@ namespace BlackCore
int CFSDClient::increaseStatisticsValue(const QString &identifier, int value)
{
return this->increaseStatisticsValue(identifier, QString::number(value));
return increaseStatisticsValue(identifier, QString::number(value));
}
void CFSDClient::clearStatistics()
{
QWriteLocker l(&m_lockStatistics);
m_callStatistics.clear();
m_callByTime.clear();
}
@@ -1676,9 +1724,17 @@ namespace BlackCore
QString CFSDClient::getNetworkStatisticsAsText(bool reset, const QString &separator)
{
QVector<std::pair<int, QString>> transformed;
if (m_callStatistics.isEmpty()) { return QString(); }
QMap <QString, int> callStatistics;
QVector <QPair<qint64, QString>> callByTime;
for (const auto pair : makePairsRange(as_const(m_callStatistics)))
{
QReadLocker l(&m_lockStatistics);
callStatistics = m_callStatistics;
callByTime = m_callByTime;
}
if (callStatistics.isEmpty()) { return QString(); }
for (const auto pair : makePairsRange(as_const(callStatistics)))
{
// key is pair.first, value is pair.second
transformed.push_back({ pair.second, pair.first });
@@ -1701,10 +1757,10 @@ namespace BlackCore
pair.second % u": " % QString::number(pair.first);
}
if (!m_callByTime.isEmpty())
if (!callByTime.isEmpty())
{
const qint64 lastTs = m_callByTime.front().first;
for (const auto &pair : m_callByTime)
const qint64 lastTs = callByTime.front().first;
for (const auto &pair : callByTime)
{
const qint64 deltaTs = lastTs - pair.first;
stats += separator % QStringLiteral("%1").arg(deltaTs, 5, 10, QChar('0')) % u": " % pair.second;
@@ -1715,9 +1771,10 @@ namespace BlackCore
return stats;
}
CLoginMode CFSDClient::getLoginMode() const
void CFSDClient::gracefulShutdown()
{
return m_loginMode;
disconnectFromServer(); // aysnc, runs in background thread
quitAndWait();
}
void CFSDClient::readDataFromSocket()
@@ -1773,7 +1830,7 @@ namespace BlackCore
// statistics
if (m_statistics)
{
this->increaseStatisticsValue(QStringLiteral("parseMessage"), this->messageTypeToString(messageType));
increaseStatisticsValue(QStringLiteral("parseMessage"), this->messageTypeToString(messageType));
}
if (messageType != MessageType::Unknown)

View File

@@ -28,6 +28,7 @@
#include "blackmisc/network/ecosystemprovider.h"
#include "blackmisc/network/clientprovider.h"
#include "blackmisc/network/textmessagelist.h"
#include "blackmisc/worker.h"
#include "blackmisc/digestsignal.h"
#include "blackmisc/tokenbucket.h"
@@ -41,8 +42,11 @@
#include <QCommandLineOption>
#include <QTimer>
#include <QTextCodec>
#include <QReadWriteLock>
#include <QQueue>
#include <atomic>
//! Protocol version @{
#define PROTOCOL_REVISION_CLASSIC 9
#define PROTOCOL_REVISION_VATSIM_ATC 10
@@ -67,7 +71,7 @@ namespace BlackCore
//! Todo: Send (interim) data updates automatically
//! Todo Check ':' in FSD messages. Disconnect if there is a wrong one
class BLACKCORE_EXPORT CFSDClient :
public QObject,
public BlackMisc::CContinuousWorker,
public BlackMisc::Network::IEcosystemProvider, // provide info about used ecosystem
public BlackMisc::Network::CClientAware, // network can set client information
public BlackMisc::Simulation::COwnAircraftAware, // network vatlib consumes own aircraft data and sets ICAO/callsign data
@@ -79,43 +83,44 @@ namespace BlackCore
public:
//! Ctor
CFSDClient(BlackMisc::Network::IClientProvider *clientProvider,
BlackMisc::Simulation::IOwnAircraftProvider *ownAircraftProvider,
CFSDClient(BlackMisc::Network::IClientProvider *clientProvider,
BlackMisc::Simulation::IOwnAircraftProvider *ownAircraftProvider,
BlackMisc::Simulation::IRemoteAircraftProvider *remoteAircraftProvider,
QObject *parent = nullptr);
QObject *owner = nullptr);
//! Preset functions
//! \remark Necessary functions to setup client. Set them all!
//! \threadsafe
//! @{
void setClientName(const QString &clientName) { m_clientName = clientName; }
void setHostApplication(const QString &hostApplication) { m_hostApplication = hostApplication; }
void setVersion(int major, int minor) { m_versionMajor = major; m_versionMinor = minor; }
void setClientName(const QString &clientName) { QWriteLocker l(&m_lockUserClientBuffered); m_clientName = clientName; }
void setHostApplication(const QString &hostApplication) { QWriteLocker l(&m_lockUserClientBuffered); m_hostApplication = hostApplication; }
void setVersion(int major, int minor) { QWriteLocker l(&m_lockUserClientBuffered); m_versionMajor = major; m_versionMinor = minor; }
void setClientIdAndKey(quint16 id, const QByteArray &key);
void setClientCapabilities(Capabilities capabilities) { m_capabilities = capabilities; }
void setClientCapabilities(Capabilities capabilities) { QWriteLocker l(&m_lockUserClientBuffered); m_capabilities = capabilities; }
void setServer(const BlackMisc::Network::CServer &server);
void setSimulatorInfo(const BlackMisc::Simulation::CSimulatorPluginInfo &simInfo);
void setLoginMode(const BlackMisc::Network::CLoginMode &mode) { m_loginMode = mode; }
void setLoginMode(const BlackMisc::Network::CLoginMode &mode) { QWriteLocker l(&m_lockUserClientBuffered); m_loginMode = mode; }
void setCallsign(const BlackMisc::Aviation::CCallsign &callsign);
void setPartnerCallsign(const BlackMisc::Aviation::CCallsign &callsign) { m_partnerCallsign = callsign; }
void setPartnerCallsign(const BlackMisc::Aviation::CCallsign &callsign) { QWriteLocker l(&m_lockUserClientBuffered); m_partnerCallsign = callsign; }
void setIcaoCodes(const BlackMisc::Simulation::CSimulatedAircraft &ownAircraft);
void setLiveryAndModelString(const QString &livery, bool sendLiveryString, const QString &modelString, bool sendModelString);
void setSimType(SimType type) { m_simType = type; }
void setSimType(SimType type) { QWriteLocker l(&m_lockUserClientBuffered); m_simType = type; }
void setSimType(const BlackMisc::Simulation::CSimulatorPluginInfo &simInfo);
void setPilotRating(PilotRating rating) { m_pilotRating = rating; }
void setAtcRating(AtcRating rating) { m_atcRating = rating; }
void setPilotRating(PilotRating rating) { QWriteLocker l(&m_lockUserClientBuffered); m_pilotRating = rating; }
void setAtcRating(AtcRating rating) { QWriteLocker l(&m_lockUserClientBuffered); m_atcRating = rating; }
//! @}
//! Get the server
const BlackMisc::Network::CServer &getServer() const { return m_server; }
const BlackMisc::Network::CServer &getServer() const { QReadLocker l(&m_lockUserClientBuffered); return m_server; }
//! List of all preset values
QStringList getPresetValues() const;
//! Callsign
BlackMisc::Aviation::CCallsign getPresetPartnerCallsign() const { return m_partnerCallsign; }
BlackMisc::Aviation::CCallsign getPresetPartnerCallsign() const { QReadLocker l(&m_lockUserClientBuffered); return m_partnerCallsign; }
//! Mode
BlackMisc::Network::CLoginMode getLoginMode() const;
BlackMisc::Network::CLoginMode getLoginMode() const { QReadLocker l(&m_lockUserClientBuffered); return m_loginMode; }
//! Connenct/disconnect @{
void connectToServer();
@@ -153,9 +158,10 @@ namespace BlackCore
//! @}
//! Connection status @{
BlackMisc::Network::CConnectionStatus getConnectionStatus() const { return m_connectionStatus; }
bool isConnected() const { return m_connectionStatus.isConnected(); }
bool isPendingConnection() const { return m_connectionStatus.isConnecting() || m_connectionStatus.isDisconnecting(); }
BlackMisc::Network::CConnectionStatus getConnectionStatus() const { QReadLocker l(&m_lockConnectionStatus); return m_connectionStatus; }
bool isConnected() const { return this->getConnectionStatus().isConnected(); }
bool isDisconnected() const { return this->getConnectionStatus().isDisconnected(); }
bool isPendingConnection() const;
//! @}
//! Statistics enable functions @{
@@ -163,11 +169,6 @@ namespace BlackCore
bool isStatisticsEnabled() const { return m_statistics; }
//! @}
//! Increase the statistics value for given identifier @{
int increaseStatisticsValue(const QString &identifier, const QString &appendix = {});
int increaseStatisticsValue(const QString &identifier, int value);
//! @}
//! Clear the statistics
void clearStatistics();
@@ -177,6 +178,9 @@ namespace BlackCore
//! Debugging and UNIT tests
void printToConsole(bool on) { m_printToConsole = on; }
//! Gracefully shut down FSD client
void gracefulShutdown();
signals:
//! Client responses received @{
void atcDataUpdateReceived(const BlackMisc::Aviation::CCallsign &callsign, const BlackMisc::PhysicalQuantities::CFrequency &freq,
@@ -246,6 +250,11 @@ namespace BlackCore
void sendQueuedMessage();
//! @}
//! Increase the statistics value for given identifier @{
int increaseStatisticsValue(const QString &identifier, const QString &appendix = {});
int increaseStatisticsValue(const QString &identifier, int value);
//! @}
//! Message send to FSD
template <class T>
void sendQueudedMessage(const T &message)
@@ -407,37 +416,23 @@ namespace BlackCore
//! String withou colons
static QString noColons(const QString &input);
// Client data
QString m_clientName;
QString m_hostApplication;
int m_versionMajor = 0;
int m_versionMinor = 0;
int m_protocolRevision = 0;
ServerType m_serverType = ServerType::LegacyFsd;
Capabilities m_capabilities = Capabilities::None;
vatsim_auth *clientAuth = nullptr;
vatsim_auth *serverAuth = nullptr;
vatsim_auth *m_clientAuth = nullptr;
vatsim_auth *m_serverAuth = nullptr;
QString m_lastServerAuthChallenge;
qint64 m_loginSince = -1; //!< when login was triggered
static constexpr qint64 PendingConnectionTimeoutMs = 7500;
// User data
BlackMisc::Network::CServer m_server;
BlackMisc::Network::CLoginMode m_loginMode;
SimType m_simType = SimType::Unknown;
PilotRating m_pilotRating = PilotRating::Unknown;
AtcRating m_atcRating = AtcRating::Unknown;
// Parser
QHash<QString, MessageType> m_messageTypeMapping;
QTcpSocket m_socket; //!< used TCP socket
QTcpSocket m_socket { this }; //!< used TCP socket, parent needed as it runs in worker thread
bool m_unitTestMode = false;
bool m_printToConsole = false;
std::atomic_bool m_unitTestMode { false };
std::atomic_bool m_printToConsole { false };
BlackMisc::Network::CConnectionStatus m_connectionStatus;
mutable QReadWriteLock m_lockConnectionStatus { QReadWriteLock::Recursive };
BlackMisc::Aviation::CAircraftParts m_sentAircraftConfig; //!< aircraft parts sent
BlackMisc::CTokenBucket m_tokenBucket; //!< used with aircraft parts messages
BlackMisc::Aviation::CCallsignSet m_interimPositionReceivers; //!< all aircraft receiving interim positions
@@ -458,7 +453,7 @@ namespace BlackCore
//! Pending ATIS query since
struct PendingAtisQuery
{
QDateTime m_queryTime = QDateTime::currentDateTimeUtc();
QDateTime m_queryTime = QDateTime::currentDateTimeUtc();
QStringList m_atisMessage;
};
@@ -468,31 +463,53 @@ namespace BlackCore
BlackMisc::CSettingReadOnly<BlackCore::Vatsim::TRawFsdMessageSetting> m_fsdMessageSetting { this, &CFSDClient::fsdMessageSettingsChanged };
QFile m_rawFsdMessageLogFile;
bool m_rawFsdMessagesEnabled = false;
bool m_filterPasswordFromLogin = false;
std::atomic_bool m_rawFsdMessagesEnabled { false };
std::atomic_bool m_filterPasswordFromLogin { false };
QTimer m_scheduledConfigUpdate;
QTimer m_positionUpdateTimer; //!< sending positions
QTimer m_interimPositionUpdateTimer; //!< sending interim positions
QTimer m_fsdSendMessageTimer; //!< FSD message sending
// timer parents are needed as we move to thread
QTimer m_scheduledConfigUpdate { this }; //!< config updates
QTimer m_positionUpdateTimer { this }; //!< sending positions
QTimer m_interimPositionUpdateTimer { this }; //!< sending interim positions
QTimer m_fsdSendMessageTimer { this }; //!< FSD message sending
qint64 m_additionalOffsetTime = 0; //!< additional offset time
bool m_statistics = false;
QMap <QString, int> m_callStatistics;
QVector <QPair<qint64, QString>> m_callByTime;
std::atomic_bool m_statistics { false };
QMap <QString, int> m_callStatistics; //!< how many calls?
QVector <QPair<qint64, QString>> m_callByTime; //!< "last call vs. ms"
mutable QReadWriteLock m_lockStatistics { QReadWriteLock::Recursive }; //!< for user, client and buffered data
// User data
BlackMisc::Network::CServer m_server;
BlackMisc::Network::CLoginMode m_loginMode;
QTextCodec *m_fsdTextCodec = nullptr;
SimType m_simType = SimType::Unknown;
PilotRating m_pilotRating = PilotRating::Unknown;
AtcRating m_atcRating = AtcRating::Unknown;
// Client data
QString m_clientName;
QString m_hostApplication;
int m_versionMajor = 0;
int m_versionMinor = 0;
int m_protocolRevision = 0;
ServerType m_serverType = ServerType::LegacyFsd;
Capabilities m_capabilities = Capabilities::None;
// buffered data for FSD
BlackMisc::Simulation::CSimulatorPluginInfo m_simulatorInfo; //!< used simulator
BlackMisc::Aviation::CCallsign m_ownCallsign; //!< "buffered callsign", as this must not change when connected
BlackMisc::Aviation::CCallsign m_partnerCallsign; //!< callsign of partner flying in shared cockpit
BlackMisc::Aviation::CCallsign m_partnerCallsign; //!< "buffered"callsign", of partner flying in shared cockpit
BlackMisc::Aviation::CAircraftIcaoCode m_ownAircraftIcaoCode; //!< "buffered icao", as this must not change when connected
BlackMisc::Aviation::CAirlineIcaoCode m_ownAirlineIcaoCode; //!< "buffered icao", as this must not change when connected
QString m_ownLivery; //!< "buffered livery", as this must not change when connected
QString m_ownModelString; //!< "buffered model string", as this must not change when connected
bool m_sendLiveryString = true;
bool m_sendMModelString = true;
std::atomic_bool m_sendLiveryString { true };
std::atomic_bool m_sendMModelString { true };
mutable QReadWriteLock m_lockUserClientBuffered { QReadWriteLock::Recursive }; //!< for user, client and buffered data
QString getOwnCallsignAsString() const { QReadLocker l(&m_lockUserClientBuffered); return m_ownCallsign.asString(); }
QTextCodec *m_fsdTextCodec = nullptr;
QQueue<QString> m_queuedFsdMessages;
//! An illegal FSD state has been detected