6 #include <QHostAddress>
8 #include <QNetworkReply>
52 using namespace swift::config;
53 using namespace swift::core::vatsim;
55 using namespace swift::misc::aviation;
56 using namespace swift::misc::geo;
57 using namespace swift::misc::json;
58 using namespace swift::misc::network;
59 using namespace swift::misc::physical_quantities;
60 using namespace swift::misc::simulation;
62 namespace swift::core::fsd
67 for (
const auto &ch : str)
69 const ushort code = ch.
unicode();
70 if (code < 0x80) { escaped += ch; }
79 QStringList cl = CContinuousWorker::getLogCategories();
95 initializeMessageTypes();
96 connectSocketSignals();
113 fsdMessageSettingsChanged();
123 void CFSDClient::connectSocketSignals()
131 #ifdef SWIFT_VATSIM_SUPPORT
132 void CFSDClient::setClientIdAndKey(quint16
id,
const QByteArray &key)
135 m_clientAuth = vatsim_auth_create(
id, qPrintable(key));
136 m_serverAuth = vatsim_auth_create(
id, qPrintable(key));
143 "Can't change server details while still connected");
148 const int protocolRev = (server.
getServerType() == CServer::FSDServerVatsim) ?
154 m_protocolRevision = protocolRev;
162 "Can't change callsign while still connected");
166 m_ownCallsign = callsign;
172 "Can't change ICAO codes while still connected");
189 bool sendModelString)
192 m_ownLivery = livery;
193 m_ownModelString = modelString;
194 m_sendLiveryString = sendLiveryString;
195 m_sendModelString = sendModelString;
205 case CSimulatorInfo::FSX: m_simType = SimType::MSFSX;
break;
206 case CSimulatorInfo::P3D: m_simType = SimType::P3Dv4;
break;
207 case CSimulatorInfo::FS9: m_simType = SimType::MSFS2004;
break;
208 case CSimulatorInfo::FG: m_simType = SimType::FlightGear;
break;
209 case CSimulatorInfo::XPLANE: m_simType = SimType::XPLANE11;
break;
210 case CSimulatorInfo::MSFS: m_simType = SimType::MSFS;
break;
211 case CSimulatorInfo::MSFS2024: m_simType = SimType::MSFS2024;
break;
212 default: m_simType = SimType::Unknown;
break;
239 if (m_socket->isOpen()) {
return; }
240 Q_ASSERT(!m_clientName.
isEmpty());
241 Q_ASSERT((m_versionMajor + m_versionMinor) > 0);
242 Q_ASSERT(m_capabilities != Capabilities::None);
247 m_filterPasswordFromLogin =
true;
250 const qint64 timerMs = qRound(PendingConnectionTimeoutMs * 1.25);
255 this->pendingTimeoutCheck();
258 this->updateConnectionStatus(CConnectionStatus::Connecting);
260 initiateConnection();
273 this->stopPositionTimers();
274 this->updateConnectionStatus(CConnectionStatus::Disconnecting);
278 if (m_socket->isOpen())
280 if (mode.
isPilot()) { this->sendDeletePilot(); }
281 else if (mode.
isObserver()) { this->sendDeleteAtc(); }
285 this->updateConnectionStatus(CConnectionStatus::Disconnected);
289 void CFSDClient::sendLogin(
const QString &token)
300 const AddPilot pilotLogin(callsign, cid, password, m_pilotRating, m_protocolRevision, m_simType, name);
301 sendQueuedMessage(pilotLogin);
303 << callsign << cid << toQString(m_pilotRating) << m_protocolRevision << toQString(m_simType) << name;
307 const AddAtc addAtc(callsign, name, cid, password, m_atcRating, m_protocolRevision);
308 sendQueuedMessage(addAtc);
310 << callsign << cid << toQString(m_atcRating) << m_protocolRevision << name;
315 this->sendClientQuery(ClientQueryType::EuroscopeSimData, {}, {});
319 void CFSDClient::sendDeletePilot()
323 sendQueuedMessage(deletePilot);
326 void CFSDClient::sendDeleteAtc()
329 const DeleteAtc deleteAtc(getOwnCallsignAsString(), cid);
330 sendQueuedMessage(deleteAtc);
333 void CFSDClient::sendPilotDataUpdate()
337 if (m_loginMode == CLoginMode::Observer)
339 sendAtcDataUpdate(myAircraft.latitude().value(CAngleUnit::deg()),
340 myAircraft.longitude().value(CAngleUnit::deg()));
344 if (this->isVisualPositionSendingEnabledForServer())
349 sendVisualPilotDataUpdate(
true);
353 PilotDataUpdate pilotDataUpdate(
354 myAircraft.getTransponderMode(), getOwnCallsignAsString(),
355 static_cast<qint16
>(myAircraft.getTransponderCode()), r, myAircraft.latitude().value(CAngleUnit::deg()),
356 myAircraft.longitude().value(CAngleUnit::deg()),
357 myAircraft.getAltitude().valueInteger(CLengthUnit::ft()),
358 myAircraft.getPressureAltitude().valueInteger(CLengthUnit::ft()),
359 myAircraft.getGroundSpeed().valueInteger(CSpeedUnit::kts()),
360 myAircraft.getPitch().value(CAngleUnit::deg()), myAircraft.getBank().value(CAngleUnit::deg()),
361 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
362 myAircraft.getParts().isOnGround());
363 sendQueuedMessage(pilotDataUpdate);
367 void CFSDClient::sendInterimPilotDataUpdate()
371 InterimPilotDataUpdate interimPilotDataUpdate(
372 getOwnCallsignAsString(),
QString(), myAircraft.latitude().value(CAngleUnit::deg()),
373 myAircraft.longitude().value(CAngleUnit::deg()), myAircraft.getAltitude().valueInteger(CLengthUnit::ft()),
374 myAircraft.getGroundSpeed().valueInteger(CSpeedUnit::kts()), myAircraft.getPitch().value(CAngleUnit::deg()),
375 myAircraft.getBank().value(CAngleUnit::deg()),
376 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
377 myAircraft.getParts().isOnGround());
379 for (
const auto &receiver : std::as_const(m_interimPositionReceivers))
381 interimPilotDataUpdate.setReceiver(receiver.asString());
382 sendQueuedMessage(interimPilotDataUpdate);
387 void CFSDClient::sendVisualPilotDataUpdate(
bool slowUpdate)
390 if (m_loginMode == CLoginMode::Observer || !isVisualPositionSendingEnabledForServer()) {
return; }
395 static constexpr
double minVelocity = 0.00005;
396 if (std::abs(myAircraft.getVelocity().getVelocityX(CSpeedUnit::m_s())) < minVelocity &&
397 std::abs(myAircraft.getVelocity().getVelocityY(CSpeedUnit::m_s())) < minVelocity &&
398 std::abs(myAircraft.getVelocity().getVelocityZ(CSpeedUnit::m_s())) < minVelocity &&
399 std::abs(myAircraft.getVelocity().getPitchVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity &&
400 std::abs(myAircraft.getVelocity().getRollVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity &&
401 std::abs(myAircraft.getVelocity().getHeadingVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity)
403 if (m_stoppedSendingVisualPositions) {
return; }
404 m_stoppedSendingVisualPositions =
true;
405 m_visualPositionUpdateSentCount = 0;
407 else { m_stoppedSendingVisualPositions =
false; }
409 if (!m_serverWantsVisualPositions) {
return; }
411 VisualPilotDataUpdate visualPilotDataUpdate(
412 getOwnCallsignAsString(), myAircraft.latitude().value(CAngleUnit::deg()),
413 myAircraft.longitude().value(CAngleUnit::deg()), myAircraft.getAltitude().value(CLengthUnit::ft()),
414 myAircraft.getAltitude().value(CLengthUnit::ft()) -
415 myAircraft.getGroundElevation().value(CLengthUnit::ft()),
416 myAircraft.getPitch().value(CAngleUnit::deg()), myAircraft.getBank().value(CAngleUnit::deg()),
417 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
418 myAircraft.getVelocity().getVelocityX(CSpeedUnit::m_s()),
419 myAircraft.getVelocity().getVelocityY(CSpeedUnit::m_s()),
420 myAircraft.getVelocity().getVelocityZ(CSpeedUnit::m_s()),
421 myAircraft.getVelocity().getPitchVelocity(CAngleUnit::rad(), CTimeUnit::s()),
422 myAircraft.getVelocity().getRollVelocity(CAngleUnit::rad(), CTimeUnit::s()),
423 myAircraft.getVelocity().getHeadingVelocity(CAngleUnit::rad(), CTimeUnit::s()));
425 if (m_stoppedSendingVisualPositions) { sendQueuedMessage(visualPilotDataUpdate.toStopped()); }
426 else if (m_visualPositionUpdateSentCount++ % 25 == 0) { sendQueuedMessage(visualPilotDataUpdate.toPeriodic()); }
427 else { sendQueuedMessage(visualPilotDataUpdate); }
430 void CFSDClient::sendAtcDataUpdate(
double latitude,
double longitude)
432 const AtcDataUpdate atcDataUpdate(getOwnCallsignAsString(), 199998, CFacilityType::OBS, 300,
433 AtcRating::Observer, latitude, longitude, 0);
434 sendQueuedMessage(atcDataUpdate);
437 void CFSDClient::sendPing(
const QString &receiver)
442 const Ping ping(getOwnCallsignAsString(), receiver, timeString);
443 sendQueuedMessage(ping);
446 increaseStatisticsValue(QStringLiteral(
"sendPing"));
449 void CFSDClient::sendClientQueryIsValidAtc(
const CCallsign &callsign)
451 sendClientQuery(ClientQueryType::IsValidATC, {}, { callsign.
asString() });
454 void CFSDClient::sendClientQueryCapabilities(
const CCallsign &callsign)
456 sendClientQuery(ClientQueryType::Capabilities, callsign);
461 sendClientQuery(ClientQueryType::Com1Freq, callsign);
466 sendClientQuery(ClientQueryType::RealName, callsign);
471 sendClientQuery(ClientQueryType::Server, callsign);
476 sendClientQuery(ClientQueryType::ATIS, callsign);
481 sendClientQuery(ClientQueryType::FP, {}, { callsign.
toQString() });
487 data = convertToUnicodeEscaped(data);
488 sendClientQuery(ClientQueryType::AircraftConfig, callsign, { data });
493 if (queryType == ClientQueryType::Unknown) {
return; }
503 if (queryType == ClientQueryType::IsValidATC)
505 const ClientQuery clientQuery(getOwnCallsignAsString(),
"SERVER", ClientQueryType::IsValidATC, queryData);
506 sendQueuedMessage(clientQuery);
508 else if (queryType == ClientQueryType::Capabilities)
510 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Capabilities);
511 sendQueuedMessage(clientQuery);
513 else if (queryType == ClientQueryType::Com1Freq)
515 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Com1Freq);
516 sendQueuedMessage(clientQuery);
518 else if (queryType == ClientQueryType::RealName)
520 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::RealName);
521 sendQueuedMessage(clientQuery);
523 else if (queryType == ClientQueryType::Server)
525 ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Server);
526 sendQueuedMessage(clientQuery);
528 else if (queryType == ClientQueryType::ATIS)
530 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::ATIS);
531 sendQueuedMessage(clientQuery);
532 if (m_serverType != ServerType::Vatsim) { m_pendingAtisQueries.
insert(receiver, {}); }
534 else if (queryType == ClientQueryType::PublicIP)
536 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::PublicIP);
537 sendQueuedMessage(clientQuery);
539 else if (queryType == ClientQueryType::INF)
541 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::INF);
542 sendQueuedMessage(clientQuery);
544 else if (queryType == ClientQueryType::FP)
546 if (queryData.
isEmpty()) {
return; }
547 const ClientQuery clientQuery(getOwnCallsignAsString(),
"SERVER", ClientQueryType::FP, queryData);
548 sendQueuedMessage(clientQuery);
550 else if (queryType == ClientQueryType::AircraftConfig)
552 if (queryData.
isEmpty()) {
return; }
553 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::AircraftConfig,
555 sendQueuedMessage(clientQuery);
557 else if (queryType == ClientQueryType::EuroscopeSimData)
559 const ClientQuery clientQuery(getOwnCallsignAsString(),
"@94835", ClientQueryType::EuroscopeSimData,
561 sendQueuedMessage(clientQuery);
564 increaseStatisticsValue(QStringLiteral(
"sendClientQuery"), toQString(queryType));
569 if (messages.
isEmpty()) {
return; }
579 const QString ownCallsign = getOwnCallsignAsString();
581 for (
const auto &message : privateMessages)
583 if (message.getRecipientCallsign().isEmpty()) {
continue; }
584 const TextMessage textMessage(ownCallsign, message.getRecipientCallsign().getFsdCallsignString(),
585 message.getMessage());
586 sendQueuedMessage(textMessage);
587 increaseStatisticsValue(QStringLiteral(
"sendTextMessages.PM"));
593 for (
const auto &message : radioMessages)
598 message.getFrequency() :
606 frequencies.push_back(freqkHz);
608 increaseStatisticsValue(QStringLiteral(
"sendTextMessages.FREQ"));
615 if (message.
isEmpty()) {
return; }
621 if (message.
isEmpty()) {
return; }
631 if (receiverGroup == TextMessageGroups::AllClients) { receiver =
'*'; }
632 else if (receiverGroup == TextMessageGroups::AllAtcClients) { receiver = QStringLiteral(
"*A"); }
633 else if (receiverGroup == TextMessageGroups::AllPilotClients) { receiver = QStringLiteral(
"*P"); }
634 else if (receiverGroup == TextMessageGroups::AllSups) { receiver = QStringLiteral(
"*S"); }
636 const TextMessage textMessage(getOwnCallsignAsString(), receiver, message);
637 sendQueuedMessage(textMessage);
638 if (receiver == QStringLiteral(
"*S"))
646 increaseStatisticsValue(QStringLiteral(
"sendTextMessages"));
658 for (
const int &frequency : frequencieskHz)
660 receivers.push_back(QStringLiteral(
"@%1").arg(frequency - 100000));
664 sendQueuedMessage(radioMessage);
665 increaseStatisticsValue(QStringLiteral(
"sendTextMessages"));
700 Q_ASSERT_X(!act.
isEmpty(), Q_FUNC_INFO,
"Aircraft type must not be empty");
704 const FlightPlan fp(getOwnCallsignAsString(),
"SERVER", flightType, act,
710 timePartsEnroute[CTime::Minutes], timePartsFuel[CTime::Hours],
714 sendQueuedMessage(fp);
715 increaseStatisticsValue(QStringLiteral(
"sendFlightPlan"));
729 sendQueuedMessage(planeInfoRequest);
730 increaseStatisticsValue(QStringLiteral(
"sendPlaneInfoRequest"));
744 SWIFT_VERIFY_X(connected, Q_FUNC_INFO,
"Can't send to server when disconnected");
745 if (!connected) {
return; }
748 const QString modelString = this->getConfiguredModelString(myAircraft);
752 sendQueuedMessage(planeInfoRequestFsinn);
753 increaseStatisticsValue(QStringLiteral(
"sendPlaneInfoRequestFsinn"));
756 void CFSDClient::sendPlaneInformation(
const QString &receiver,
const QString &aircraft,
const QString &airline,
759 const PlaneInformation planeInformation(getOwnCallsignAsString(), receiver, aircraft, airline, livery);
760 sendQueuedMessage(planeInformation);
761 increaseStatisticsValue(QStringLiteral(
"sendPlaneInformation"));
764 void CFSDClient::sendPlaneInformationFsinn(
const CCallsign &callsign)
768 const QString modelString = this->getConfiguredModelString(myAircraft);
769 const PlaneInformationFsinn planeInformationFsinn(
770 getOwnCallsignAsString(), callsign.
toQString(), myAircraft.getAirlineIcaoCodeDesignator(),
771 myAircraft.getAircraftIcaoCodeDesignator(), myAircraft.getAircraftIcaoCombinedType(), modelString);
772 sendQueuedMessage(planeInformationFsinn);
773 increaseStatisticsValue(QStringLiteral(
"sendPlaneInformationFsinn"));
776 void CFSDClient::sendAircraftConfiguration(
const QString &receiver,
const QString &aircraftConfigJson)
778 if (aircraftConfigJson.
size() == 0) {
return; }
779 const ClientQuery clientQuery(getOwnCallsignAsString(), receiver, ClientQueryType::AircraftConfig,
780 { aircraftConfigJson });
781 sendQueuedMessage(clientQuery);
784 void CFSDClient::sendMessageString(
const QString &message)
786 if (message.
isEmpty()) {
return; }
787 const QByteArray bufferEncoded = m_encoder(message);
788 if (m_printToConsole) { qDebug() <<
"FSD Sent=>" << bufferEncoded; }
789 if (!m_unitTestMode) { m_socket->write(bufferEncoded); }
792 emitRawFsdMessage(message.
trimmed(),
true);
795 void CFSDClient::sendQueuedMessage()
797 if (m_queuedFsdMessages.isEmpty()) {
return; }
798 const qsizetype s = m_queuedFsdMessages.size();
799 this->sendMessageString(m_queuedFsdMessages.
dequeue());
802 if (s > 5) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
803 if (s > 10) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
804 if (s > 20) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
805 if (s > 30) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
811 const StatusSeverity severity = s > 75 ? SeverityWarning : SeverityInfo;
812 CLogMessage(
this).
log(severity, u
"Too many queued messages (%1), bulk send!") << s;
814 if (s > 75) { sendNo = 20; }
815 if (s > 100) { sendNo = 30; }
817 for (
int i = 0; i < sendNo; i++) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
821 void CFSDClient::sendFsdMessage(
const QString &message)
824 parseMessage(message);
829 if (!m_sendModelString) {
return noModelString(); }
832 return ms.
isEmpty() ? noModelString() : ms;
837 if (!m_sendLiveryString) {
return {}; }
843 void CFSDClient::sendAuthChallenge(
const QString &challenge)
845 const AuthChallenge pduAuthChallenge(getOwnCallsignAsString(),
"SERVER", challenge);
846 sendDirectMessage(pduAuthChallenge);
847 increaseStatisticsValue(QStringLiteral(
"sendAuthChallenge"));
850 void CFSDClient::sendAuthResponse(
const QString &response)
852 const AuthResponse pduAuthResponse(getOwnCallsignAsString(),
"SERVER", response);
853 sendDirectMessage(pduAuthResponse);
854 increaseStatisticsValue(QStringLiteral(
"sendAuthResponse"));
857 void CFSDClient::sendPong(
const QString &receiver,
const QString ×tamp)
859 const Pong pong(getOwnCallsignAsString(), receiver, timestamp);
860 sendQueuedMessage(pong);
861 increaseStatisticsValue(QStringLiteral(
"sendPong"));
864 void CFSDClient::sendClientResponse(ClientQueryType queryType,
const QString &receiver)
866 if (queryType == ClientQueryType::Unknown) {
return; }
867 if (queryType == ClientQueryType::IsValidATC)
869 this->handleIllegalFsdState(
"Never use sendClientResponse with IsValidATC from the client");
873 increaseStatisticsValue(QStringLiteral(
"sendClientResponse"), toQString(queryType));
876 const QString ownCallsign = getOwnCallsignAsString();
878 if (queryType == ClientQueryType::Capabilities)
880 responseData.
clear();
881 if (m_capabilities & Capabilities::AtcInfo) responseData.
push_back(toQString(Capabilities::AtcInfo) %
"=1");
882 if (m_capabilities & Capabilities::SecondaryPos)
883 responseData.
push_back(toQString(Capabilities::SecondaryPos) %
"=1");
884 if (m_capabilities & Capabilities::AircraftInfo)
885 responseData.
push_back(toQString(Capabilities::AircraftInfo) %
"=1");
886 if (m_capabilities & Capabilities::OngoingCoord)
887 responseData.
push_back(toQString(Capabilities::OngoingCoord) %
"=1");
888 if (m_capabilities & Capabilities::InterminPos)
889 responseData.
push_back(toQString(Capabilities::InterminPos) %
"=1");
890 if (m_capabilities & Capabilities::FastPos) responseData.
push_back(toQString(Capabilities::FastPos) %
"=1");
891 if (m_capabilities & Capabilities::VisPos) responseData.
push_back(toQString(Capabilities::VisPos) %
"=1");
892 if (m_capabilities & Capabilities::Stealth) responseData.
push_back(toQString(Capabilities::Stealth) %
"=1");
893 if (m_capabilities & Capabilities::AircraftConfig)
894 responseData.
push_back(toQString(Capabilities::AircraftConfig) %
"=1");
895 if (m_capabilities & Capabilities::IcaoEquipment)
896 responseData.
push_back(toQString(Capabilities::IcaoEquipment) %
"=1");
897 const ClientResponse clientResponse(ownCallsign, receiver, ClientQueryType::Capabilities, responseData);
898 sendQueuedMessage(clientResponse);
900 else if (queryType == ClientQueryType::Com1Freq)
903 getOwnAircraft().getCom1System().getFrequencyActive().value(CFrequencyUnit::MHz()),
'f', 3);
905 const ClientResponse pduClientResponse(ownCallsign, receiver, ClientQueryType::Com1Freq, responseData);
906 sendQueuedMessage(pduClientResponse);
908 else if (queryType == ClientQueryType::RealName)
917 else { responseData.
push_back(toQString(m_pilotRating)); }
919 const ClientResponse pduClientQueryResponse(ownCallsign, receiver, ClientQueryType::RealName, responseData);
920 sendQueuedMessage(pduClientQueryResponse);
922 else if (queryType == ClientQueryType::Server)
925 const ClientResponse pduClientQueryResponse(ownCallsign, receiver, ClientQueryType::Server, responseData);
926 sendQueuedMessage(pduClientQueryResponse);
928 else if (queryType == ClientQueryType::ATIS)
930 this->handleIllegalFsdState(
931 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::ATIS)));
933 else if (queryType == ClientQueryType::PublicIP)
935 this->handleIllegalFsdState(
936 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::PublicIP)));
938 else if (queryType == ClientQueryType::INF)
944 const double latitude = situation.
latitude().
value(CAngleUnit::deg());
945 const double longitude = situation.
longitude().
value(CAngleUnit::deg());
948 std::array<char, 50> sysuid = {};
949 #ifdef SWIFT_VATSIM_SUPPORT
950 vatsim_get_system_unique_id(sysuid.data());
953 const QString userInfo = QStringLiteral(
"CID=") % cid %
" " % m_clientName %
" IP=" %
954 m_socket->localAddress().toString() %
" SYS_UID=" % sysuid.data() %
" FSVER=" %
958 const TextMessage textMessage(ownCallsign, receiver, userInfo);
959 sendQueuedMessage(textMessage);
961 else if (queryType == ClientQueryType::FP)
963 this->handleIllegalFsdState(
964 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::FP)));
966 else if (queryType == ClientQueryType::AircraftConfig)
968 this->handleIllegalFsdState(
969 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::AircraftConfig)));
973 #ifdef SWIFT_VATSIM_SUPPORT
974 void CFSDClient::sendClientIdentification(
const QString &fsdChallenge)
976 std::array<char, 50> sysuid = {};
977 vatsim_get_system_unique_id(sysuid.data());
979 const ClientIdentification clientIdentification(
980 getOwnCallsignAsString(), vatsim_auth_get_client_id(m_clientAuth), m_clientName, m_versionMajor,
981 m_versionMinor, cid, sysuid.
data(), fsdChallenge);
982 this->sendQueuedMessage(clientIdentification);
984 if (
getServer().getEcosystem().isSystem(CEcosystem::VATSIM))
987 { this, [this](const QString &token) {
988 this->sendLogin(token);
989 this->updateConnectionStatus(CConnectionStatus::Connected);
995 this->updateConnectionStatus(CConnectionStatus::Connected);
997 increaseStatisticsValue(QStringLiteral(
"sendClientIdentification"));
1001 void CFSDClient::getVatsimAuthToken(
const QString &cid,
const QString &password,
1004 Q_ASSERT_X(
sApp, Q_FUNC_INFO,
"Need app");
1007 const QJsonObject jsonRequest { {
"cid", cid }, {
"password", password } };
1019 nwReply->errorString();
1022 disconnectFromServer();
1024 nwReply->deleteLater();
1028 void CFSDClient::sendIncrementalAircraftConfig()
1030 if (!m_unitTestMode && (!this->isConnected() || !this->getSetupForServer().sendAircraftParts())) {
return; }
1034 if (m_sentAircraftConfig == currentParts) {
return; }
1036 if (!m_tokenBucket.tryConsume()) {
return; }
1038 const QJsonObject previousConfig = m_sentAircraftConfig.toJson();
1039 const QJsonObject currentConfig = currentParts.toJson();
1040 const QJsonObject incrementalConfig = getIncrementalObject(previousConfig, currentConfig);
1042 const QString dataStr = convertToUnicodeEscaped(
1045 sendAircraftConfiguration(
"@94836", dataStr);
1046 m_sentAircraftConfig = currentParts;
1049 void CFSDClient::initializeMessageTypes()
1051 m_messageTypeMapping[
"#AA"] = MessageType::AddAtc;
1052 m_messageTypeMapping[
"#AP"] = MessageType::AddPilot;
1053 m_messageTypeMapping[
"%"] = MessageType::AtcDataUpdate;
1054 m_messageTypeMapping[
"$ZC"] = MessageType::AuthChallenge;
1055 m_messageTypeMapping[
"$ZR"] = MessageType::AuthResponse;
1056 m_messageTypeMapping[
"$ID"] = MessageType::ClientIdentification;
1057 m_messageTypeMapping[
"$CQ"] = MessageType::ClientQuery;
1058 m_messageTypeMapping[
"$CR"] = MessageType::ClientResponse;
1059 m_messageTypeMapping[
"#DA"] = MessageType::DeleteATC;
1060 m_messageTypeMapping[
"#DP"] = MessageType::DeletePilot;
1061 m_messageTypeMapping[
"$FP"] = MessageType::FlightPlan;
1062 m_messageTypeMapping[
"#PC"] = MessageType::ProController;
1063 m_messageTypeMapping[
"$DI"] = MessageType::FsdIdentification;
1064 m_messageTypeMapping[
"$!!"] = MessageType::KillRequest;
1065 m_messageTypeMapping[
"@"] = MessageType::PilotDataUpdate;
1066 m_messageTypeMapping[
"^"] = MessageType::VisualPilotDataUpdate;
1067 m_messageTypeMapping[
"#SL"] = MessageType::VisualPilotDataPeriodic;
1068 m_messageTypeMapping[
"#ST"] = MessageType::VisualPilotDataStopped;
1069 m_messageTypeMapping[
"$SF"] = MessageType::VisualPilotDataToggle;
1070 m_messageTypeMapping[
"$PI"] = MessageType::Ping;
1071 m_messageTypeMapping[
"$PO"] = MessageType::Pong;
1072 m_messageTypeMapping[
"$ER"] = MessageType::ServerError;
1073 m_messageTypeMapping[
"#DL"] = MessageType::ServerHeartbeat;
1074 m_messageTypeMapping[
"#TM"] = MessageType::TextMessage;
1075 m_messageTypeMapping[
"#SB"] = MessageType::PilotClientCom;
1076 m_messageTypeMapping[
"$XX"] = MessageType::Rehost;
1077 m_messageTypeMapping[
"#MU"] = MessageType::Mute;
1080 m_messageTypeMapping[
"SIMDATA"] = MessageType::EuroscopeSimData;
1083 void CFSDClient::handleAtcDataUpdate(
const QStringList &tokens)
1085 const AtcDataUpdate atcDataUpdate = AtcDataUpdate::fromTokens(tokens);
1086 const QString senderCs = atcDataUpdate.sender();
1087 const CCallsign cs(senderCs, CCallsign::Atc);
1090 if (atcDataUpdate.m_facility == CFacilityType::Unknown && !cs.isObserverCallsign())
1094 if (atcDataUpdate.m_facility == CFacilityType::OBS && !cs.hasSuffix()) {
return; }
1096 CFrequency freq(atcDataUpdate.m_frequencykHz, CFrequencyUnit::kHz());
1097 freq.switchUnit(CFrequencyUnit::MHz());
1103 const CLength networkRange(atcDataUpdate.m_visibleRange, CLengthUnit::NM());
1104 const CLength range = fixAtcRange(networkRange, cs);
1105 const CCoordinateGeodetic position(atcDataUpdate.m_latitude, atcDataUpdate.m_longitude, 0);
1107 emit this->atcDataUpdateReceived(cs, freq, position, range);
1109 m_atcStations.replaceOrAddObjectByCallsign({ cs, {}, freq, position, range });
1112 #ifdef SWIFT_VATSIM_SUPPORT
1113 void CFSDClient::handleAuthChallenge(
const QStringList &tokens)
1115 const AuthChallenge authChallenge = AuthChallenge::fromTokens(tokens);
1117 vatsim_auth_generate_response(m_clientAuth, qPrintable(authChallenge.m_challengeKey), response);
1118 sendAuthResponse(
QString(response));
1121 vatsim_auth_generate_challenge(m_serverAuth, challenge);
1122 m_lastServerAuthChallenge =
QString(challenge);
1123 sendAuthChallenge(m_lastServerAuthChallenge);
1127 #ifdef SWIFT_VATSIM_SUPPORT
1128 void CFSDClient::handleAuthResponse(
const QStringList &tokens)
1130 const AuthResponse authResponse = AuthResponse::fromTokens(tokens);
1132 char expectedResponse[33];
1133 vatsim_auth_generate_response(m_serverAuth, qPrintable(m_lastServerAuthChallenge), expectedResponse);
1134 if (authResponse.m_response !=
QString(expectedResponse))
1136 CLogMessage().
error(u
"The server you are connected to is not a VATSIM server. Disconnecting!");
1137 disconnectFromServer();
1142 void CFSDClient::handleDeleteATC(
const QStringList &tokens)
1144 const DeleteAtc deleteAtc = DeleteAtc::fromTokens(tokens);
1145 emit deleteAtcReceived(deleteAtc.m_cid);
1147 m_atcStations.removeByCallsign(deleteAtc.m_cid);
1150 void CFSDClient::handleDeletePilot(
const QStringList &tokens)
1152 const DeletePilot deletePilot = DeletePilot::fromTokens(tokens);
1153 const CCallsign cs(deletePilot.sender(), CCallsign::Aircraft);
1155 emit deletePilotReceived(deletePilot.m_cid);
1158 void CFSDClient::handleTextMessage(
const QStringList &tokens)
1160 const TextMessage textMessage = TextMessage::fromTokens(tokens);
1162 const CCallsign sender(textMessage.sender());
1163 const CCallsign receiver(textMessage.receiver());
1165 if (textMessage.m_type == TextMessage::PrivateMessage)
1171 if (m_server.getServerType() != CServer::FSDServerVatsim &&
1172 m_ownCallsign.asString() == textMessage.receiver() && m_pendingAtisQueries.contains(sender))
1174 maybeHandleAtisReply(sender, receiver, textMessage.m_message);
1178 CTextMessage tm(textMessage.m_message, sender, receiver);
1179 tm.setCurrentUtcTime();
1180 this->consolidateTextMessage(tm);
1182 else if (textMessage.m_type == TextMessage::RadioMessage)
1184 const CFrequency com1 = getOwnAircraft().getCom1System().getFrequencyActive();
1185 const CFrequency com2 = getOwnAircraft().getCom2System().getFrequencyActive();
1188 for (
int freqKhz : textMessage.m_frequencies)
1190 CFrequency f(freqKhz, CFrequencyUnit::kHz());
1191 CComSystem::roundToChannelSpacing(f, CComSystem::ChannelSpacing8_33KHz);
1192 if (f == com1 || f == com2) { frequencies.
push_back(f); }
1194 if (frequencies.
isEmpty()) {
return; }
1196 messages.setCurrentUtcTime();
1197 emit textMessagesReceived(messages);
1201 void CFSDClient::handlePilotDataUpdate(
const QStringList &tokens)
1203 const PilotDataUpdate dataUpdate = PilotDataUpdate::fromTokens(tokens);
1204 const CCallsign callsign(dataUpdate.sender(), CCallsign::Aircraft);
1207 callsign,
CCoordinateGeodetic(dataUpdate.m_latitude, dataUpdate.m_longitude, dataUpdate.m_altitudeTrue),
1208 CHeading(dataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1209 CAngle(dataUpdate.m_pitch, CAngleUnit::deg()),
CAngle(dataUpdate.m_bank, CAngleUnit::deg()),
1210 CSpeed(dataUpdate.m_groundSpeed, CSpeedUnit::kts()));
1212 CAltitude::PressureAltitude, CLengthUnit::ft()));
1214 const COnGroundInfo og(dataUpdate.m_onGround ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
1215 COnGroundInfo::NotSetGroundDetails);
1220 const qint64 offsetTimeMs =
1227 if (CTransponder::isValidTransponderCode(dataUpdate.m_transponderCode))
1229 transponder =
CTransponder(dataUpdate.m_transponderCode, dataUpdate.m_transponderMode);
1233 if (CBuildConfig::isLocalDeveloperDebugBuild())
1236 << dataUpdate.m_transponderCode << callsign;
1240 transponder =
CTransponder(2000, CTransponder::StateStandby);
1242 emit pilotDataUpdateReceived(situation, transponder);
1245 void CFSDClient::handleEuroscopeSimData(
const QStringList &tokens)
1251 CHeading(data.m_heading, CAngleUnit::deg()),
1252 CAngle(-data.m_pitch, CAngleUnit::deg()),
CAngle(-data.m_bank, CAngleUnit::deg()),
1253 CSpeed(data.m_groundSpeed, CSpeedUnit::kts()));
1255 const COnGroundInfo og(data.m_onGround ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
1256 COnGroundInfo::NotSetGroundDetails);
1261 const qint64 offsetTimeMs =
1270 emit euroscopeSimDataUpdatedReceived(situation, parts, currentOffsetTime(data.sender()), data.m_model,
1277 VisualPilotDataUpdate dataUpdate;
1278 switch (messageType)
1280 case MessageType::VisualPilotDataUpdate: dataUpdate = VisualPilotDataUpdate::fromTokens(tokens);
break;
1281 case MessageType::VisualPilotDataPeriodic: dataUpdate = VisualPilotDataPeriodic::fromTokens(tokens).toUpdate();
break;
1282 case MessageType::VisualPilotDataStopped: dataUpdate = VisualPilotDataStopped::fromTokens(tokens).toUpdate();
break;
1283 default: qFatal(
"Precondition violated");
break;
1285 const CCallsign callsign(dataUpdate.sender(), CCallsign::Aircraft);
1289 CCoordinateGeodetic(dataUpdate.m_latitude, dataUpdate.m_longitude, dataUpdate.m_altitudeTrue),
1290 CHeading(dataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1291 CAngle(dataUpdate.m_pitch, CAngleUnit::deg()),
1292 CAngle(dataUpdate.m_bank, CAngleUnit::deg()));
1304 emit visualPilotDataUpdateReceived(situation);
1308 void CFSDClient::handleVisualPilotDataToggle(
const QStringList &tokens)
1310 const VisualPilotDataToggle toggle = VisualPilotDataToggle::fromTokens(tokens);
1311 m_serverWantsVisualPositions = toggle.m_active;
1314 void CFSDClient::handlePing(
const QStringList &tokens)
1316 const Ping ping = Ping::fromTokens(tokens);
1317 sendPong(ping.sender(), ping.m_timestamp);
1320 void CFSDClient::handlePong(
const QStringList &tokens)
1322 const Pong pong = Pong::fromTokens(tokens);
1324 const qint64 elapsedTime = msecSinceEpoch - pong.m_timestamp.toLongLong();
1325 emit pongReceived(pong.sender(),
static_cast<double>(elapsedTime));
1328 void CFSDClient::handleKillRequest(
const QStringList &tokens)
1330 KillRequest killRequest = KillRequest::fromTokens(tokens);
1331 emit killRequestReceived(killRequest.m_reason);
1332 disconnectFromServer();
1335 void CFSDClient::handleFlightPlan(
const QStringList &tokens)
1337 FlightPlan fp = FlightPlan::fromTokens(tokens);
1340 switch (fp.m_flightType)
1342 case FlightType::VFR: rules = CFlightPlan::VFR;
break;
1343 case FlightType::IFR: rules = CFlightPlan::IFR;
break;
1344 case FlightType::DVFR: rules = CFlightPlan::DVFR;
break;
1345 case FlightType::SVFR: rules = CFlightPlan::SVFR;
break;
1351 int ca = cruiseAltString.
toInt();
1355 if (rules == CFlightPlan::IFR)
1357 if (ca >= 1000) { cruiseAltString = u
"FL" %
QString::number(ca / 100); }
1358 else { cruiseAltString = u
"FL" % cruiseAltString; }
1362 if (ca >= 5000) { cruiseAltString = u
"FL" %
QString::number(ca / 100); }
1363 else { cruiseAltString = cruiseAltString % u
"ft"; }
1367 cruiseAlt.
parseFromString(cruiseAltString, CPqString::SeparatorBestGuess);
1372 const CCallsign callsign(fp.sender(), CCallsign::Aircraft);
1376 CTime(fp.m_hoursEnroute * 60 + fp.m_minutesEnroute, CTimeUnit::min()),
1377 CTime(fp.m_fuelAvailHours * 60 + fp.m_fuelAvailMinutes, CTimeUnit::min()), cruiseAlt,
1378 CSpeed(fp.m_trueCruisingSpeed, CSpeedUnit::kts()), rules, fp.m_route, fp.m_remarks);
1380 emit flightPlanReceived(callsign, flightPlan);
1383 void CFSDClient::handleClientQuery(
const QStringList &tokens)
1385 const ClientQuery clientQuery = ClientQuery::fromTokens(tokens);
1387 if (clientQuery.m_queryType == ClientQueryType::Unknown) {
return; }
1388 if (clientQuery.m_queryType == ClientQueryType::IsValidATC)
1392 else if (clientQuery.m_queryType == ClientQueryType::Capabilities)
1394 sendClientResponse(ClientQueryType::Capabilities, clientQuery.sender());
1396 else if (clientQuery.m_queryType == ClientQueryType::Com1Freq)
1398 sendClientResponse(ClientQueryType::Com1Freq, clientQuery.sender());
1400 else if (clientQuery.m_queryType == ClientQueryType::RealName)
1402 sendClientResponse(ClientQueryType::RealName, clientQuery.sender());
1404 else if (clientQuery.m_queryType == ClientQueryType::Server)
1406 sendClientResponse(ClientQueryType::Server, clientQuery.sender());
1408 else if (clientQuery.m_queryType == ClientQueryType::ATIS)
1412 else if (clientQuery.m_queryType == ClientQueryType::PublicIP)
1416 else if (clientQuery.m_queryType == ClientQueryType::INF)
1418 sendClientResponse(ClientQueryType::INF, clientQuery.sender());
1420 else if (clientQuery.m_queryType == ClientQueryType::FP)
1424 else if (clientQuery.m_queryType == ClientQueryType::AircraftConfig)
1427 QString aircraftConfigJson = aircraftConfigTokens.
join(
":");
1429 const CCallsign callsign(clientQuery.sender(), CCallsign::Aircraft);
1437 CLogMessage(
this).
warning(u
"Failed to parse aircraft config packet: '%1' packet: '%2'")
1443 if (packet == JsonPackets::aircraftConfigRequest())
1447 QJsonObject config = this->getOwnAircraftParts().toJson();
1448 config.
insert(CAircraftParts::attributeNameIsFullJson(),
true);
1450 data = convertToUnicodeEscaped(data);
1451 sendAircraftConfiguration(clientQuery.sender(), data);
1455 const bool inRange = isAircraftInRange(callsign);
1456 if (!inRange) {
return; }
1457 if (!getSetupForServer().receiveAircraftParts()) {
return; }
1459 if (config.
isEmpty()) {
return; }
1461 const qint64 offsetTimeMs = currentOffsetTime(callsign);
1462 emit aircraftConfigReceived(clientQuery.sender(), config, offsetTimeMs);
1467 void CFSDClient::handleClientResponse(
const QStringList &tokens)
1469 const ClientResponse clientResponse = ClientResponse::fromTokens(tokens);
1470 if (clientResponse.isUnknownQuery()) {
return; }
1471 const QString sender = clientResponse.sender();
1476 if (!clientResponse.m_responseData.empty()) { responseData1 = clientResponse.m_responseData.
at(0); }
1478 if (clientResponse.m_responseData.size() > 1) { responseData2 = clientResponse.m_responseData.
at(1); }
1480 if (clientResponse.m_queryType == ClientQueryType::IsValidATC)
1482 emit validAtcResponseReceived(responseData2, responseData1 == u
"Y");
1484 else if (clientResponse.m_queryType == ClientQueryType::Capabilities)
1487 for (
auto keyValuePair : clientResponse.m_responseData)
1489 if (keyValuePair.count(
'=') != 1) {
continue; }
1492 if (
split.size() < 2) {
continue; }
1496 if (value ==
"1") { capabilities |= fromQString<Capabilities>(key); }
1499 CClient::Capabilities caps = CClient::None;
1500 if (capabilities & Capabilities::AtcInfo) { caps |= CClient::FsdAtisCanBeReceived; }
1501 if (capabilities & Capabilities::FastPos) { caps |= CClient::FsdWithInterimPositions; }
1502 if (capabilities & Capabilities::VisPos) { caps |= CClient::FsdWithVisualPositions; }
1503 if (capabilities & Capabilities::AircraftInfo) { caps |= CClient::FsdWithIcaoCodes; }
1504 if (capabilities & Capabilities::AircraftConfig) { caps |= CClient::FsdWithAircraftConfig; }
1506 emit capabilityResponseReceived(clientResponse.sender(), caps);
1508 else if (clientResponse.m_queryType == ClientQueryType::Com1Freq)
1510 if (responseData1.
isEmpty()) {
return; }
1512 const double freqMHz = responseData1.
toDouble(&ok);
1513 if (!ok) {
return; }
1514 emit com1FrequencyResponseReceived(clientResponse.sender(),
CFrequency(freqMHz, CFrequencyUnit::MHz()));
1516 else if (clientResponse.m_queryType == ClientQueryType::RealName)
1519 emit realNameResponseReceived(clientResponse.sender(), responseData1);
1521 else if (clientResponse.m_queryType == ClientQueryType::Server)
1523 emit serverResponseReceived(clientResponse.sender(), responseData1);
1525 else if (clientResponse.m_queryType == ClientQueryType::ATIS)
1532 updateAtisMap(clientResponse.sender(), fromQString<AtisLineType>(responseData1), responseData2);
1534 else if (clientResponse.m_queryType == ClientQueryType::PublicIP)
1538 else if (clientResponse.m_queryType == ClientQueryType::INF)
1542 else if (clientResponse.m_queryType == ClientQueryType::FP)
1546 else if (clientResponse.m_queryType == ClientQueryType::AircraftConfig)
1553 void CFSDClient::handleServerError(
const QStringList &tokens)
1555 const ServerError serverError = ServerError::fromTokens(tokens);
1556 switch (serverError.m_errorNumber)
1558 case ServerErrorCode::CallsignInUse:
CLogMessage(
this).
error(u
"The requested callsign is already taken");
break;
1559 case ServerErrorCode::InvalidCallsign:
CLogMessage(
this).
error(u
"The requested callsign is not valid");
break;
1560 case ServerErrorCode::InvalidCidPassword:
1561 CLogMessage(
this).
error(u
"Wrong user ID (%1) or password") << m_server.getUser().getId();
1563 case ServerErrorCode::InvalidRevision:
1564 CLogMessage(
this).
error(u
"This server does not support our protocol version");
1566 case ServerErrorCode::ServerFull:
CLogMessage(
this).
error(u
"The server is full");
break;
1567 case ServerErrorCode::CidSuspended:
CLogMessage(
this).
error(u
"Your user account is suspended");
break;
1568 case ServerErrorCode::RatingTooLow:
1569 CLogMessage(
this).
error(u
"You are not authorized to use the requested rating");
1571 case ServerErrorCode::InvalidClient:
1572 CLogMessage(
this).
error(u
"This software is not authorized for use on this network");
1574 case ServerErrorCode::RequestedLevelTooHigh:
1575 CLogMessage(
this).
error(u
"You are not authorized to use the requested pilot rating");
1579 case ServerErrorCode::SyntaxError:
1581 u
"Malformed packet, syntax error: '%1'. This can also occur if an OBS sends frequency text messages.")
1582 << serverError.getCausingParameter();
1584 case ServerErrorCode::InvalidSrcCallsign:
1585 CLogMessage(
this).
info(u
"FSD message was using an invalid callsign: %1 (%2)")
1586 << serverError.getCausingParameter() << serverError.getDescription();
1588 case ServerErrorCode::NoSuchCallsign:
1590 << serverError.getCausingParameter() << serverError.getDescription();
1592 case ServerErrorCode::NoFlightPlan:
CLogMessage(
this).
info(u
"FSD Server: no flight plan");
break;
1593 case ServerErrorCode::NoWeatherProfile:
1594 CLogMessage(
this).
info(u
"FSD Server: requested weather profile does not exist");
1598 case ServerErrorCode::AlreadyRegistered:
1599 CLogMessage(
this).
warning(u
"Server says already registered: %1") << serverError.getDescription();
1601 case ServerErrorCode::InvalidCtrl:
1602 CLogMessage(
this).
warning(u
"Server invalid control: %1") << serverError.getDescription();
1604 case ServerErrorCode::Unknown:
1606 << serverError.getCausingParameter() << serverError.getDescription();
1608 case ServerErrorCode::AuthTimeout:
CLogMessage(
this).
warning(u
"Client did not authenticate in time");
break;
1610 if (serverError.isFatalError()) { disconnectFromServer(); }
1613 void CFSDClient::handleRehost(
const QStringList &tokens)
1615 const Rehost rehost = Rehost::fromTokens(tokens);
1617 CLogMessage(
this).
info(u
"Server requested we switch server to %1") << rehost.m_hostname;
1619 SWIFT_AUDIT_X(!m_rehosting, Q_FUNC_INFO,
"Rehosting already in progress");
1622 auto rehostingSocket = std::make_shared<QTcpSocket>();
1624 readDataFromSocket();
1625 CLogMessage(this).debug(u
"Successfully switched server");
1626 m_socket = rehostingSocket;
1627 m_rehosting = false;
1628 rehostingSocket->disconnect(this);
1629 connectSocketSignals();
1630 readDataFromSocket();
1633 CLogMessage(this).warning(u
"Failed to switch server: %1") << rehostingSocket->errorString();
1634 m_rehosting = false;
1635 rehostingSocket->disconnect(this);
1636 if (m_socket->state() != QAbstractSocket::ConnectedState)
1638 updateConnectionStatus(CConnectionStatus::Disconnected);
1642 initiateConnection(rehostingSocket, rehost.m_hostname);
1645 void CFSDClient::handleMute(
const QStringList &tokens)
1647 const Mute mute = Mute::fromTokens(tokens);
1648 if (mute.receiver() != m_ownCallsign.asString()) {
return; }
1649 emit muteRequestReceived(mute.m_mute);
1652 void CFSDClient::initiateConnection(std::shared_ptr<QTcpSocket> rehostingSocket,
const QString &rehostingHost)
1654 const CServer server = this->getServer();
1655 const auto socket = rehostingSocket ? rehostingSocket : m_socket;
1659 const quint16 port = rehostingSocket ? m_socket->peerPort() :
static_cast<quint16
>(getServer().getPort());
1662 resolveLoadBalancing(host, [=,
this](
const QString &host) {
1663 socket->connectToHost(host, port);
1664 if (!rehostingSocket) { this->startPositionTimers(); }
1668 void CFSDClient::resolveLoadBalancing(
const QString &host, std::function<
void(
const QString &)> callback)
1670 if (
QHostAddress(host).isNull() && (getServer().getName() ==
"AUTOMATIC" || m_rehosting) &&
1671 getServer().getEcosystem() == CEcosystem::VATSIM)
1674 Q_ASSERT_X(
sApp, Q_FUNC_INFO,
"Need app");
1684 callback(addr.toString());
1691 else { callback(host); }
1694 void CFSDClient::handleCustomPilotPacket(
const QStringList &tokens)
1698 if (subType == u
"PIR")
1700 PlaneInfoRequest planeInfoRequest = PlaneInfoRequest::fromTokens(tokens);
1703 const QString airlineIcao = m_server.getFsdSetup().force3LetterAirlineCodes() ?
1707 const QString livery = this->getConfiguredLiveryString(myAircraft);
1709 sendPlaneInformation(planeInfoRequest.sender(), acTypeICAO, airlineIcao, livery);
1711 else if (subType ==
"PI")
1713 if (tokens.
size() > 6 && tokens.
at(3) ==
"X")
1717 else if (tokens.
size() > 4 && tokens.
at(3) ==
"GEN")
1719 const PlaneInformation planeInformation = PlaneInformation::fromTokens(tokens);
1720 emit planeInformationReceived(planeInformation.sender(), planeInformation.m_aircraft,
1721 planeInformation.m_airline, planeInformation.m_livery);
1724 else if (subType ==
"I")
1728 else if (subType ==
"VI")
1731 if (!isInterimPositionReceivingEnabledForServer()) {
return; }
1733 const InterimPilotDataUpdate interimPilotDataUpdate = InterimPilotDataUpdate::fromTokens(tokens);
1734 const CCallsign callsign(interimPilotDataUpdate.sender(), CCallsign::Aircraft);
1738 interimPilotDataUpdate.m_longitude,
1739 interimPilotDataUpdate.m_altitudeTrue),
1740 CHeading(interimPilotDataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1741 CAngle(interimPilotDataUpdate.m_pitch, CAngleUnit::deg()),
1742 CAngle(interimPilotDataUpdate.m_bank, CAngleUnit::deg()),
1743 CSpeed(interimPilotDataUpdate.m_groundSpeed, CSpeedUnit::kts()));
1745 const COnGroundInfo og(interimPilotDataUpdate.m_onGround ? COnGroundInfo::OnGround :
1746 COnGroundInfo::NotOnGround,
1747 COnGroundInfo::NotSetGroundDetails);
1752 const qint64 offsetTimeMs =
1756 emit interimPilotDataUpdatedReceived(situation);
1758 else if (subType ==
"FSIPI")
1760 const PlaneInformationFsinn planeInformationFsinn = PlaneInformationFsinn::fromTokens(tokens);
1761 emit planeInformationFsinnReceived(planeInformationFsinn.sender(), planeInformationFsinn.m_airlineIcao,
1762 planeInformationFsinn.m_aircraftIcao,
1763 planeInformationFsinn.m_aircraftIcaoCombinedType,
1764 planeInformationFsinn.m_sendMModelString);
1766 else if (subType ==
"FSIPIR")
1768 const PlaneInfoRequestFsinn planeInfoRequestFsinn = PlaneInfoRequestFsinn::fromTokens(tokens);
1769 sendPlaneInformationFsinn(planeInfoRequestFsinn.sender());
1770 emit planeInformationFsinnReceived(planeInfoRequestFsinn.sender(), planeInfoRequestFsinn.m_airlineIcao,
1771 planeInfoRequestFsinn.m_aircraftIcao,
1772 planeInfoRequestFsinn.m_aircraftIcaoCombinedType,
1773 planeInfoRequestFsinn.m_sendMModelString);
1780 emit customPilotPacketReceived(sender, data);
1784 #ifdef SWIFT_VATSIM_SUPPORT
1785 void CFSDClient::handleFsdIdentification(
const QStringList &tokens)
1789 const FSDIdentification fsdIdentification = FSDIdentification::fromTokens(tokens);
1790 vatsim_auth_set_initial_challenge(m_clientAuth, qPrintable(fsdIdentification.m_initialChallenge));
1792 char fsdChallenge[33];
1793 vatsim_auth_generate_challenge(m_serverAuth, fsdChallenge);
1794 vatsim_auth_set_initial_challenge(m_serverAuth, fsdChallenge);
1795 sendClientIdentification(
QString(fsdChallenge));
1800 u
"You tried to connect to a VATSIM server without using VATSIM protocol, disconnecting!");
1801 disconnectFromServer();
1806 void CFSDClient::handleUnknownPacket(
const QString &line)
1811 void CFSDClient::handleUnknownPacket(
const QStringList &tokens) { this->handleUnknownPacket(tokens.
join(
", ")); }
1815 if (m_rehosting) {
return; }
1817 CLogMessage(
this).
error(u
"FSD socket error: %1") << this->socketErrorString(socketError);
1822 if (m_rehosting) {
return; }
1824 const QString error = this->socketErrorString(socketError);
1825 switch (socketError)
1829 emit this->severeNetworkError(error);
1830 this->disconnectFromServer();
1836 void CFSDClient::handleSocketConnected()
1841 this->updateConnectionStatus(CConnectionStatus::Connected);
1847 if (this->getConnectionStatus() == newStatus) {
return; }
1853 m_server.setConnectedSinceNow();
1854 ecoSystem = m_server.getEcosystem();
1856 this->setCurrentEcosystem(ecoSystem);
1861 m_server.markAsDisconnected();
1866 this->stopPositionTimers();
1868 this->setLastEcosystem(m_server.getEcosystem());
1869 this->setCurrentEcosystem(CEcosystem::NoSystem);
1870 this->saveNetworkStatistics(m_server.getName());
1876 oldStatus = m_connectionStatus;
1877 m_connectionStatus = newStatus;
1880 emit this->connectionStatusChanged(oldStatus, newStatus);
1883 void CFSDClient::consolidateTextMessage(
const CTextMessage &textMessage)
1888 m_textMessagesToConsolidate.addConsolidatedTextMessage(textMessage);
1889 m_dsSendTextMessage.inputSignal();
1893 void CFSDClient::emitConsolidatedTextMessages()
1895 emit this->textMessagesReceived(m_textMessagesToConsolidate);
1896 m_textMessagesToConsolidate.clear();
1899 qint64 CFSDClient::receivedPositionFixTsAndGetOffsetTime(
const CCallsign &callsign, qint64 markerTs)
1901 Q_ASSERT_X(!callsign.
isEmpty(), Q_FUNC_INFO,
"Need callsign");
1904 if (!m_lastPositionUpdate.contains(callsign))
1906 m_lastPositionUpdate.insert(callsign, markerTs);
1907 return CFsdSetup::c_positionTimeOffsetMsec;
1909 const qint64 oldTs = m_lastPositionUpdate.value(callsign);
1910 m_lastPositionUpdate[callsign] = markerTs;
1913 const qint64 diff = qAbs(markerTs - oldTs);
1914 this->insertLatestOffsetTime(callsign, diff);
1917 const qint64 avgTimeMs = this->averageOffsetTimeMs(callsign, count, 3);
1918 qint64 offsetTime = CFsdSetup::c_positionTimeOffsetMsec;
1920 if (avgTimeMs < CFsdSetup::c_interimPositionTimeOffsetMsec && count >= 3)
1922 offsetTime = CFsdSetup::c_interimPositionTimeOffsetMsec;
1925 return m_additionalOffsetTime + offsetTime;
1928 qint64 CFSDClient::currentOffsetTime(
const CCallsign &callsign)
const
1930 Q_ASSERT_X(!callsign.
isEmpty(), Q_FUNC_INFO,
"Need callsign");
1932 if (!m_lastOffsetTimes.contains(callsign) || m_lastOffsetTimes[callsign].isEmpty())
1934 return CFsdSetup::c_positionTimeOffsetMsec;
1936 return m_lastOffsetTimes[callsign].front();
1939 void CFSDClient::clearState()
1941 m_rehosting =
false;
1942 m_stoppedSendingVisualPositions =
false;
1943 m_serverWantsVisualPositions =
false;
1944 m_visualPositionUpdateSentCount = 0;
1945 m_textMessagesToConsolidate.clear();
1946 m_pendingAtisQueries.clear();
1947 m_lastPositionUpdate.clear();
1948 m_lastOffsetTimes.clear();
1949 m_atcStations.clear();
1950 m_queuedFsdMessages.clear();
1951 m_sentAircraftConfig = CAircraftParts::null();
1955 void CFSDClient::clearState(
const CCallsign &callsign)
1957 if (callsign.
isEmpty()) {
return; }
1958 m_pendingAtisQueries.remove(callsign);
1959 m_lastPositionUpdate.remove(callsign);
1960 m_interimPositionReceivers.remove(callsign);
1961 m_lastOffsetTimes.remove(callsign);
1964 void CFSDClient::insertLatestOffsetTime(
const CCallsign &callsign, qint64 offsetMs)
1971 qint64 CFSDClient::averageOffsetTimeMs(
const CCallsign &callsign,
int &count,
int maxLastValues)
const
1974 if (offsets.
size() < 1) {
return -1; }
1977 for (qint64 v : offsets)
1981 if (count > maxLastValues) {
break; }
1983 return qRound(
static_cast<double>(sum) / count);
1986 bool CFSDClient::isInterimPositionSendingEnabledForServer()
const
1988 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
1989 return (d & CFsdSetup::SendInterimPositions);
1992 bool CFSDClient::isInterimPositionReceivingEnabledForServer()
const
1994 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
1995 return (d & CFsdSetup::ReceiveInterimPositions);
1998 bool CFSDClient::isVisualPositionSendingEnabledForServer()
const
2000 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
2001 return (d & CFsdSetup::SendVisualPositions);
2004 const CFsdSetup &CFSDClient::getSetupForServer()
const {
return m_server.getFsdSetup(); }
2008 Q_ASSERT(m_pendingAtisQueries.contains(sender));
2009 PendingAtisQuery &pendingQuery = m_pendingAtisQueries[sender];
2010 pendingQuery.m_atisMessage.push_back(message);
2017 tm.setCurrentUtcTime();
2018 this->consolidateTextMessage(tm);
2019 m_pendingAtisQueries.remove(sender);
2027 if (reLogoff.match(message).hasMatch())
2029 emit atisLogoffTimeReplyReceived(sender, message);
2031 for (
const auto &line : std::as_const(pendingQuery.m_atisMessage))
2033 if (!atisMessage.isEmpty()) atisMessage.appendMessage(
"\n");
2034 atisMessage.appendMessage(line);
2037 m_pendingAtisQueries.remove(sender);
2042 void CFSDClient::fsdMessageSettingsChanged()
2044 if (m_rawFsdMessageLogFile.isOpen()) { m_rawFsdMessageLogFile.close(); }
2051 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(),
"rawfsdmessages.log");
2052 m_rawFsdMessageLogFile.setFileName(filePath);
2058 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(),
"rawfsdmessages.log");
2059 m_rawFsdMessageLogFile.setFileName(filePath);
2063 else if (setting.
getFileWriteMode() == CRawFsdMessageSettings::Timestamped)
2065 QString filename(
"rawfsdmessages");
2069 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(), filename);
2070 m_rawFsdMessageLogFile.setFileName(filePath);
2078 return m_interimPositionReceivers;
2083 m_interimPositionReceivers = interimPositionReceivers;
2086 bool CFSDClient::isPendingConnection()
const
2088 return m_connectionStatus.isConnecting() || m_connectionStatus.isDisconnecting();
2091 int CFSDClient::increaseStatisticsValue(
const QString &identifier,
const QString &appendix)
2093 if (identifier.
isEmpty() || !m_statistics) {
return -1; }
2096 const QString i = appendix.
isEmpty() ? identifier : identifier % u
"." % appendix;
2097 int &v = m_callStatistics[i];
2100 constexpr
int MaxTimeValues = 50;
2102 if (m_callByTime.size() > MaxTimeValues) { m_callByTime.removeLast(); }
2106 int CFSDClient::increaseStatisticsValue(
const QString &identifier,
int value)
2111 void CFSDClient::clearStatistics()
2114 m_callStatistics.clear();
2115 m_callByTime.clear();
2126 callStatistics = m_callStatistics;
2127 callByTime = m_callByTime;
2130 if (callStatistics.
isEmpty()) {
return {}; }
2131 for (
const auto [key, value] :
makePairsRange(std::as_const(callStatistics)))
2138 std::sort(transformed.
begin(), transformed.
end(), std::greater<>());
2140 for (
const auto &pair : transformed)
2145 for (
const auto &pair : transformed)
2152 const qint64 lastTs = callByTime.
front().
first;
2153 for (
const auto &pair : std::as_const(callByTime))
2155 const qint64 deltaTs = lastTs - pair.first;
2156 stats += separator % QStringLiteral(
"%1").
arg(deltaTs, 5, 10,
QChar(
'0')) % u
": " % pair.second;
2160 if (reset) { this->clearStatistics(); }
2164 void CFSDClient::gracefulShutdown()
2166 disconnectFromServer();
2170 void CFSDClient::readDataFromSocketMaxLines(
int maxLines)
2172 if (m_socket->bytesAvailable() < 1) {
return; }
2177 while (m_socket->canReadLine())
2179 const QByteArray dataEncoded = m_socket->readLine();
2180 if (dataEncoded.
isEmpty()) {
continue; }
2181 const QString data = m_decoder(dataEncoded);
2182 this->parseMessage(data);
2185 static constexpr
int MaxLines = 75 - 1;
2186 if (maxLines < 0) { maxLines = MaxLines; }
2188 if (lines > maxLines)
2190 static constexpr
int DelayMs = 10;
2191 const int newMax = qRound(1.2 * lines);
2193 CLogMessage(
this).
debug(u
"ReadDataFromSocket has too many lines (>%1), will read again in %2ms")
2194 << MaxLines << DelayMs;
2198 if (myself) { myself->readDataFromSocketMaxLines(newMax); }
2207 QString e = CFSDClient::socketErrorToQString(error);
2208 if (!m_socket->errorString().isEmpty()) { e += QStringLiteral(
": ") % m_socket->errorString(); }
2214 static const QMetaEnum metaEnum = QMetaEnum::fromType<QAbstractSocket::SocketError>();
2218 void CFSDClient::parseMessage(
const QString &lineRaw)
2224 if (m_printToConsole) { qDebug() <<
"FSD Recv=>" << line; }
2225 emitRawFsdMessage(line,
false);
2232 messageType = m_messageTypeMapping.value(str, MessageType::Unknown);
2240 increaseStatisticsValue(QStringLiteral(
"parseMessage"), this->messageTypeToString(messageType));
2243 if (messageType != MessageType::Unknown)
2249 if (payload.
length() == 0) {
return; }
2252 switch (messageType)
2255 case MessageType::AddAtc:
2256 case MessageType::AddPilot:
2257 case MessageType::ServerHeartbeat:
2258 case MessageType::ProController:
2259 case MessageType::ClientIdentification:
break;
2262 case MessageType::AtcDataUpdate: handleAtcDataUpdate(tokens);
break;
2263 #ifdef SWIFT_VATSIM_SUPPORT
2264 case MessageType::AuthChallenge: handleAuthChallenge(tokens);
break;
2265 case MessageType::AuthResponse: handleAuthResponse(tokens);
break;
2267 case MessageType::ClientQuery: handleClientQuery(tokens);
break;
2268 case MessageType::ClientResponse: handleClientResponse(tokens);
break;
2269 case MessageType::DeleteATC: handleDeleteATC(tokens);
break;
2270 case MessageType::DeletePilot: handleDeletePilot(tokens);
break;
2271 case MessageType::FlightPlan: handleFlightPlan(tokens);
break;
2272 #ifdef SWIFT_VATSIM_SUPPORT
2273 case MessageType::FsdIdentification: handleFsdIdentification(tokens);
break;
2275 case MessageType::KillRequest: handleKillRequest(tokens);
break;
2276 case MessageType::PilotDataUpdate: handlePilotDataUpdate(tokens);
break;
2277 case MessageType::Ping: handlePing(tokens);
break;
2278 case MessageType::Pong: handlePong(tokens);
break;
2279 case MessageType::ServerError: handleServerError(tokens);
break;
2280 case MessageType::TextMessage: handleTextMessage(tokens);
break;
2281 case MessageType::PilotClientCom: handleCustomPilotPacket(tokens);
break;
2282 case MessageType::VisualPilotDataUpdate:
2283 case MessageType::VisualPilotDataPeriodic:
2284 case MessageType::VisualPilotDataStopped: handleVisualPilotDataUpdate(tokens, messageType);
break;
2285 case MessageType::VisualPilotDataToggle: handleVisualPilotDataToggle(tokens);
break;
2286 case MessageType::EuroscopeSimData: handleEuroscopeSimData(tokens);
break;
2287 case MessageType::Rehost: handleRehost(tokens);
break;
2288 case MessageType::Mute: handleMute(tokens);
break;
2292 case MessageType::Unknown: handleUnknownPacket(tokens);
break;
2295 else { handleUnknownPacket(line); }
2298 void CFSDClient::emitRawFsdMessage(
const QString &fsdMessage,
bool isSent)
2300 if (!m_unitTestMode && !m_rawFsdMessagesEnabled) {
return; }
2301 QString fsdMessageFiltered(fsdMessage);
2302 if (m_filterPasswordFromLogin)
2304 if (fsdMessageFiltered.startsWith(
"#AP"))
2306 thread_local
const QRegularExpression re(R
"(^(#AP\w+:SERVER:\d+:)[^:]+(:\d+:\d+:\d+:.+)$)");
2307 fsdMessageFiltered.replace(re, "\\1<password>\\2");
2308 m_filterPasswordFromLogin =
false;
2312 const QString prefix = isSent ?
"FSD Sent=>" :
"FSD Recv=>";
2314 rawMessage.setCurrentUtcTime();
2315 if (m_rawFsdMessageLogFile.isOpen())
2318 stream << rawMessage.toQString().trimmed() <<
Qt::endl;
2320 emit rawFsdMessage(rawMessage);
2323 bool CFSDClient::saveNetworkStatistics(
const QString &server)
2325 if (m_callStatistics.isEmpty()) {
return false; }
2327 const QString s = this->getNetworkStatisticsAsText(
false,
"\n");
2328 if (s.
isEmpty()) {
return false; }
2329 const QString fn = QStringLiteral(
"networkstatistics_%1_%2.log")
2331 const QString fp = CFileUtils::appendFilePaths(CSwiftDirectories::logDirectory(), fn);
2332 return CFileUtils::writeStringToFile(s, fp);
2335 void CFSDClient::startPositionTimers()
2337 m_positionUpdateTimer.start(c_updatePositionIntervalMsec);
2338 m_scheduledConfigUpdate.start(c_processingIntervalMsec);
2339 m_fsdSendMessageTimer.start(c_sendFsdMsgIntervalMsec);
2340 m_queuedFsdMessages.clear();
2344 if (this->isInterimPositionSendingEnabledForServer())
2346 m_interimPositionUpdateTimer.start(c_updateInterimPositionIntervalMsec);
2348 else { m_interimPositionUpdateTimer.stop(); }
2349 if (this->isVisualPositionSendingEnabledForServer())
2351 m_visualPositionUpdateTimer.start(c_updateVisualPositionIntervalMsec);
2353 else { m_visualPositionUpdateTimer.stop(); }
2357 void CFSDClient::stopPositionTimers()
2359 m_positionUpdateTimer.stop();
2360 m_interimPositionUpdateTimer.stop();
2361 m_visualPositionUpdateTimer.stop();
2362 m_scheduledConfigUpdate.stop();
2363 m_fsdSendMessageTimer.stop();
2366 void CFSDClient::updateAtisMap(
const QString &callsign, AtisLineType type,
const QString &line)
2369 if (type == AtisLineType::VoiceRoom)
2371 m_mapAtisMessages[callsign].m_voiceRoom = line;
2372 m_mapAtisMessages[callsign].m_lineCount++;
2375 if (type == AtisLineType::TextMessage)
2377 m_mapAtisMessages[callsign].m_textLines.
push_back(line);
2378 m_mapAtisMessages[callsign].m_lineCount++;
2381 if (type == AtisLineType::ZuluLogoff)
2383 m_mapAtisMessages[callsign].m_zuluLogoff = line;
2384 m_mapAtisMessages[callsign].m_lineCount++;
2387 if (!m_mapAtisMessages.contains(callsign)) {
return; }
2390 m_mapAtisMessages[callsign].m_lineCount++;
2392 const CCallsign cs(callsign, CCallsign::Atc);
2394 emit atisLogoffTimeReplyReceived(cs, m_mapAtisMessages[callsign].m_zuluLogoff);
2397 for (
const QString &tm : std::as_const(m_mapAtisMessages[callsign].m_textLines))
2400 if (!
fixed.isEmpty())
2406 if (test ==
"z")
return;
2408 if (test.
length() == 1)
return;
2411 if (!atisMessage.isEmpty()) atisMessage.appendMessage(
"\n");
2412 atisMessage.appendMessage(fixed);
2416 emit this->atisReplyReceived(cs, atisMessage);
2418 m_mapAtisMessages.remove(callsign);
2422 void CFSDClient::pendingTimeoutCheck()
2424 if (!this->isPendingConnection()) {
return; }
2427 if (age < PendingConnectionTimeoutMs) {
return; }
2430 CLogMessage(
this).
warning(u
"Timeout on pending connection to '%1'") << this->getServer().getName();
2431 this->disconnectFromServer();
2449 static const CLength l_Atis(150.0, CLengthUnit::NM());
2450 return maxOrNotNull(networkRange, l_Atis);
2454 static const CLength l_Gnd(10.0, CLengthUnit::NM());
2455 return maxOrNotNull(networkRange, l_Gnd);
2459 static const CLength l_Twr(25.0, CLengthUnit::NM());
2460 return maxOrNotNull(networkRange, l_Twr);
2464 static const CLength l_Dep(150.0, CLengthUnit::NM());
2465 return maxOrNotNull(networkRange, l_Dep);
2469 static const CLength l_App(150.0, CLengthUnit::NM());
2470 return maxOrNotNull(networkRange, l_App);
2474 static const CLength l_Ctr(300.0, CLengthUnit::NM());
2475 return maxOrNotNull(networkRange, l_Ctr);
2479 static const CLength l_Fss(1500.0, CLengthUnit::NM());
2480 return maxOrNotNull(networkRange, l_Fss);
2483 return networkRange;
2488 if (l1.
isNull()) {
return l2; }
2489 if (l2.
isNull()) {
return l1; }
2490 return (l2 > l1) ? l2 : l1;
2495 if (!input.
contains(
':')) {
return input; }
2497 return copy.remove(
':');
2502 switch (flightRules)
2504 case CFlightPlan::IFR:
return FlightType::IFR;
2505 case CFlightPlan::VFR:
return FlightType::VFR;
2506 case CFlightPlan::SVFR:
return FlightType::SVFR;
2507 case CFlightPlan::DVFR:
return FlightType::DVFR;
2508 default:
return FlightType::IFR;
2515 while (i != m_messageTypeMapping.constEnd())
2517 if (i.
value() == mt) {
return i.
key(); }
2525 void CFSDClient::handleIllegalFsdState(
const QString &message)
2527 if (CBuildConfig::isLocalDeveloperDebugBuild()) {
SWIFT_VERIFY_X(
false, Q_FUNC_INFO,
"Illegal FSD state"); }
2531 const QJsonObject &CFSDClient::JsonPackets::aircraftConfigRequest()
2533 static const QJsonObject jsonObject { {
"request",
"full" } };
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
QNetworkReply * getFromNetwork(const swift::misc::network::CUrl &url, const CallbackSlot &callback, int maxRedirects=DefaultMaxRedirects)
Request to get network reply.
data::CGlobalSetup getGlobalSetup() const
Global setup.
static constexpr int NoLogRequestId
network request without logging
swift::misc::db::CDistribution getOwnDistribution() const
Own distribution.
QNetworkReply * postToNetwork(const QNetworkRequest &request, int logId, const QByteArray &data, const CallbackSlot &callback)
Post to network.
swift::misc::network::CUrl getVatsimFsdHttpUrl() const
Get VATSIM FSD HTTP URL.
bool isShuttingDown() const
Is application shutting down?
swift::misc::network::CUrl getVatsimAuthUrl() const
VATSIM auth URL.
void setCallsign(const swift::misc::aviation::CCallsign &callsign)
Preset functions.
void setIcaoCodes(const swift::misc::simulation::CSimulatedAircraft &ownAircraft)
Preset functions.
void sendClientQueryCom1Freq(const swift::misc::aviation::CCallsign &callsign)
void sendFlightPlan(const swift::misc::aviation::CFlightPlan &flightPlan)
bool isConnected() const
Connection status.
void sendTextMessages(const swift::misc::network::CTextMessageList &messages)
void sendClientQueryAtis(const swift::misc::aviation::CCallsign &callsign)
void setServer(const swift::misc::network::CServer &server)
Preset functions.
void setLiveryAndModelString(const QString &livery, bool sendLiveryString, const QString &modelString, bool sendModelString)
Preset functions.
const swift::misc::network::CServer & getServer() const
Get the server.
swift::misc::network::CLoginMode getLoginMode() const
Mode.
void disconnectFromServer()
Connect/disconnect.
void sendPlaneInfoRequest(const swift::misc::aviation::CCallsign &receiver)
void sendClientQueryRealName(const swift::misc::aviation::CCallsign &callsign)
PilotRating getPilotRating() const
Rating.
QStringList getPresetValues() const
List of all preset values.
void sendClientQueryServer(const swift::misc::aviation::CCallsign &callsign)
void setSimType(const swift::misc::simulation::CSimulatorInfo &simInfo)
Preset functions.
void sendClientQueryAircraftConfig(const swift::misc::aviation::CCallsign &callsign)
void textMessageSent(const swift::misc::network::CTextMessage &sentMessage)
We have sent a text message.
void sendTextMessage(const swift::misc::network::CTextMessage &message)
void sendClientQueryFlightPlan(const swift::misc::aviation::CCallsign &callsign)
void sendRadioMessage(const QVector< int > &frequencieskHz, const QString &message)
void connectToServer()
Connect/disconnect.
void sendPlaneInfoRequestFsinn(const swift::misc::aviation::CCallsign &callsign)
swift::misc::network::CConnectionStatus getConnectionStatus() const
Connection status.
bool isDisconnected() const
Connection status.
FSinn specific version of plane information request.
Request to send plane information. Shall be answered by a PlaneInformation message.
Text, radio or private message.
QString getFileDir() const
Get file directory.
FileWriteMode getFileWriteMode() const
Get file write mode.
bool areRawFsdMessagesEnabled() const
Are raw FSD messages enabled?
Base class for a long-lived worker object which lives in its own thread.
Class for emitting a log message.
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & log(StatusSeverity s, const char16_t(&m)[N])
Set the severity and format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & debug()
Set the severity to debug.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
bool isEmpty() const
Synonym for empty.
Callable wrapper for a member function with function signature F.
Streamable status message, e.g.
static bool isInThisThread(const QObject *toBeTested)
Is the current thread the object's thread?
qint64 getMSecsSinceEpoch() const
Timestamp as ms value.
void setCurrentUtcTime()
Set the current time as timestamp.
void setTimeOffsetMs(qint64 offset)
Milliseconds to add to timestamp for interpolation.
const QString & getDesignator() const
Get ICAO designator, e.g. "B737".
Value object encapsulating information of aircraft's parts.
void setOnGround(bool onGround)
Set aircraft on ground.
void setGearDown(bool down)
Set gear down.
void setLights(const CAircraftLights &lights)
Set aircraft lights.
Value object encapsulating information of an aircraft's situation.
void setPressureAltitude(const CAltitude &altitude)
Set pressure altitude.
geo::CLongitude longitude() const
Longitude.
geo::CLatitude latitude() const
Latitude.
const CCallsign & getCallsign() const
Corresponding callsign.
void setOnGroundInfo(const aviation::COnGroundInfo &info)
Set the on ground info.
const CAltitude & getAltitude() const
Get altitude.
const QString & getDesignator() const
Get airline, e.g. "DLH".
QString getVDesignator() const
Get airline, e.g. "DLH", but "VMVA" for virtual airlines.
const QString & asString() const
Get code.
Altitude as used in aviation, can be AGL or MSL altitude.
QString asFpVatsimAltitudeString() const
As simple VATSIM string, only FLxxx or altitude as ft.
void parseFromString(const QString &value)
Parse value from string.
Value object for a list of ATC stations.
CAtcStationList findIfFrequencyIsWithinSpacing(const physical_quantities::CFrequency &frequency)
Find 0..n stations with frequency (with 5 kHz spacing for .x20/.x25 and .x70/.x75)
Value object encapsulating information of a callsign.
const QString & asString() const
Get callsign (normalized)
QString getSuffix() const
Get the callsign suffix ("TWR", "ATIS" ...) if any ("_" is removed)
bool isEmpty() const
Is empty?
QString getFsdCallsignString() const
The callsign string used with FSD.
Value object for a set of callsigns.
Flightplan-related information about an aircraft (aircraft ICAO, equipment and WTC)
QString asIcaoString() const
Full string in ICAO format: "AIRCRAFT_ICAO/WTC-EQUIPMENT/SSR".
QString asFaaString() const
Full string in FAA format: "H/J (if heavy/super)/AIRCRAFT_ICAO/EQUIPMENT-CODE".
Value object for a flight plan.
const physical_quantities::CTime & getFuelTime() const
Get amount of fuel load in time.
const physical_quantities::CSpeed & getCruiseTrueAirspeed() const
Get planned cruise TAS.
const QString & getRoute() const
Get route string.
FlightRules
Flight rules (VFR or IFR)
const QDateTime & getTakeoffTimePlanned() const
Get planned takeoff time (planned)
FlightRules getFlightRules() const
Get flight rules as in FlightRules.
const QString & getRemarks() const
Get remarks string.
const CAirportIcaoCode & getAlternateAirportIcao() const
Get alternate destination airport ICAO code.
const CAltitude & getCruiseAltitude() const
Cruising altitudes.
const physical_quantities::CTime & getEnrouteTime() const
Get planned enroute flight time.
const CAirportIcaoCode & getOriginAirportIcao() const
Get origin airport ICAO code.
const CAirportIcaoCode & getDestinationAirportIcao() const
Get destination airport ICAO code.
const QDateTime & getTakeoffTimeActual() const
Get actual takeoff time (actual)
CFlightPlanAircraftInfo getAircraftInfo() const
Get ICAO aircraft NAV/COM equipment.
Heading as used in aviation, can be true or magnetic heading.
Information about the ground status.
bool isRestricted() const
Restricted channel?
CONTAINER findClosest(int number, const ICoordinateGeodetic &coordinate) const
Find 0..n objects closest to the given coordinate.
QString toQString(bool i18n=false) const
Cast as QString.
Class which can be directly used to access an.
Value object encapsulating information about a connection status.
bool isConnected() const
Query status.
bool isDisconnected() const
Query status.
Ecosystem of server belonging together.
Value object for a FSD setup.
bool shouldSendFlightPlanEquipmentInIcaoFormat() const
FSD setup flags.
const QString & getTextCodec() const
Get codec.
bool receiveEuroscopeSimData() const
FSD setup flags.
Value object encapsulating information about login mode.
bool isObserver() const
Is login as observer?
bool isPilot() const
Is login as pilot?
Value object for a raw FSD message.
Value object encapsulating information of a server.
const CFsdSetup & getFsdSetup() const
Get FSD setup.
const QString & getAddress() const
Get address.
const CUser & getUser() const
Get user.
ServerType getServerType() const
Get server type.
Value object encapsulating information of a text message.
void markAsSent()
Mark as sent.
bool isEmpty() const
Empty message.
bool isSupervisorMessage() const
Supervisor message?
Value object encapsulating a list of text messages.
CTextMessageList getPrivateMessages() const
Private messages.
CTextMessageList getRadioMessages() const
Public messages.
CTextMessageList markedAsSent()
Marked as sent.
Value object encapsulating information of a location, kind of simplified CValueObject compliant versi...
const QString & getPassword() const
Get password.
const QString & getRealName() const
Get full name.
const QString & getId() const
Get id.
QString getRealNameAndHomeBase(const QString &separator=QString(" ")) const
Real name + homebase.
Direct in memory access to client (network client) data.
Physical unit angle (radians, degrees)
Physical unit length (length)
int valueInteger(MU unit) const
As integer value.
bool isNull() const
Is quantity null?
double value(MU unit) const
Value in given unit.
QList< int > getHrsMinSecParts() const
Parts hh, mm, ss.
QString getSwiftLiveryString(bool aircraftIcao=true, bool livery=true, bool model=true) const
swift livery string (to be sent via network)
Delegating class which can be directly used to access an.
bool updateOwnCallsign(const aviation::CCallsign &callsign)
Update aircraft's callsign.
aviation::CCallsign getOwnCallsign() const
Own aircraft's callsign.
bool updateOwnIcaoCodes(const aviation::CAircraftIcaoCode &aircraftIcaoData, const aviation::CAirlineIcaoCode &airlineIcaoCode)
Update ICAO data.
swift::misc::geo::CCoordinateGeodetic getOwnAircraftPosition() const
Own aircraft's position.
CSimulatedAircraft getOwnAircraft() const
Own aircraft.
Class which can be directly used to access an.
Comprehensive information of an aircraft.
const QString & getAirlineIcaoCodeDesignator() const
Airline ICAO code designator.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Get aircraft ICAO info.
const QString & getAircraftIcaoCombinedType() const
Aircraft ICAO combined code.
const QString & getAircraftIcaoCodeDesignator() const
Aircraft ICAO code designator.
const simulation::CAircraftModel & getModel() const
Get model (model used for mapping)
const aviation::CAirlineIcaoCode & getAirlineIcaoCode() const
Airline ICAO code if any.
const QString & getModelString() const
Get model string.
QString getSimulatorNameAndVersion() const
Version and simulator details info.
Simple hardcoded info about the corresponding simulator.
Simulator getSimulator() const
Simulator.
Direct threadsafe in memory access to own aircraft.
Direct thread safe in memory access to remote aircraft.
PilotRating
Pilot ratings.
ClientQueryType
Client query types.
@ EuroscopeSimData
Broadcast to announce we request SIMDATA packets.
Capabilities
Client capability flags */.
TextMessageGroups
Message groups.
constexpr int PROTOCOL_REVISION_VATSIM_AUTH
Protocol version.
constexpr int PROTOCOL_REVISION_VATSIM_VELOCITY
Protocol version.
constexpr int PROTOCOL_REVISION_CLASSIC
Protocol version.
Free functions in swift::misc.
auto makeKeysRange(const T &container)
Returns a const CRange for iterating over the keys of a Qt associative container.
bool is09OnlyString(const QString &testString)
String with 0-9 only.
StatusSeverity
Status severities.
auto makePairsRange(const T &container)
Returns a const CRange for iterating over the keys and values of a Qt associative container.
SWIFT_MISC_EXPORT QDateTime fromStringUtc(const QString &dateTimeString, const QString &format)
Same as QDateTime::fromString but QDateTime will be set to UTC.
void errorOccurred(QAbstractSocket::SocketError socketError)
bool isEmpty() const const
QDateTime currentDateTime()
QDateTime currentDateTimeUtc()
qint64 currentMSecsSinceEpoch()
QString toString(QStringView format) const const
QDateTime toUTC() const const
QHash< Key, T >::iterator insert(Key &&key, T &&value)
Key key(const T &value) const const
T value(const Key &key) const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QByteArray toJson(QJsonDocument::JsonFormat format) const const
QJsonObject::iterator insert(QLatin1StringView key, const QJsonValue &value)
bool isEmpty() const const
QJsonValue value(QLatin1StringView key) const const
QString errorString() const const
bool isString() const const
bool toBool(bool defaultValue) const const
QJsonObject toObject() const const
QString toString() const const
QList< T >::const_reference at(qsizetype i) const const
QList< T >::iterator begin()
QList< T >::iterator end()
QList< T >::reference front()
bool isEmpty() const const
QList< T > mid(qsizetype pos, qsizetype length) const const
void push_back(QList< T >::parameter_type value)
void push_front(QList< T >::parameter_type value)
qsizetype size() const const
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
int receivers(const char *signal) const const
QObject * sender() const const
void setObjectName(QAnyStringView name)
QString & append(QChar ch)
QString arg(Args &&... args) const const
const QChar at(qsizetype position) const const
QString::const_iterator constBegin() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) &&
QString number(double n, char format, int precision)
void push_front(QChar ch)
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QString right(qsizetype n) &&
QString rightJustified(qsizetype width, QChar fill, bool truncate) const const
qsizetype size() const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
double toDouble(bool *ok) const const
int toInt(bool *ok, int base) const const
QByteArray toUtf8() const const
QString trimmed() const const
const QChar * unicode() const const
std::optional< QStringConverter::Encoding > encodingForName(QAnyStringView name)
QString join(QChar separator) const const
QTextStream & endl(QTextStream &stream)
QTextStream & fixed(QTextStream &stream)
char * toString(QSizePolicy sp)
std::vector< std::string > split(const std::string &str, size_t maxSplitCount=0, const std::string &delimiter=" ")
Split string by delimiter and maxSplitCount times.
QFuture< QtFuture::ArgsType< Signal >> connect(Sender *sender, Signal signal)
#define SWIFT_AUDIT_X(COND, WHERE, WHAT)
A weaker kind of verify.
#define SWIFT_VERIFY_X(COND, WHERE, WHAT)
A weaker kind of assert.