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");
153 m_protocolRevision = protocolRev;
161 "Can't change callsign while still connected");
165 m_ownCallsign = callsign;
171 "Can't change ICAO codes while still connected");
188 bool sendModelString)
191 m_ownLivery = livery;
192 m_ownModelString = modelString;
193 m_sendLiveryString = sendLiveryString;
194 m_sendModelString = sendModelString;
204 case CSimulatorInfo::P3D: m_simType = SimType::P3Dv4;
break;
205 case CSimulatorInfo::FG: m_simType = SimType::FlightGear;
break;
206 case CSimulatorInfo::XPLANE: m_simType = SimType::XPLANE11;
break;
207 case CSimulatorInfo::MSFS: m_simType = SimType::MSFS;
break;
208 case CSimulatorInfo::MSFS2024: m_simType = SimType::MSFS2024;
break;
209 default: m_simType = SimType::Unknown;
break;
236 if (m_socket->isOpen()) {
return; }
237 Q_ASSERT(!m_clientName.
isEmpty());
238 Q_ASSERT((m_versionMajor + m_versionMinor) > 0);
239 Q_ASSERT(m_capabilities != Capabilities::None);
244 m_filterPasswordFromLogin =
true;
247 const qint64 timerMs = qRound(PendingConnectionTimeoutMs * 1.25);
252 this->pendingTimeoutCheck();
255 this->updateConnectionStatus(CConnectionStatus::Connecting);
257 initiateConnection();
270 this->stopPositionTimers();
271 this->updateConnectionStatus(CConnectionStatus::Disconnecting);
275 if (m_socket->isOpen())
277 if (mode.
isPilot()) { this->sendDeletePilot(); }
278 else if (mode.
isObserver()) { this->sendDeleteAtc(); }
282 this->updateConnectionStatus(CConnectionStatus::Disconnected);
286 void CFSDClient::sendLogin(
const QString &token)
297 const AddPilot pilotLogin(callsign, cid, password, m_pilotRating, m_protocolRevision, m_simType, name);
298 sendQueuedMessage(pilotLogin);
300 << callsign << cid << toQString(m_pilotRating) << m_protocolRevision << toQString(m_simType) << name;
304 const AddAtc addAtc(callsign, name, cid, password, m_atcRating, m_protocolRevision);
305 sendQueuedMessage(addAtc);
307 << callsign << cid << toQString(m_atcRating) << m_protocolRevision << name;
312 this->sendClientQuery(ClientQueryType::EuroscopeSimData, {}, {});
316 void CFSDClient::sendDeletePilot()
320 sendQueuedMessage(deletePilot);
323 void CFSDClient::sendDeleteAtc()
326 const DeleteAtc deleteAtc(getOwnCallsignAsString(), cid);
327 sendQueuedMessage(deleteAtc);
330 void CFSDClient::sendPilotDataUpdate()
334 if (m_loginMode == CLoginMode::Observer)
336 sendAtcDataUpdate(myAircraft.latitude().value(CAngleUnit::deg()),
337 myAircraft.longitude().value(CAngleUnit::deg()));
341 if (this->isVisualPositionSendingEnabledForServer())
346 sendVisualPilotDataUpdate(
true);
350 PilotDataUpdate pilotDataUpdate(
351 myAircraft.getTransponderMode(), getOwnCallsignAsString(),
352 static_cast<qint16
>(myAircraft.getTransponderCode()), r, myAircraft.latitude().value(CAngleUnit::deg()),
353 myAircraft.longitude().value(CAngleUnit::deg()),
354 myAircraft.getAltitude().valueInteger(CLengthUnit::ft()),
355 myAircraft.getPressureAltitude().valueInteger(CLengthUnit::ft()),
356 myAircraft.getGroundSpeed().valueInteger(CSpeedUnit::kts()),
357 myAircraft.getPitch().value(CAngleUnit::deg()), myAircraft.getBank().value(CAngleUnit::deg()),
358 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
359 myAircraft.getParts().isOnGround());
360 sendQueuedMessage(pilotDataUpdate);
364 void CFSDClient::sendInterimPilotDataUpdate()
368 InterimPilotDataUpdate interimPilotDataUpdate(
369 getOwnCallsignAsString(),
QString(), myAircraft.latitude().value(CAngleUnit::deg()),
370 myAircraft.longitude().value(CAngleUnit::deg()), myAircraft.getAltitude().valueInteger(CLengthUnit::ft()),
371 myAircraft.getGroundSpeed().valueInteger(CSpeedUnit::kts()), myAircraft.getPitch().value(CAngleUnit::deg()),
372 myAircraft.getBank().value(CAngleUnit::deg()),
373 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
374 myAircraft.getParts().isOnGround());
376 for (
const auto &receiver : std::as_const(m_interimPositionReceivers))
378 interimPilotDataUpdate.setReceiver(receiver.asString());
379 sendQueuedMessage(interimPilotDataUpdate);
384 void CFSDClient::sendVisualPilotDataUpdate(
bool slowUpdate)
387 if (m_loginMode == CLoginMode::Observer || !isVisualPositionSendingEnabledForServer()) {
return; }
392 static constexpr
double minVelocity = 0.00005;
393 if (std::abs(myAircraft.getVelocity().getVelocityX(CSpeedUnit::m_s())) < minVelocity &&
394 std::abs(myAircraft.getVelocity().getVelocityY(CSpeedUnit::m_s())) < minVelocity &&
395 std::abs(myAircraft.getVelocity().getVelocityZ(CSpeedUnit::m_s())) < minVelocity &&
396 std::abs(myAircraft.getVelocity().getPitchVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity &&
397 std::abs(myAircraft.getVelocity().getRollVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity &&
398 std::abs(myAircraft.getVelocity().getHeadingVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity)
400 if (m_stoppedSendingVisualPositions) {
return; }
401 m_stoppedSendingVisualPositions =
true;
402 m_visualPositionUpdateSentCount = 0;
404 else { m_stoppedSendingVisualPositions =
false; }
406 if (!m_serverWantsVisualPositions) {
return; }
408 VisualPilotDataUpdate visualPilotDataUpdate(
409 getOwnCallsignAsString(), myAircraft.latitude().value(CAngleUnit::deg()),
410 myAircraft.longitude().value(CAngleUnit::deg()), myAircraft.getAltitude().value(CLengthUnit::ft()),
411 myAircraft.getAltitude().value(CLengthUnit::ft()) -
412 myAircraft.getGroundElevation().value(CLengthUnit::ft()),
413 myAircraft.getPitch().value(CAngleUnit::deg()), myAircraft.getBank().value(CAngleUnit::deg()),
414 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
415 myAircraft.getVelocity().getVelocityX(CSpeedUnit::m_s()),
416 myAircraft.getVelocity().getVelocityY(CSpeedUnit::m_s()),
417 myAircraft.getVelocity().getVelocityZ(CSpeedUnit::m_s()),
418 myAircraft.getVelocity().getPitchVelocity(CAngleUnit::rad(), CTimeUnit::s()),
419 myAircraft.getVelocity().getRollVelocity(CAngleUnit::rad(), CTimeUnit::s()),
420 myAircraft.getVelocity().getHeadingVelocity(CAngleUnit::rad(), CTimeUnit::s()));
422 if (m_stoppedSendingVisualPositions) { sendQueuedMessage(visualPilotDataUpdate.toStopped()); }
423 else if (m_visualPositionUpdateSentCount++ % 25 == 0) { sendQueuedMessage(visualPilotDataUpdate.toPeriodic()); }
424 else { sendQueuedMessage(visualPilotDataUpdate); }
427 void CFSDClient::sendAtcDataUpdate(
double latitude,
double longitude)
429 const AtcDataUpdate atcDataUpdate(getOwnCallsignAsString(), 199998, CFacilityType::OBS, 300,
430 AtcRating::Observer, latitude, longitude, 0);
431 sendQueuedMessage(atcDataUpdate);
434 void CFSDClient::sendPing(
const QString &receiver)
439 const Ping ping(getOwnCallsignAsString(), receiver, timeString);
440 sendQueuedMessage(ping);
443 increaseStatisticsValue(QStringLiteral(
"sendPing"));
446 void CFSDClient::sendClientQueryIsValidAtc(
const CCallsign &callsign)
448 sendClientQuery(ClientQueryType::IsValidATC, {}, { callsign.
asString() });
451 void CFSDClient::sendClientQueryCapabilities(
const CCallsign &callsign)
453 sendClientQuery(ClientQueryType::Capabilities, callsign);
458 sendClientQuery(ClientQueryType::Com1Freq, callsign);
463 sendClientQuery(ClientQueryType::RealName, callsign);
468 sendClientQuery(ClientQueryType::Server, callsign);
473 sendClientQuery(ClientQueryType::ATIS, callsign);
478 sendClientQuery(ClientQueryType::FP, {}, { callsign.
toQString() });
484 data = convertToUnicodeEscaped(data);
485 sendClientQuery(ClientQueryType::AircraftConfig, callsign, { data });
490 if (queryType == ClientQueryType::Unknown) {
return; }
500 if (queryType == ClientQueryType::IsValidATC)
502 const ClientQuery clientQuery(getOwnCallsignAsString(),
"SERVER", ClientQueryType::IsValidATC, queryData);
503 sendQueuedMessage(clientQuery);
505 else if (queryType == ClientQueryType::Capabilities)
507 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Capabilities);
508 sendQueuedMessage(clientQuery);
510 else if (queryType == ClientQueryType::Com1Freq)
512 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Com1Freq);
513 sendQueuedMessage(clientQuery);
515 else if (queryType == ClientQueryType::RealName)
517 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::RealName);
518 sendQueuedMessage(clientQuery);
520 else if (queryType == ClientQueryType::Server)
522 ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Server);
523 sendQueuedMessage(clientQuery);
525 else if (queryType == ClientQueryType::ATIS)
527 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::ATIS);
528 sendQueuedMessage(clientQuery);
529 if (m_serverType != ServerType::Vatsim) { m_pendingAtisQueries.
insert(receiver, {}); }
531 else if (queryType == ClientQueryType::PublicIP)
533 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::PublicIP);
534 sendQueuedMessage(clientQuery);
536 else if (queryType == ClientQueryType::INF)
538 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::INF);
539 sendQueuedMessage(clientQuery);
541 else if (queryType == ClientQueryType::FP)
543 if (queryData.
isEmpty()) {
return; }
544 const ClientQuery clientQuery(getOwnCallsignAsString(),
"SERVER", ClientQueryType::FP, queryData);
545 sendQueuedMessage(clientQuery);
547 else if (queryType == ClientQueryType::AircraftConfig)
549 if (queryData.
isEmpty()) {
return; }
550 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::AircraftConfig,
552 sendQueuedMessage(clientQuery);
554 else if (queryType == ClientQueryType::EuroscopeSimData)
556 const ClientQuery clientQuery(getOwnCallsignAsString(),
"@94835", ClientQueryType::EuroscopeSimData,
558 sendQueuedMessage(clientQuery);
561 increaseStatisticsValue(QStringLiteral(
"sendClientQuery"), toQString(queryType));
566 if (messages.
isEmpty()) {
return; }
576 const QString ownCallsign = getOwnCallsignAsString();
578 for (
const auto &message : privateMessages)
580 if (message.getRecipientCallsign().isEmpty()) {
continue; }
581 const TextMessage textMessage(ownCallsign, message.getRecipientCallsign().getFsdCallsignString(),
582 message.getMessage());
583 sendQueuedMessage(textMessage);
584 increaseStatisticsValue(QStringLiteral(
"sendTextMessages.PM"));
590 for (
const auto &message : radioMessages)
595 message.getFrequency() :
603 frequencies.push_back(freqkHz);
605 increaseStatisticsValue(QStringLiteral(
"sendTextMessages.FREQ"));
612 if (message.
isEmpty()) {
return; }
618 if (message.
isEmpty()) {
return; }
628 if (receiverGroup == TextMessageGroups::AllClients) { receiver =
'*'; }
629 else if (receiverGroup == TextMessageGroups::AllAtcClients) { receiver = QStringLiteral(
"*A"); }
630 else if (receiverGroup == TextMessageGroups::AllPilotClients) { receiver = QStringLiteral(
"*P"); }
631 else if (receiverGroup == TextMessageGroups::AllSups) { receiver = QStringLiteral(
"*S"); }
633 const TextMessage textMessage(getOwnCallsignAsString(), receiver, message);
634 sendQueuedMessage(textMessage);
635 if (receiver == QStringLiteral(
"*S"))
643 increaseStatisticsValue(QStringLiteral(
"sendTextMessages"));
655 for (
const int &frequency : frequencieskHz)
657 receivers.push_back(QStringLiteral(
"@%1").arg(frequency - 100000));
661 sendQueuedMessage(radioMessage);
662 increaseStatisticsValue(QStringLiteral(
"sendTextMessages"));
697 Q_ASSERT_X(!act.
isEmpty(), Q_FUNC_INFO,
"Aircraft type must not be empty");
701 const FlightPlan fp(getOwnCallsignAsString(),
"SERVER", flightType, act,
707 timePartsEnroute[CTime::Minutes], timePartsFuel[CTime::Hours],
711 sendQueuedMessage(fp);
712 increaseStatisticsValue(QStringLiteral(
"sendFlightPlan"));
726 sendQueuedMessage(planeInfoRequest);
727 increaseStatisticsValue(QStringLiteral(
"sendPlaneInfoRequest"));
741 SWIFT_VERIFY_X(connected, Q_FUNC_INFO,
"Can't send to server when disconnected");
742 if (!connected) {
return; }
745 const QString modelString = this->getConfiguredModelString(myAircraft);
749 sendQueuedMessage(planeInfoRequestFsinn);
750 increaseStatisticsValue(QStringLiteral(
"sendPlaneInfoRequestFsinn"));
753 void CFSDClient::sendPlaneInformation(
const QString &receiver,
const QString &aircraft,
const QString &airline,
756 const PlaneInformation planeInformation(getOwnCallsignAsString(), receiver, aircraft, airline, livery);
757 sendQueuedMessage(planeInformation);
758 increaseStatisticsValue(QStringLiteral(
"sendPlaneInformation"));
761 void CFSDClient::sendPlaneInformationFsinn(
const CCallsign &callsign)
765 const QString modelString = this->getConfiguredModelString(myAircraft);
766 const PlaneInformationFsinn planeInformationFsinn(
767 getOwnCallsignAsString(), callsign.
toQString(), myAircraft.getAirlineIcaoCodeDesignator(),
768 myAircraft.getAircraftIcaoCodeDesignator(), myAircraft.getAircraftIcaoCombinedType(), modelString);
769 sendQueuedMessage(planeInformationFsinn);
770 increaseStatisticsValue(QStringLiteral(
"sendPlaneInformationFsinn"));
773 void CFSDClient::sendAircraftConfiguration(
const QString &receiver,
const QString &aircraftConfigJson)
775 if (aircraftConfigJson.
size() == 0) {
return; }
776 const ClientQuery clientQuery(getOwnCallsignAsString(), receiver, ClientQueryType::AircraftConfig,
777 { aircraftConfigJson });
778 sendQueuedMessage(clientQuery);
781 void CFSDClient::sendMessageString(
const QString &message)
783 if (message.
isEmpty()) {
return; }
784 const QByteArray bufferEncoded = m_encoder(message);
785 if (m_printToConsole) { qDebug() <<
"FSD Sent=>" << bufferEncoded; }
786 if (!m_unitTestMode) { m_socket->write(bufferEncoded); }
789 emitRawFsdMessage(message.
trimmed(),
true);
792 void CFSDClient::sendQueuedMessage()
794 if (m_queuedFsdMessages.isEmpty()) {
return; }
795 const qsizetype s = m_queuedFsdMessages.size();
796 this->sendMessageString(m_queuedFsdMessages.
dequeue());
799 if (s > 5) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
800 if (s > 10) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
801 if (s > 20) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
802 if (s > 30) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
808 const StatusSeverity severity = s > 75 ? SeverityWarning : SeverityInfo;
809 CLogMessage(
this).
log(severity, u
"Too many queued messages (%1), bulk send!") << s;
811 if (s > 75) { sendNo = 20; }
812 if (s > 100) { sendNo = 30; }
814 for (
int i = 0; i < sendNo; i++) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
818 void CFSDClient::sendFsdMessage(
const QString &message)
821 parseMessage(message);
826 if (!m_sendModelString) {
return noModelString(); }
829 return ms.
isEmpty() ? noModelString() : ms;
834 if (!m_sendLiveryString) {
return {}; }
840 void CFSDClient::sendAuthChallenge(
const QString &challenge)
842 const AuthChallenge pduAuthChallenge(getOwnCallsignAsString(),
"SERVER", challenge);
843 sendDirectMessage(pduAuthChallenge);
844 increaseStatisticsValue(QStringLiteral(
"sendAuthChallenge"));
847 void CFSDClient::sendAuthResponse(
const QString &response)
849 const AuthResponse pduAuthResponse(getOwnCallsignAsString(),
"SERVER", response);
850 sendDirectMessage(pduAuthResponse);
851 increaseStatisticsValue(QStringLiteral(
"sendAuthResponse"));
854 void CFSDClient::sendPong(
const QString &receiver,
const QString ×tamp)
856 const Pong pong(getOwnCallsignAsString(), receiver, timestamp);
857 sendQueuedMessage(pong);
858 increaseStatisticsValue(QStringLiteral(
"sendPong"));
861 void CFSDClient::sendClientResponse(ClientQueryType queryType,
const QString &receiver)
863 if (queryType == ClientQueryType::Unknown) {
return; }
864 if (queryType == ClientQueryType::IsValidATC)
866 this->handleIllegalFsdState(
"Never use sendClientResponse with IsValidATC from the client");
870 increaseStatisticsValue(QStringLiteral(
"sendClientResponse"), toQString(queryType));
873 const QString ownCallsign = getOwnCallsignAsString();
875 if (queryType == ClientQueryType::Capabilities)
877 responseData.
clear();
878 if (m_capabilities & Capabilities::AtcInfo) responseData.
push_back(toQString(Capabilities::AtcInfo) %
"=1");
879 if (m_capabilities & Capabilities::SecondaryPos)
880 responseData.
push_back(toQString(Capabilities::SecondaryPos) %
"=1");
881 if (m_capabilities & Capabilities::AircraftInfo)
882 responseData.
push_back(toQString(Capabilities::AircraftInfo) %
"=1");
883 if (m_capabilities & Capabilities::OngoingCoord)
884 responseData.
push_back(toQString(Capabilities::OngoingCoord) %
"=1");
885 if (m_capabilities & Capabilities::InterminPos)
886 responseData.
push_back(toQString(Capabilities::InterminPos) %
"=1");
887 if (m_capabilities & Capabilities::FastPos) responseData.
push_back(toQString(Capabilities::FastPos) %
"=1");
888 if (m_capabilities & Capabilities::VisPos) responseData.
push_back(toQString(Capabilities::VisPos) %
"=1");
889 if (m_capabilities & Capabilities::Stealth) responseData.
push_back(toQString(Capabilities::Stealth) %
"=1");
890 if (m_capabilities & Capabilities::AircraftConfig)
891 responseData.
push_back(toQString(Capabilities::AircraftConfig) %
"=1");
892 if (m_capabilities & Capabilities::IcaoEquipment)
893 responseData.
push_back(toQString(Capabilities::IcaoEquipment) %
"=1");
894 const ClientResponse clientResponse(ownCallsign, receiver, ClientQueryType::Capabilities, responseData);
895 sendQueuedMessage(clientResponse);
897 else if (queryType == ClientQueryType::Com1Freq)
900 getOwnAircraft().getCom1System().getFrequencyActive().value(CFrequencyUnit::MHz()),
'f', 3);
902 const ClientResponse pduClientResponse(ownCallsign, receiver, ClientQueryType::Com1Freq, responseData);
903 sendQueuedMessage(pduClientResponse);
905 else if (queryType == ClientQueryType::RealName)
914 else { responseData.
push_back(toQString(m_pilotRating)); }
916 const ClientResponse pduClientQueryResponse(ownCallsign, receiver, ClientQueryType::RealName, responseData);
917 sendQueuedMessage(pduClientQueryResponse);
919 else if (queryType == ClientQueryType::Server)
922 const ClientResponse pduClientQueryResponse(ownCallsign, receiver, ClientQueryType::Server, responseData);
923 sendQueuedMessage(pduClientQueryResponse);
925 else if (queryType == ClientQueryType::ATIS)
927 this->handleIllegalFsdState(
928 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::ATIS)));
930 else if (queryType == ClientQueryType::PublicIP)
932 this->handleIllegalFsdState(
933 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::PublicIP)));
935 else if (queryType == ClientQueryType::INF)
941 const double latitude = situation.
latitude().
value(CAngleUnit::deg());
942 const double longitude = situation.
longitude().
value(CAngleUnit::deg());
945 std::array<char, 50> sysuid = {};
946 #ifdef SWIFT_VATSIM_SUPPORT
947 vatsim_get_system_unique_id(sysuid.data());
950 const QString userInfo = QStringLiteral(
"CID=") % cid %
" " % m_clientName %
" IP=" %
951 m_socket->localAddress().toString() %
" SYS_UID=" % sysuid.data() %
" FSVER=" %
955 const TextMessage textMessage(ownCallsign, receiver, userInfo);
956 sendQueuedMessage(textMessage);
958 else if (queryType == ClientQueryType::FP)
960 this->handleIllegalFsdState(
961 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::FP)));
963 else if (queryType == ClientQueryType::AircraftConfig)
965 this->handleIllegalFsdState(
966 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::AircraftConfig)));
970 #ifdef SWIFT_VATSIM_SUPPORT
971 void CFSDClient::sendClientIdentification(
const QString &fsdChallenge)
973 std::array<char, 50> sysuid = {};
974 vatsim_get_system_unique_id(sysuid.data());
976 const ClientIdentification clientIdentification(
977 getOwnCallsignAsString(), vatsim_auth_get_client_id(m_clientAuth), m_clientName, m_versionMajor,
978 m_versionMinor, cid, sysuid.
data(), fsdChallenge);
979 this->sendQueuedMessage(clientIdentification);
981 if (
getServer().getEcosystem().isSystem(CEcosystem::VATSIM))
984 { this, [this](const QString &token) {
985 this->sendLogin(token);
986 this->updateConnectionStatus(CConnectionStatus::Connected);
992 this->updateConnectionStatus(CConnectionStatus::Connected);
994 increaseStatisticsValue(QStringLiteral(
"sendClientIdentification"));
998 void CFSDClient::getVatsimAuthToken(
const QString &cid,
const QString &password,
1001 Q_ASSERT_X(
sApp, Q_FUNC_INFO,
"Need app");
1004 const QJsonObject jsonRequest { {
"cid", cid }, {
"password", password } };
1016 nwReply->errorString();
1019 disconnectFromServer();
1021 nwReply->deleteLater();
1025 void CFSDClient::sendIncrementalAircraftConfig()
1027 if (!m_unitTestMode && (!this->isConnected() || !this->getSetupForServer().sendAircraftParts())) {
return; }
1031 if (m_sentAircraftConfig == currentParts) {
return; }
1033 if (!m_tokenBucket.tryConsume()) {
return; }
1035 const QJsonObject previousConfig = m_sentAircraftConfig.toJson();
1036 const QJsonObject currentConfig = currentParts.toJson();
1037 const QJsonObject incrementalConfig = getIncrementalObject(previousConfig, currentConfig);
1039 const QString dataStr = convertToUnicodeEscaped(
1042 sendAircraftConfiguration(
"@94836", dataStr);
1043 m_sentAircraftConfig = currentParts;
1046 void CFSDClient::initializeMessageTypes()
1048 m_messageTypeMapping[
"#AA"] = MessageType::AddAtc;
1049 m_messageTypeMapping[
"#AP"] = MessageType::AddPilot;
1050 m_messageTypeMapping[
"%"] = MessageType::AtcDataUpdate;
1051 m_messageTypeMapping[
"$ZC"] = MessageType::AuthChallenge;
1052 m_messageTypeMapping[
"$ZR"] = MessageType::AuthResponse;
1053 m_messageTypeMapping[
"$ID"] = MessageType::ClientIdentification;
1054 m_messageTypeMapping[
"$CQ"] = MessageType::ClientQuery;
1055 m_messageTypeMapping[
"$CR"] = MessageType::ClientResponse;
1056 m_messageTypeMapping[
"#DA"] = MessageType::DeleteATC;
1057 m_messageTypeMapping[
"#DP"] = MessageType::DeletePilot;
1058 m_messageTypeMapping[
"$FP"] = MessageType::FlightPlan;
1059 m_messageTypeMapping[
"#PC"] = MessageType::ProController;
1060 m_messageTypeMapping[
"$DI"] = MessageType::FsdIdentification;
1061 m_messageTypeMapping[
"$!!"] = MessageType::KillRequest;
1062 m_messageTypeMapping[
"@"] = MessageType::PilotDataUpdate;
1063 m_messageTypeMapping[
"^"] = MessageType::VisualPilotDataUpdate;
1064 m_messageTypeMapping[
"#SL"] = MessageType::VisualPilotDataPeriodic;
1065 m_messageTypeMapping[
"#ST"] = MessageType::VisualPilotDataStopped;
1066 m_messageTypeMapping[
"$SF"] = MessageType::VisualPilotDataToggle;
1067 m_messageTypeMapping[
"$PI"] = MessageType::Ping;
1068 m_messageTypeMapping[
"$PO"] = MessageType::Pong;
1069 m_messageTypeMapping[
"$ER"] = MessageType::ServerError;
1070 m_messageTypeMapping[
"#DL"] = MessageType::ServerHeartbeat;
1071 m_messageTypeMapping[
"#TM"] = MessageType::TextMessage;
1072 m_messageTypeMapping[
"#SB"] = MessageType::PilotClientCom;
1073 m_messageTypeMapping[
"$XX"] = MessageType::Rehost;
1074 m_messageTypeMapping[
"#MU"] = MessageType::Mute;
1077 m_messageTypeMapping[
"SIMDATA"] = MessageType::EuroscopeSimData;
1080 void CFSDClient::handleAtcDataUpdate(
const QStringList &tokens)
1082 const AtcDataUpdate atcDataUpdate = AtcDataUpdate::fromTokens(tokens);
1083 const QString senderCs = atcDataUpdate.sender();
1084 const CCallsign cs(senderCs, CCallsign::Atc);
1087 if (atcDataUpdate.m_facility == CFacilityType::Unknown && !cs.isObserverCallsign())
1091 if (atcDataUpdate.m_facility == CFacilityType::OBS && !cs.hasSuffix()) {
return; }
1093 CFrequency freq(atcDataUpdate.m_frequencykHz, CFrequencyUnit::kHz());
1094 freq.switchUnit(CFrequencyUnit::MHz());
1100 const CLength networkRange(atcDataUpdate.m_visibleRange, CLengthUnit::NM());
1101 const CLength range = fixAtcRange(networkRange, cs);
1102 const CCoordinateGeodetic position(atcDataUpdate.m_latitude, atcDataUpdate.m_longitude, 0);
1104 emit this->atcDataUpdateReceived(cs, freq, position, range);
1106 m_atcStations.replaceOrAddObjectByCallsign({ cs, {}, freq, position, range });
1109 #ifdef SWIFT_VATSIM_SUPPORT
1110 void CFSDClient::handleAuthChallenge(
const QStringList &tokens)
1112 const AuthChallenge authChallenge = AuthChallenge::fromTokens(tokens);
1114 vatsim_auth_generate_response(m_clientAuth, qPrintable(authChallenge.m_challengeKey), response);
1115 sendAuthResponse(
QString(response));
1118 vatsim_auth_generate_challenge(m_serverAuth, challenge);
1119 m_lastServerAuthChallenge =
QString(challenge);
1120 sendAuthChallenge(m_lastServerAuthChallenge);
1124 #ifdef SWIFT_VATSIM_SUPPORT
1125 void CFSDClient::handleAuthResponse(
const QStringList &tokens)
1127 const AuthResponse authResponse = AuthResponse::fromTokens(tokens);
1129 char expectedResponse[33];
1130 vatsim_auth_generate_response(m_serverAuth, qPrintable(m_lastServerAuthChallenge), expectedResponse);
1131 if (authResponse.m_response !=
QString(expectedResponse))
1133 CLogMessage().
error(u
"The server you are connected to is not a VATSIM server. Disconnecting!");
1134 disconnectFromServer();
1139 void CFSDClient::handleDeleteATC(
const QStringList &tokens)
1141 const DeleteAtc deleteAtc = DeleteAtc::fromTokens(tokens);
1142 emit deleteAtcReceived(deleteAtc.m_cid);
1144 m_atcStations.removeByCallsign(deleteAtc.m_cid);
1147 void CFSDClient::handleDeletePilot(
const QStringList &tokens)
1149 const DeletePilot deletePilot = DeletePilot::fromTokens(tokens);
1150 const CCallsign cs(deletePilot.sender(), CCallsign::Aircraft);
1152 emit deletePilotReceived(deletePilot.m_cid);
1155 void CFSDClient::handleTextMessage(
const QStringList &tokens)
1157 const TextMessage textMessage = TextMessage::fromTokens(tokens);
1159 const CCallsign sender(textMessage.sender());
1160 const CCallsign receiver(textMessage.receiver());
1162 if (textMessage.m_type == TextMessage::PrivateMessage)
1168 if (m_server.getEcosystem() != CEcosystem::vatsim() && m_ownCallsign.asString() == textMessage.receiver() &&
1169 m_pendingAtisQueries.contains(sender))
1171 maybeHandleAtisReply(sender, receiver, textMessage.m_message);
1175 CTextMessage tm(textMessage.m_message, sender, receiver);
1176 tm.setCurrentUtcTime();
1177 this->consolidateTextMessage(tm);
1179 else if (textMessage.m_type == TextMessage::RadioMessage)
1181 const CFrequency com1 = getOwnAircraft().getCom1System().getFrequencyActive();
1182 const CFrequency com2 = getOwnAircraft().getCom2System().getFrequencyActive();
1185 for (
int freqKhz : textMessage.m_frequencies)
1187 CFrequency f(freqKhz, CFrequencyUnit::kHz());
1188 CComSystem::roundToChannelSpacing(f, CComSystem::ChannelSpacing8_33KHz);
1189 if (f == com1 || f == com2) { frequencies.
push_back(f); }
1191 if (frequencies.
isEmpty()) {
return; }
1193 messages.setCurrentUtcTime();
1194 emit textMessagesReceived(messages);
1198 void CFSDClient::handlePilotDataUpdate(
const QStringList &tokens)
1200 const PilotDataUpdate dataUpdate = PilotDataUpdate::fromTokens(tokens);
1201 const CCallsign callsign(dataUpdate.sender(), CCallsign::Aircraft);
1204 callsign,
CCoordinateGeodetic(dataUpdate.m_latitude, dataUpdate.m_longitude, dataUpdate.m_altitudeTrue),
1205 CHeading(dataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1206 CAngle(dataUpdate.m_pitch, CAngleUnit::deg()),
CAngle(dataUpdate.m_bank, CAngleUnit::deg()),
1207 CSpeed(dataUpdate.m_groundSpeed, CSpeedUnit::kts()));
1209 CAltitude::PressureAltitude, CLengthUnit::ft()));
1211 const COnGroundInfo og(dataUpdate.m_onGround ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
1212 COnGroundInfo::NotSetGroundDetails);
1217 const qint64 offsetTimeMs =
1224 if (CTransponder::isValidTransponderCode(dataUpdate.m_transponderCode))
1226 transponder =
CTransponder(dataUpdate.m_transponderCode, dataUpdate.m_transponderMode);
1230 if (CBuildConfig::isLocalDeveloperDebugBuild())
1233 << dataUpdate.m_transponderCode << callsign;
1237 transponder =
CTransponder(2000, CTransponder::StateStandby);
1239 emit pilotDataUpdateReceived(situation, transponder);
1242 void CFSDClient::handleEuroscopeSimData(
const QStringList &tokens)
1248 CHeading(data.m_heading, CAngleUnit::deg()),
1249 CAngle(-data.m_pitch, CAngleUnit::deg()),
CAngle(-data.m_bank, CAngleUnit::deg()),
1250 CSpeed(data.m_groundSpeed, CSpeedUnit::kts()));
1252 const COnGroundInfo og(data.m_onGround ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
1253 COnGroundInfo::NotSetGroundDetails);
1258 const qint64 offsetTimeMs =
1267 emit euroscopeSimDataUpdatedReceived(situation, parts, currentOffsetTime(data.sender()), data.m_model,
1274 VisualPilotDataUpdate dataUpdate;
1275 switch (messageType)
1277 case MessageType::VisualPilotDataUpdate: dataUpdate = VisualPilotDataUpdate::fromTokens(tokens);
break;
1278 case MessageType::VisualPilotDataPeriodic: dataUpdate = VisualPilotDataPeriodic::fromTokens(tokens).toUpdate();
break;
1279 case MessageType::VisualPilotDataStopped: dataUpdate = VisualPilotDataStopped::fromTokens(tokens).toUpdate();
break;
1280 default: qFatal(
"Precondition violated");
break;
1282 const CCallsign callsign(dataUpdate.sender(), CCallsign::Aircraft);
1286 CCoordinateGeodetic(dataUpdate.m_latitude, dataUpdate.m_longitude, dataUpdate.m_altitudeTrue),
1287 CHeading(dataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1288 CAngle(dataUpdate.m_pitch, CAngleUnit::deg()),
1289 CAngle(dataUpdate.m_bank, CAngleUnit::deg()));
1301 emit visualPilotDataUpdateReceived(situation);
1305 void CFSDClient::handleVisualPilotDataToggle(
const QStringList &tokens)
1307 const VisualPilotDataToggle toggle = VisualPilotDataToggle::fromTokens(tokens);
1308 m_serverWantsVisualPositions = toggle.m_active;
1311 void CFSDClient::handlePing(
const QStringList &tokens)
1313 const Ping ping = Ping::fromTokens(tokens);
1314 sendPong(ping.sender(), ping.m_timestamp);
1317 void CFSDClient::handlePong(
const QStringList &tokens)
1319 const Pong pong = Pong::fromTokens(tokens);
1321 const qint64 elapsedTime = msecSinceEpoch - pong.m_timestamp.toLongLong();
1322 emit pongReceived(pong.sender(),
static_cast<double>(elapsedTime));
1325 void CFSDClient::handleKillRequest(
const QStringList &tokens)
1327 KillRequest killRequest = KillRequest::fromTokens(tokens);
1328 emit killRequestReceived(killRequest.m_reason);
1329 disconnectFromServer();
1332 void CFSDClient::handleFlightPlan(
const QStringList &tokens)
1334 FlightPlan fp = FlightPlan::fromTokens(tokens);
1337 switch (fp.m_flightType)
1339 case FlightType::VFR: rules = CFlightPlan::VFR;
break;
1340 case FlightType::IFR: rules = CFlightPlan::IFR;
break;
1341 case FlightType::DVFR: rules = CFlightPlan::DVFR;
break;
1342 case FlightType::SVFR: rules = CFlightPlan::SVFR;
break;
1348 int ca = cruiseAltString.
toInt();
1352 if (rules == CFlightPlan::IFR)
1354 if (ca >= 1000) { cruiseAltString = u
"FL" %
QString::number(ca / 100); }
1355 else { cruiseAltString = u
"FL" % cruiseAltString; }
1359 if (ca >= 5000) { cruiseAltString = u
"FL" %
QString::number(ca / 100); }
1360 else { cruiseAltString = cruiseAltString % u
"ft"; }
1364 cruiseAlt.
parseFromString(cruiseAltString, CPqString::SeparatorBestGuess);
1369 const CCallsign callsign(fp.sender(), CCallsign::Aircraft);
1373 CTime(fp.m_hoursEnroute * 60 + fp.m_minutesEnroute, CTimeUnit::min()),
1374 CTime(fp.m_fuelAvailHours * 60 + fp.m_fuelAvailMinutes, CTimeUnit::min()), cruiseAlt,
1375 CSpeed(fp.m_trueCruisingSpeed, CSpeedUnit::kts()), rules, fp.m_route, fp.m_remarks);
1377 emit flightPlanReceived(callsign, flightPlan);
1380 void CFSDClient::handleClientQuery(
const QStringList &tokens)
1382 const ClientQuery clientQuery = ClientQuery::fromTokens(tokens);
1384 if (clientQuery.m_queryType == ClientQueryType::Unknown) {
return; }
1385 if (clientQuery.m_queryType == ClientQueryType::IsValidATC)
1389 else if (clientQuery.m_queryType == ClientQueryType::Capabilities)
1391 sendClientResponse(ClientQueryType::Capabilities, clientQuery.sender());
1393 else if (clientQuery.m_queryType == ClientQueryType::Com1Freq)
1395 sendClientResponse(ClientQueryType::Com1Freq, clientQuery.sender());
1397 else if (clientQuery.m_queryType == ClientQueryType::RealName)
1399 sendClientResponse(ClientQueryType::RealName, clientQuery.sender());
1401 else if (clientQuery.m_queryType == ClientQueryType::Server)
1403 sendClientResponse(ClientQueryType::Server, clientQuery.sender());
1405 else if (clientQuery.m_queryType == ClientQueryType::ATIS)
1409 else if (clientQuery.m_queryType == ClientQueryType::PublicIP)
1413 else if (clientQuery.m_queryType == ClientQueryType::INF)
1415 sendClientResponse(ClientQueryType::INF, clientQuery.sender());
1417 else if (clientQuery.m_queryType == ClientQueryType::FP)
1421 else if (clientQuery.m_queryType == ClientQueryType::AircraftConfig)
1424 QString aircraftConfigJson = aircraftConfigTokens.
join(
":");
1426 const CCallsign callsign(clientQuery.sender(), CCallsign::Aircraft);
1434 CLogMessage(
this).
warning(u
"Failed to parse aircraft config packet: '%1' packet: '%2'")
1440 if (packet == JsonPackets::aircraftConfigRequest())
1444 QJsonObject config = this->getOwnAircraftParts().toJson();
1445 config.
insert(CAircraftParts::attributeNameIsFullJson(),
true);
1447 data = convertToUnicodeEscaped(data);
1448 sendAircraftConfiguration(clientQuery.sender(), data);
1452 const bool inRange = isAircraftInRange(callsign);
1453 if (!inRange) {
return; }
1454 if (!getSetupForServer().receiveAircraftParts()) {
return; }
1456 if (config.
isEmpty()) {
return; }
1458 const qint64 offsetTimeMs = currentOffsetTime(callsign);
1459 emit aircraftConfigReceived(clientQuery.sender(), config, offsetTimeMs);
1464 void CFSDClient::handleClientResponse(
const QStringList &tokens)
1466 const ClientResponse clientResponse = ClientResponse::fromTokens(tokens);
1467 if (clientResponse.isUnknownQuery()) {
return; }
1468 const QString sender = clientResponse.sender();
1473 if (!clientResponse.m_responseData.empty()) { responseData1 = clientResponse.m_responseData.
at(0); }
1475 if (clientResponse.m_responseData.size() > 1) { responseData2 = clientResponse.m_responseData.
at(1); }
1477 if (clientResponse.m_queryType == ClientQueryType::IsValidATC)
1479 emit validAtcResponseReceived(responseData2, responseData1 == u
"Y");
1481 else if (clientResponse.m_queryType == ClientQueryType::Capabilities)
1484 for (
auto keyValuePair : clientResponse.m_responseData)
1486 if (keyValuePair.count(
'=') != 1) {
continue; }
1489 if (
split.size() < 2) {
continue; }
1493 if (value ==
"1") { capabilities |= fromQString<Capabilities>(key); }
1496 CClient::Capabilities caps = CClient::None;
1497 if (capabilities & Capabilities::AtcInfo) { caps |= CClient::FsdAtisCanBeReceived; }
1498 if (capabilities & Capabilities::FastPos) { caps |= CClient::FsdWithInterimPositions; }
1499 if (capabilities & Capabilities::VisPos) { caps |= CClient::FsdWithVisualPositions; }
1500 if (capabilities & Capabilities::AircraftInfo) { caps |= CClient::FsdWithIcaoCodes; }
1501 if (capabilities & Capabilities::AircraftConfig) { caps |= CClient::FsdWithAircraftConfig; }
1503 emit capabilityResponseReceived(clientResponse.sender(), caps);
1505 else if (clientResponse.m_queryType == ClientQueryType::Com1Freq)
1507 if (responseData1.
isEmpty()) {
return; }
1509 const double freqMHz = responseData1.
toDouble(&ok);
1510 if (!ok) {
return; }
1511 emit com1FrequencyResponseReceived(clientResponse.sender(),
CFrequency(freqMHz, CFrequencyUnit::MHz()));
1513 else if (clientResponse.m_queryType == ClientQueryType::RealName)
1516 emit realNameResponseReceived(clientResponse.sender(), responseData1);
1518 else if (clientResponse.m_queryType == ClientQueryType::Server)
1520 emit serverResponseReceived(clientResponse.sender(), responseData1);
1522 else if (clientResponse.m_queryType == ClientQueryType::ATIS)
1524 updateAtisMap(clientResponse.sender(), fromQString<AtisLineType>(responseData1), responseData2);
1526 else if (clientResponse.m_queryType == ClientQueryType::PublicIP)
1530 else if (clientResponse.m_queryType == ClientQueryType::INF)
1534 else if (clientResponse.m_queryType == ClientQueryType::FP)
1538 else if (clientResponse.m_queryType == ClientQueryType::AircraftConfig)
1545 void CFSDClient::handleServerError(
const QStringList &tokens)
1547 const ServerError serverError = ServerError::fromTokens(tokens);
1548 switch (serverError.m_errorNumber)
1550 case ServerErrorCode::CallsignInUse:
CLogMessage(
this).
error(u
"The requested callsign is already taken");
break;
1551 case ServerErrorCode::InvalidCallsign:
CLogMessage(
this).
error(u
"The requested callsign is not valid");
break;
1552 case ServerErrorCode::InvalidCidPassword:
1553 CLogMessage(
this).
error(u
"Wrong user ID (%1) or password") << m_server.getUser().getId();
1555 case ServerErrorCode::InvalidRevision:
1556 CLogMessage(
this).
error(u
"This server does not support our protocol version");
1558 case ServerErrorCode::ServerFull:
CLogMessage(
this).
error(u
"The server is full");
break;
1559 case ServerErrorCode::CidSuspended:
CLogMessage(
this).
error(u
"Your user account is suspended");
break;
1560 case ServerErrorCode::RatingTooLow:
1561 CLogMessage(
this).
error(u
"You are not authorized to use the requested rating");
1563 case ServerErrorCode::InvalidClient:
1564 CLogMessage(
this).
error(u
"This software is not authorized for use on this network");
1566 case ServerErrorCode::RequestedLevelTooHigh:
1567 CLogMessage(
this).
error(u
"You are not authorized to use the requested pilot rating");
1571 case ServerErrorCode::SyntaxError:
1573 u
"Malformed packet, syntax error: '%1'. This can also occur if an OBS sends frequency text messages.")
1574 << serverError.getCausingParameter();
1576 case ServerErrorCode::InvalidSrcCallsign:
1577 CLogMessage(
this).
info(u
"FSD message was using an invalid callsign: %1 (%2)")
1578 << serverError.getCausingParameter() << serverError.getDescription();
1580 case ServerErrorCode::NoSuchCallsign:
1582 << serverError.getCausingParameter() << serverError.getDescription();
1584 case ServerErrorCode::NoFlightPlan:
CLogMessage(
this).
info(u
"FSD Server: no flight plan");
break;
1585 case ServerErrorCode::NoWeatherProfile:
1586 CLogMessage(
this).
info(u
"FSD Server: requested weather profile does not exist");
1590 case ServerErrorCode::AlreadyRegistered:
1591 CLogMessage(
this).
warning(u
"Server says already registered: %1") << serverError.getDescription();
1593 case ServerErrorCode::InvalidCtrl:
1594 CLogMessage(
this).
warning(u
"Server invalid control: %1") << serverError.getDescription();
1596 case ServerErrorCode::Unknown:
1598 << serverError.getCausingParameter() << serverError.getDescription();
1600 case ServerErrorCode::AuthTimeout:
CLogMessage(
this).
warning(u
"Client did not authenticate in time");
break;
1602 if (serverError.isFatalError()) { disconnectFromServer(); }
1605 void CFSDClient::handleRehost(
const QStringList &tokens)
1607 const Rehost rehost = Rehost::fromTokens(tokens);
1609 CLogMessage(
this).
info(u
"Server requested we switch server to %1") << rehost.m_hostname;
1611 SWIFT_AUDIT_X(!m_rehosting, Q_FUNC_INFO,
"Rehosting already in progress");
1614 auto rehostingSocket = std::make_shared<QTcpSocket>();
1616 readDataFromSocket();
1617 CLogMessage(this).debug(u
"Successfully switched server");
1618 m_socket = rehostingSocket;
1619 m_rehosting = false;
1620 rehostingSocket->disconnect(this);
1621 connectSocketSignals();
1622 readDataFromSocket();
1625 CLogMessage(this).warning(u
"Failed to switch server: %1") << rehostingSocket->errorString();
1626 m_rehosting = false;
1627 rehostingSocket->disconnect(this);
1628 if (m_socket->state() != QAbstractSocket::ConnectedState)
1630 updateConnectionStatus(CConnectionStatus::Disconnected);
1634 initiateConnection(rehostingSocket, rehost.m_hostname);
1637 void CFSDClient::handleMute(
const QStringList &tokens)
1639 const Mute mute = Mute::fromTokens(tokens);
1640 if (mute.receiver() != m_ownCallsign.asString()) {
return; }
1641 emit muteRequestReceived(mute.m_mute);
1644 void CFSDClient::initiateConnection(std::shared_ptr<QTcpSocket> rehostingSocket,
const QString &rehostingHost)
1646 const CServer server = this->getServer();
1647 const auto socket = rehostingSocket ? rehostingSocket : m_socket;
1651 const quint16 port = rehostingSocket ? m_socket->peerPort() :
static_cast<quint16
>(getServer().getPort());
1654 resolveLoadBalancing(host, [=,
this](
const QString &host) {
1655 socket->connectToHost(host, port);
1656 if (!rehostingSocket) { this->startPositionTimers(); }
1660 void CFSDClient::resolveLoadBalancing(
const QString &host, std::function<
void(
const QString &)> callback)
1662 if (
QHostAddress(host).isNull() && (getServer().getName() ==
"AUTOMATIC" || m_rehosting) &&
1663 getServer().getEcosystem() == CEcosystem::VATSIM)
1666 Q_ASSERT_X(
sApp, Q_FUNC_INFO,
"Need app");
1676 callback(addr.toString());
1683 else { callback(host); }
1686 void CFSDClient::handleCustomPilotPacket(
const QStringList &tokens)
1690 if (subType == u
"PIR")
1692 PlaneInfoRequest planeInfoRequest = PlaneInfoRequest::fromTokens(tokens);
1695 const QString airlineIcao = m_server.getFsdSetup().force3LetterAirlineCodes() ?
1699 const QString livery = this->getConfiguredLiveryString(myAircraft);
1701 sendPlaneInformation(planeInfoRequest.sender(), acTypeICAO, airlineIcao, livery);
1703 else if (subType ==
"PI")
1705 if (tokens.
size() > 6 && tokens.
at(3) ==
"X")
1709 else if (tokens.
size() > 4 && tokens.
at(3) ==
"GEN")
1711 const PlaneInformation planeInformation = PlaneInformation::fromTokens(tokens);
1712 emit planeInformationReceived(planeInformation.sender(), planeInformation.m_aircraft,
1713 planeInformation.m_airline, planeInformation.m_livery);
1716 else if (subType ==
"I")
1720 else if (subType ==
"VI")
1723 if (!isInterimPositionReceivingEnabledForServer()) {
return; }
1725 const InterimPilotDataUpdate interimPilotDataUpdate = InterimPilotDataUpdate::fromTokens(tokens);
1726 const CCallsign callsign(interimPilotDataUpdate.sender(), CCallsign::Aircraft);
1730 interimPilotDataUpdate.m_longitude,
1731 interimPilotDataUpdate.m_altitudeTrue),
1732 CHeading(interimPilotDataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1733 CAngle(interimPilotDataUpdate.m_pitch, CAngleUnit::deg()),
1734 CAngle(interimPilotDataUpdate.m_bank, CAngleUnit::deg()),
1735 CSpeed(interimPilotDataUpdate.m_groundSpeed, CSpeedUnit::kts()));
1737 const COnGroundInfo og(interimPilotDataUpdate.m_onGround ? COnGroundInfo::OnGround :
1738 COnGroundInfo::NotOnGround,
1739 COnGroundInfo::NotSetGroundDetails);
1744 const qint64 offsetTimeMs =
1748 emit interimPilotDataUpdatedReceived(situation);
1750 else if (subType ==
"FSIPI")
1752 const PlaneInformationFsinn planeInformationFsinn = PlaneInformationFsinn::fromTokens(tokens);
1753 emit planeInformationFsinnReceived(planeInformationFsinn.sender(), planeInformationFsinn.m_airlineIcao,
1754 planeInformationFsinn.m_aircraftIcao,
1755 planeInformationFsinn.m_aircraftIcaoCombinedType,
1756 planeInformationFsinn.m_sendMModelString);
1758 else if (subType ==
"FSIPIR")
1760 const PlaneInfoRequestFsinn planeInfoRequestFsinn = PlaneInfoRequestFsinn::fromTokens(tokens);
1761 sendPlaneInformationFsinn(planeInfoRequestFsinn.sender());
1762 emit planeInformationFsinnReceived(planeInfoRequestFsinn.sender(), planeInfoRequestFsinn.m_airlineIcao,
1763 planeInfoRequestFsinn.m_aircraftIcao,
1764 planeInfoRequestFsinn.m_aircraftIcaoCombinedType,
1765 planeInfoRequestFsinn.m_sendMModelString);
1772 emit customPilotPacketReceived(sender, data);
1776 #ifdef SWIFT_VATSIM_SUPPORT
1777 void CFSDClient::handleFsdIdentification(
const QStringList &tokens)
1781 const FSDIdentification fsdIdentification = FSDIdentification::fromTokens(tokens);
1782 vatsim_auth_set_initial_challenge(m_clientAuth, qPrintable(fsdIdentification.m_initialChallenge));
1784 char fsdChallenge[33];
1785 vatsim_auth_generate_challenge(m_serverAuth, fsdChallenge);
1786 vatsim_auth_set_initial_challenge(m_serverAuth, fsdChallenge);
1787 sendClientIdentification(
QString(fsdChallenge));
1792 u
"You tried to connect to a VATSIM server without using VATSIM protocol, disconnecting!");
1793 disconnectFromServer();
1798 void CFSDClient::handleUnknownPacket(
const QString &line)
1803 void CFSDClient::handleUnknownPacket(
const QStringList &tokens) { this->handleUnknownPacket(tokens.
join(
", ")); }
1807 if (m_rehosting) {
return; }
1809 CLogMessage(
this).
error(u
"FSD socket error: %1") << this->socketErrorString(socketError);
1814 if (m_rehosting) {
return; }
1816 const QString error = this->socketErrorString(socketError);
1817 switch (socketError)
1821 emit this->severeNetworkError(error);
1822 this->disconnectFromServer();
1828 void CFSDClient::handleSocketConnected()
1833 this->updateConnectionStatus(CConnectionStatus::Connected);
1839 if (this->getConnectionStatus() == newStatus) {
return; }
1845 m_server.setConnectedSinceNow();
1846 ecoSystem = m_server.getEcosystem();
1848 this->setCurrentEcosystem(ecoSystem);
1853 m_server.markAsDisconnected();
1858 this->stopPositionTimers();
1860 this->setLastEcosystem(m_server.getEcosystem());
1861 this->setCurrentEcosystem(CEcosystem::NoSystem);
1862 this->saveNetworkStatistics(m_server.getName());
1868 oldStatus = m_connectionStatus;
1869 m_connectionStatus = newStatus;
1872 emit this->connectionStatusChanged(oldStatus, newStatus);
1875 void CFSDClient::consolidateTextMessage(
const CTextMessage &textMessage)
1880 m_textMessagesToConsolidate.addConsolidatedTextMessage(textMessage);
1881 m_dsSendTextMessage.inputSignal();
1885 void CFSDClient::emitConsolidatedTextMessages()
1887 emit this->textMessagesReceived(m_textMessagesToConsolidate);
1888 m_textMessagesToConsolidate.clear();
1891 qint64 CFSDClient::receivedPositionFixTsAndGetOffsetTime(
const CCallsign &callsign, qint64 markerTs)
1893 Q_ASSERT_X(!callsign.
isEmpty(), Q_FUNC_INFO,
"Need callsign");
1896 if (!m_lastPositionUpdate.contains(callsign))
1898 m_lastPositionUpdate.insert(callsign, markerTs);
1899 return CFsdSetup::c_positionTimeOffsetMsec;
1901 const qint64 oldTs = m_lastPositionUpdate.value(callsign);
1902 m_lastPositionUpdate[callsign] = markerTs;
1905 const qint64 diff = qAbs(markerTs - oldTs);
1906 this->insertLatestOffsetTime(callsign, diff);
1909 const qint64 avgTimeMs = this->averageOffsetTimeMs(callsign, count, 3);
1910 qint64 offsetTime = CFsdSetup::c_positionTimeOffsetMsec;
1912 if (avgTimeMs < CFsdSetup::c_interimPositionTimeOffsetMsec && count >= 3)
1914 offsetTime = CFsdSetup::c_interimPositionTimeOffsetMsec;
1917 return m_additionalOffsetTime + offsetTime;
1920 qint64 CFSDClient::currentOffsetTime(
const CCallsign &callsign)
const
1922 Q_ASSERT_X(!callsign.
isEmpty(), Q_FUNC_INFO,
"Need callsign");
1924 if (!m_lastOffsetTimes.contains(callsign) || m_lastOffsetTimes[callsign].isEmpty())
1926 return CFsdSetup::c_positionTimeOffsetMsec;
1928 return m_lastOffsetTimes[callsign].front();
1931 void CFSDClient::clearState()
1933 m_rehosting =
false;
1934 m_stoppedSendingVisualPositions =
false;
1935 m_serverWantsVisualPositions =
false;
1936 m_visualPositionUpdateSentCount = 0;
1937 m_textMessagesToConsolidate.clear();
1938 m_pendingAtisQueries.clear();
1939 m_lastPositionUpdate.clear();
1940 m_lastOffsetTimes.clear();
1941 m_atcStations.clear();
1942 m_queuedFsdMessages.clear();
1943 m_sentAircraftConfig = CAircraftParts::null();
1947 void CFSDClient::clearState(
const CCallsign &callsign)
1949 if (callsign.
isEmpty()) {
return; }
1950 m_pendingAtisQueries.remove(callsign);
1951 m_lastPositionUpdate.remove(callsign);
1952 m_interimPositionReceivers.remove(callsign);
1953 m_lastOffsetTimes.remove(callsign);
1956 void CFSDClient::insertLatestOffsetTime(
const CCallsign &callsign, qint64 offsetMs)
1963 qint64 CFSDClient::averageOffsetTimeMs(
const CCallsign &callsign,
int &count,
int maxLastValues)
const
1966 if (offsets.
size() < 1) {
return -1; }
1969 for (qint64 v : offsets)
1973 if (count > maxLastValues) {
break; }
1975 return qRound(
static_cast<double>(sum) / count);
1978 bool CFSDClient::isInterimPositionSendingEnabledForServer()
const
1980 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
1981 return (d & CFsdSetup::SendInterimPositions);
1984 bool CFSDClient::isInterimPositionReceivingEnabledForServer()
const
1986 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
1987 return (d & CFsdSetup::ReceiveInterimPositions);
1990 bool CFSDClient::isVisualPositionSendingEnabledForServer()
const
1992 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
1993 return (d & CFsdSetup::SendVisualPositions);
1996 const CFsdSetup &CFSDClient::getSetupForServer()
const {
return m_server.getFsdSetup(); }
2000 Q_ASSERT(m_pendingAtisQueries.contains(sender));
2001 PendingAtisQuery &pendingQuery = m_pendingAtisQueries[sender];
2002 pendingQuery.m_atisMessage.push_back(message);
2009 tm.setCurrentUtcTime();
2010 this->consolidateTextMessage(tm);
2011 m_pendingAtisQueries.remove(sender);
2019 if (reLogoff.match(message).hasMatch())
2021 emit atisLogoffTimeReplyReceived(sender, message);
2023 for (
const auto &line : std::as_const(pendingQuery.m_atisMessage))
2025 if (!atisMessage.isEmpty()) atisMessage.appendMessage(
"\n");
2026 atisMessage.appendMessage(line);
2029 m_pendingAtisQueries.remove(sender);
2034 void CFSDClient::fsdMessageSettingsChanged()
2036 if (m_rawFsdMessageLogFile.isOpen()) { m_rawFsdMessageLogFile.close(); }
2043 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(),
"rawfsdmessages.log");
2044 m_rawFsdMessageLogFile.setFileName(filePath);
2050 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(),
"rawfsdmessages.log");
2051 m_rawFsdMessageLogFile.setFileName(filePath);
2055 else if (setting.
getFileWriteMode() == CRawFsdMessageSettings::Timestamped)
2057 QString filename(
"rawfsdmessages");
2061 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(), filename);
2062 m_rawFsdMessageLogFile.setFileName(filePath);
2070 return m_interimPositionReceivers;
2075 m_interimPositionReceivers = interimPositionReceivers;
2078 bool CFSDClient::isPendingConnection()
const
2080 return m_connectionStatus.isConnecting() || m_connectionStatus.isDisconnecting();
2083 int CFSDClient::increaseStatisticsValue(
const QString &identifier,
const QString &appendix)
2085 if (identifier.
isEmpty() || !m_statistics) {
return -1; }
2088 const QString i = appendix.
isEmpty() ? identifier : identifier % u
"." % appendix;
2089 int &v = m_callStatistics[i];
2092 constexpr
int MaxTimeValues = 50;
2094 if (m_callByTime.size() > MaxTimeValues) { m_callByTime.removeLast(); }
2098 int CFSDClient::increaseStatisticsValue(
const QString &identifier,
int value)
2103 void CFSDClient::clearStatistics()
2106 m_callStatistics.clear();
2107 m_callByTime.clear();
2118 callStatistics = m_callStatistics;
2119 callByTime = m_callByTime;
2122 if (callStatistics.
isEmpty()) {
return {}; }
2123 for (
const auto [key, value] :
makePairsRange(std::as_const(callStatistics)))
2130 std::sort(transformed.
begin(), transformed.
end(), std::greater<>());
2132 for (
const auto &pair : transformed)
2137 for (
const auto &pair : transformed)
2144 const qint64 lastTs = callByTime.
front().
first;
2145 for (
const auto &pair : std::as_const(callByTime))
2147 const qint64 deltaTs = lastTs - pair.first;
2148 stats += separator % QStringLiteral(
"%1").
arg(deltaTs, 5, 10,
QChar(
'0')) % u
": " % pair.second;
2152 if (reset) { this->clearStatistics(); }
2156 void CFSDClient::gracefulShutdown()
2158 disconnectFromServer();
2162 void CFSDClient::readDataFromSocketMaxLines(
int maxLines)
2164 if (m_socket->bytesAvailable() < 1) {
return; }
2169 while (m_socket->canReadLine())
2171 const QByteArray dataEncoded = m_socket->readLine();
2172 if (dataEncoded.
isEmpty()) {
continue; }
2173 const QString data = m_decoder(dataEncoded);
2174 this->parseMessage(data);
2177 static constexpr
int MaxLines = 75 - 1;
2178 if (maxLines < 0) { maxLines = MaxLines; }
2180 if (lines > maxLines)
2182 static constexpr
int DelayMs = 10;
2183 const int newMax = qRound(1.2 * lines);
2185 CLogMessage(
this).
debug(u
"ReadDataFromSocket has too many lines (>%1), will read again in %2ms")
2186 << MaxLines << DelayMs;
2190 if (myself) { myself->readDataFromSocketMaxLines(newMax); }
2199 QString e = CFSDClient::socketErrorToQString(error);
2200 if (!m_socket->errorString().isEmpty()) { e += QStringLiteral(
": ") % m_socket->errorString(); }
2206 static const QMetaEnum metaEnum = QMetaEnum::fromType<QAbstractSocket::SocketError>();
2210 void CFSDClient::parseMessage(
const QString &lineRaw)
2216 if (m_printToConsole) { qDebug() <<
"FSD Recv=>" << line; }
2217 emitRawFsdMessage(line,
false);
2224 messageType = m_messageTypeMapping.value(str, MessageType::Unknown);
2232 increaseStatisticsValue(QStringLiteral(
"parseMessage"), this->messageTypeToString(messageType));
2235 if (messageType != MessageType::Unknown)
2241 if (payload.
length() == 0) {
return; }
2244 switch (messageType)
2247 case MessageType::AddAtc:
2248 case MessageType::AddPilot:
2249 case MessageType::ServerHeartbeat:
2250 case MessageType::ProController:
2251 case MessageType::ClientIdentification:
break;
2254 case MessageType::AtcDataUpdate: handleAtcDataUpdate(tokens);
break;
2255 #ifdef SWIFT_VATSIM_SUPPORT
2256 case MessageType::AuthChallenge: handleAuthChallenge(tokens);
break;
2257 case MessageType::AuthResponse: handleAuthResponse(tokens);
break;
2259 case MessageType::ClientQuery: handleClientQuery(tokens);
break;
2260 case MessageType::ClientResponse: handleClientResponse(tokens);
break;
2261 case MessageType::DeleteATC: handleDeleteATC(tokens);
break;
2262 case MessageType::DeletePilot: handleDeletePilot(tokens);
break;
2263 case MessageType::FlightPlan: handleFlightPlan(tokens);
break;
2264 #ifdef SWIFT_VATSIM_SUPPORT
2265 case MessageType::FsdIdentification: handleFsdIdentification(tokens);
break;
2267 case MessageType::KillRequest: handleKillRequest(tokens);
break;
2268 case MessageType::PilotDataUpdate: handlePilotDataUpdate(tokens);
break;
2269 case MessageType::Ping: handlePing(tokens);
break;
2270 case MessageType::Pong: handlePong(tokens);
break;
2271 case MessageType::ServerError: handleServerError(tokens);
break;
2272 case MessageType::TextMessage: handleTextMessage(tokens);
break;
2273 case MessageType::PilotClientCom: handleCustomPilotPacket(tokens);
break;
2274 case MessageType::VisualPilotDataUpdate:
2275 case MessageType::VisualPilotDataPeriodic:
2276 case MessageType::VisualPilotDataStopped: handleVisualPilotDataUpdate(tokens, messageType);
break;
2277 case MessageType::VisualPilotDataToggle: handleVisualPilotDataToggle(tokens);
break;
2278 case MessageType::EuroscopeSimData: handleEuroscopeSimData(tokens);
break;
2279 case MessageType::Rehost: handleRehost(tokens);
break;
2280 case MessageType::Mute: handleMute(tokens);
break;
2284 case MessageType::Unknown: handleUnknownPacket(tokens);
break;
2287 else { handleUnknownPacket(line); }
2290 void CFSDClient::emitRawFsdMessage(
const QString &fsdMessage,
bool isSent)
2292 if (!m_unitTestMode && !m_rawFsdMessagesEnabled) {
return; }
2293 QString fsdMessageFiltered(fsdMessage);
2294 if (m_filterPasswordFromLogin)
2296 if (fsdMessageFiltered.startsWith(
"#AP"))
2298 thread_local
const QRegularExpression re(R
"(^(#AP\w+:SERVER:\d+:)[^:]+(:\d+:\d+:\d+:.+)$)");
2299 fsdMessageFiltered.replace(re, "\\1<password>\\2");
2300 m_filterPasswordFromLogin =
false;
2304 const QString prefix = isSent ?
"FSD Sent=>" :
"FSD Recv=>";
2306 rawMessage.setCurrentUtcTime();
2307 if (m_rawFsdMessageLogFile.isOpen())
2310 stream << rawMessage.toQString().trimmed() <<
Qt::endl;
2312 emit rawFsdMessage(rawMessage);
2315 bool CFSDClient::saveNetworkStatistics(
const QString &server)
2317 if (m_callStatistics.isEmpty()) {
return false; }
2319 const QString s = this->getNetworkStatisticsAsText(
false,
"\n");
2320 if (s.
isEmpty()) {
return false; }
2321 const QString fn = QStringLiteral(
"networkstatistics_%1_%2.log")
2323 const QString fp = CFileUtils::appendFilePaths(CSwiftDirectories::logDirectory(), fn);
2324 return CFileUtils::writeStringToFile(s, fp);
2327 void CFSDClient::startPositionTimers()
2329 m_positionUpdateTimer.start(c_updatePositionIntervalMsec);
2330 m_scheduledConfigUpdate.start(c_processingIntervalMsec);
2331 m_fsdSendMessageTimer.start(c_sendFsdMsgIntervalMsec);
2332 m_queuedFsdMessages.clear();
2336 if (this->isInterimPositionSendingEnabledForServer())
2338 m_interimPositionUpdateTimer.start(c_updateInterimPositionIntervalMsec);
2340 else { m_interimPositionUpdateTimer.stop(); }
2341 if (this->isVisualPositionSendingEnabledForServer())
2343 m_visualPositionUpdateTimer.start(c_updateVisualPositionIntervalMsec);
2345 else { m_visualPositionUpdateTimer.stop(); }
2349 void CFSDClient::stopPositionTimers()
2351 m_positionUpdateTimer.stop();
2352 m_interimPositionUpdateTimer.stop();
2353 m_visualPositionUpdateTimer.stop();
2354 m_scheduledConfigUpdate.stop();
2355 m_fsdSendMessageTimer.stop();
2358 void CFSDClient::updateAtisMap(
const QString &callsign, AtisLineType type,
const QString &line)
2361 if (type == AtisLineType::VoiceRoom)
2363 m_mapAtisMessages[callsign].m_voiceRoom = line;
2364 m_mapAtisMessages[callsign].m_lineCount++;
2367 if (type == AtisLineType::TextMessage)
2369 m_mapAtisMessages[callsign].m_textLines.
push_back(line);
2370 m_mapAtisMessages[callsign].m_lineCount++;
2373 if (type == AtisLineType::ZuluLogoff)
2375 m_mapAtisMessages[callsign].m_zuluLogoff = line;
2376 m_mapAtisMessages[callsign].m_lineCount++;
2379 if (!m_mapAtisMessages.contains(callsign)) {
return; }
2382 m_mapAtisMessages[callsign].m_lineCount++;
2384 const CCallsign cs(callsign, CCallsign::Atc);
2385 emit atisLogoffTimeReplyReceived(cs, m_mapAtisMessages[callsign].m_zuluLogoff);
2388 for (
const QString &tm : std::as_const(m_mapAtisMessages[callsign].m_textLines))
2392 if (!atisMessage.isEmpty()) atisMessage.appendMessage(
"\n");
2393 atisMessage.appendMessage(fixed);
2396 emit this->atisReplyReceived(cs, atisMessage);
2398 m_mapAtisMessages.remove(callsign);
2402 void CFSDClient::pendingTimeoutCheck()
2404 if (!this->isPendingConnection()) {
return; }
2407 if (age < PendingConnectionTimeoutMs) {
return; }
2410 CLogMessage(
this).
warning(u
"Timeout on pending connection to '%1'") << this->getServer().getName();
2411 this->disconnectFromServer();
2429 static const CLength l_Atis(150.0, CLengthUnit::NM());
2430 return maxOrNotNull(networkRange, l_Atis);
2434 static const CLength l_Gnd(10.0, CLengthUnit::NM());
2435 return maxOrNotNull(networkRange, l_Gnd);
2439 static const CLength l_Twr(25.0, CLengthUnit::NM());
2440 return maxOrNotNull(networkRange, l_Twr);
2444 static const CLength l_Dep(150.0, CLengthUnit::NM());
2445 return maxOrNotNull(networkRange, l_Dep);
2449 static const CLength l_App(150.0, CLengthUnit::NM());
2450 return maxOrNotNull(networkRange, l_App);
2454 static const CLength l_Ctr(300.0, CLengthUnit::NM());
2455 return maxOrNotNull(networkRange, l_Ctr);
2459 static const CLength l_Fss(1500.0, CLengthUnit::NM());
2460 return maxOrNotNull(networkRange, l_Fss);
2463 return networkRange;
2468 if (l1.
isNull()) {
return l2; }
2469 if (l2.
isNull()) {
return l1; }
2470 return (l2 > l1) ? l2 : l1;
2475 if (!input.
contains(
':')) {
return input; }
2477 return copy.remove(
':');
2482 switch (flightRules)
2484 case CFlightPlan::IFR:
return FlightType::IFR;
2485 case CFlightPlan::VFR:
return FlightType::VFR;
2486 case CFlightPlan::SVFR:
return FlightType::SVFR;
2487 case CFlightPlan::DVFR:
return FlightType::DVFR;
2488 default:
return FlightType::IFR;
2495 while (i != m_messageTypeMapping.constEnd())
2497 if (i.
value() == mt) {
return i.
key(); }
2505 void CFSDClient::handleIllegalFsdState(
const QString &message)
2507 if (CBuildConfig::isLocalDeveloperDebugBuild()) {
SWIFT_VERIFY_X(
false, Q_FUNC_INFO,
"Illegal FSD state"); }
2511 const QJsonObject &CFSDClient::JsonPackets::aircraftConfigRequest()
2513 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 FSD server.
const CFsdSetup & getFsdSetup() const
Get FSD setup.
const QString & getAddress() const
Get address.
const CEcosystem & getEcosystem() const
Get the ecosystem.
const CUser & getUser() const
Get user.
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.