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();
1021 disconnectFromServer();
1023 nwReply->deleteLater();
1027 void CFSDClient::sendIncrementalAircraftConfig()
1029 if (!m_unitTestMode && (!this->isConnected() || !this->getSetupForServer().sendAircraftParts())) {
return; }
1033 if (m_sentAircraftConfig == currentParts) {
return; }
1035 if (!m_tokenBucket.tryConsume()) {
return; }
1037 const QJsonObject previousConfig = m_sentAircraftConfig.toJson();
1038 const QJsonObject currentConfig = currentParts.toJson();
1039 const QJsonObject incrementalConfig = getIncrementalObject(previousConfig, currentConfig);
1041 const QString dataStr = convertToUnicodeEscaped(
1044 sendAircraftConfiguration(
"@94836", dataStr);
1045 m_sentAircraftConfig = currentParts;
1048 void CFSDClient::initializeMessageTypes()
1050 m_messageTypeMapping[
"#AA"] = MessageType::AddAtc;
1051 m_messageTypeMapping[
"#AP"] = MessageType::AddPilot;
1052 m_messageTypeMapping[
"%"] = MessageType::AtcDataUpdate;
1053 m_messageTypeMapping[
"$ZC"] = MessageType::AuthChallenge;
1054 m_messageTypeMapping[
"$ZR"] = MessageType::AuthResponse;
1055 m_messageTypeMapping[
"$ID"] = MessageType::ClientIdentification;
1056 m_messageTypeMapping[
"$CQ"] = MessageType::ClientQuery;
1057 m_messageTypeMapping[
"$CR"] = MessageType::ClientResponse;
1058 m_messageTypeMapping[
"#DA"] = MessageType::DeleteATC;
1059 m_messageTypeMapping[
"#DP"] = MessageType::DeletePilot;
1060 m_messageTypeMapping[
"$FP"] = MessageType::FlightPlan;
1061 m_messageTypeMapping[
"#PC"] = MessageType::ProController;
1062 m_messageTypeMapping[
"$DI"] = MessageType::FsdIdentification;
1063 m_messageTypeMapping[
"$!!"] = MessageType::KillRequest;
1064 m_messageTypeMapping[
"@"] = MessageType::PilotDataUpdate;
1065 m_messageTypeMapping[
"^"] = MessageType::VisualPilotDataUpdate;
1066 m_messageTypeMapping[
"#SL"] = MessageType::VisualPilotDataPeriodic;
1067 m_messageTypeMapping[
"#ST"] = MessageType::VisualPilotDataStopped;
1068 m_messageTypeMapping[
"$SF"] = MessageType::VisualPilotDataToggle;
1069 m_messageTypeMapping[
"$PI"] = MessageType::Ping;
1070 m_messageTypeMapping[
"$PO"] = MessageType::Pong;
1071 m_messageTypeMapping[
"$ER"] = MessageType::ServerError;
1072 m_messageTypeMapping[
"#DL"] = MessageType::ServerHeartbeat;
1073 m_messageTypeMapping[
"#TM"] = MessageType::TextMessage;
1074 m_messageTypeMapping[
"#SB"] = MessageType::PilotClientCom;
1075 m_messageTypeMapping[
"$XX"] = MessageType::Rehost;
1076 m_messageTypeMapping[
"#MU"] = MessageType::Mute;
1079 m_messageTypeMapping[
"SIMDATA"] = MessageType::EuroscopeSimData;
1082 void CFSDClient::handleAtcDataUpdate(
const QStringList &tokens)
1084 const AtcDataUpdate atcDataUpdate = AtcDataUpdate::fromTokens(tokens);
1085 const QString senderCs = atcDataUpdate.sender();
1086 const CCallsign cs(senderCs, CCallsign::Atc);
1089 if (atcDataUpdate.m_facility == CFacilityType::Unknown && !cs.isObserverCallsign())
1093 if (atcDataUpdate.m_facility == CFacilityType::OBS && !cs.hasSuffix()) {
return; }
1095 CFrequency freq(atcDataUpdate.m_frequencykHz, CFrequencyUnit::kHz());
1096 freq.switchUnit(CFrequencyUnit::MHz());
1102 const CLength networkRange(atcDataUpdate.m_visibleRange, CLengthUnit::NM());
1103 const CLength range = fixAtcRange(networkRange, cs);
1104 const CCoordinateGeodetic position(atcDataUpdate.m_latitude, atcDataUpdate.m_longitude, 0);
1106 emit this->atcDataUpdateReceived(cs, freq, position, range);
1108 m_atcStations.replaceOrAddObjectByCallsign({ cs, {}, freq, position, range });
1111 #ifdef SWIFT_VATSIM_SUPPORT
1112 void CFSDClient::handleAuthChallenge(
const QStringList &tokens)
1114 const AuthChallenge authChallenge = AuthChallenge::fromTokens(tokens);
1116 vatsim_auth_generate_response(m_clientAuth, qPrintable(authChallenge.m_challengeKey), response);
1117 sendAuthResponse(
QString(response));
1120 vatsim_auth_generate_challenge(m_serverAuth, challenge);
1121 m_lastServerAuthChallenge =
QString(challenge);
1122 sendAuthChallenge(m_lastServerAuthChallenge);
1126 #ifdef SWIFT_VATSIM_SUPPORT
1127 void CFSDClient::handleAuthResponse(
const QStringList &tokens)
1129 const AuthResponse authResponse = AuthResponse::fromTokens(tokens);
1131 char expectedResponse[33];
1132 vatsim_auth_generate_response(m_serverAuth, qPrintable(m_lastServerAuthChallenge), expectedResponse);
1133 if (authResponse.m_response !=
QString(expectedResponse))
1135 CLogMessage().
error(u
"The server you are connected to is not a VATSIM server. Disconnecting!");
1136 disconnectFromServer();
1141 void CFSDClient::handleDeleteATC(
const QStringList &tokens)
1143 const DeleteAtc deleteAtc = DeleteAtc::fromTokens(tokens);
1144 emit deleteAtcReceived(deleteAtc.m_cid);
1146 m_atcStations.removeByCallsign(deleteAtc.m_cid);
1149 void CFSDClient::handleDeletePilot(
const QStringList &tokens)
1151 const DeletePilot deletePilot = DeletePilot::fromTokens(tokens);
1152 const CCallsign cs(deletePilot.sender(), CCallsign::Aircraft);
1154 emit deletePilotReceived(deletePilot.m_cid);
1157 void CFSDClient::handleTextMessage(
const QStringList &tokens)
1159 const TextMessage textMessage = TextMessage::fromTokens(tokens);
1161 const CCallsign sender(textMessage.sender());
1162 const CCallsign receiver(textMessage.receiver());
1164 if (textMessage.m_type == TextMessage::PrivateMessage)
1170 if (m_server.getServerType() != CServer::FSDServerVatsim &&
1171 m_ownCallsign.asString() == textMessage.receiver() && m_pendingAtisQueries.contains(sender))
1173 maybeHandleAtisReply(sender, receiver, textMessage.m_message);
1177 CTextMessage tm(textMessage.m_message, sender, receiver);
1178 tm.setCurrentUtcTime();
1179 this->consolidateTextMessage(tm);
1181 else if (textMessage.m_type == TextMessage::RadioMessage)
1183 const CFrequency com1 = getOwnAircraft().getCom1System().getFrequencyActive();
1184 const CFrequency com2 = getOwnAircraft().getCom2System().getFrequencyActive();
1187 for (
int freqKhz : textMessage.m_frequencies)
1189 CFrequency f(freqKhz, CFrequencyUnit::kHz());
1190 CComSystem::roundToChannelSpacing(f, CComSystem::ChannelSpacing8_33KHz);
1191 if (f == com1 || f == com2) { frequencies.
push_back(f); }
1193 if (frequencies.
isEmpty()) {
return; }
1195 messages.setCurrentUtcTime();
1196 emit textMessagesReceived(messages);
1200 void CFSDClient::handlePilotDataUpdate(
const QStringList &tokens)
1202 const PilotDataUpdate dataUpdate = PilotDataUpdate::fromTokens(tokens);
1203 const CCallsign callsign(dataUpdate.sender(), CCallsign::Aircraft);
1206 callsign,
CCoordinateGeodetic(dataUpdate.m_latitude, dataUpdate.m_longitude, dataUpdate.m_altitudeTrue),
1207 CHeading(dataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1208 CAngle(dataUpdate.m_pitch, CAngleUnit::deg()),
CAngle(dataUpdate.m_bank, CAngleUnit::deg()),
1209 CSpeed(dataUpdate.m_groundSpeed, CSpeedUnit::kts()));
1211 CAltitude::PressureAltitude, CLengthUnit::ft()));
1213 const COnGroundInfo og(dataUpdate.m_onGround ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
1214 COnGroundInfo::NotSetGroundDetails);
1219 const qint64 offsetTimeMs =
1226 if (CTransponder::isValidTransponderCode(dataUpdate.m_transponderCode))
1228 transponder =
CTransponder(dataUpdate.m_transponderCode, dataUpdate.m_transponderMode);
1232 if (CBuildConfig::isLocalDeveloperDebugBuild())
1235 << dataUpdate.m_transponderCode << callsign;
1239 transponder =
CTransponder(2000, CTransponder::StateStandby);
1241 emit pilotDataUpdateReceived(situation, transponder);
1244 void CFSDClient::handleEuroscopeSimData(
const QStringList &tokens)
1250 CHeading(data.m_heading, CAngleUnit::deg()),
1251 CAngle(-data.m_pitch, CAngleUnit::deg()),
CAngle(-data.m_bank, CAngleUnit::deg()),
1252 CSpeed(data.m_groundSpeed, CSpeedUnit::kts()));
1254 const COnGroundInfo og(data.m_onGround ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
1255 COnGroundInfo::NotSetGroundDetails);
1260 const qint64 offsetTimeMs =
1269 emit euroscopeSimDataUpdatedReceived(situation, parts, currentOffsetTime(data.sender()), data.m_model,
1276 VisualPilotDataUpdate dataUpdate;
1277 switch (messageType)
1279 case MessageType::VisualPilotDataUpdate: dataUpdate = VisualPilotDataUpdate::fromTokens(tokens);
break;
1280 case MessageType::VisualPilotDataPeriodic: dataUpdate = VisualPilotDataPeriodic::fromTokens(tokens).toUpdate();
break;
1281 case MessageType::VisualPilotDataStopped: dataUpdate = VisualPilotDataStopped::fromTokens(tokens).toUpdate();
break;
1282 default: qFatal(
"Precondition violated");
break;
1284 const CCallsign callsign(dataUpdate.sender(), CCallsign::Aircraft);
1288 CCoordinateGeodetic(dataUpdate.m_latitude, dataUpdate.m_longitude, dataUpdate.m_altitudeTrue),
1289 CHeading(dataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1290 CAngle(dataUpdate.m_pitch, CAngleUnit::deg()),
1291 CAngle(dataUpdate.m_bank, CAngleUnit::deg()));
1303 emit visualPilotDataUpdateReceived(situation);
1307 void CFSDClient::handleVisualPilotDataToggle(
const QStringList &tokens)
1309 const VisualPilotDataToggle toggle = VisualPilotDataToggle::fromTokens(tokens);
1310 m_serverWantsVisualPositions = toggle.m_active;
1313 void CFSDClient::handlePing(
const QStringList &tokens)
1315 const Ping ping = Ping::fromTokens(tokens);
1316 sendPong(ping.sender(), ping.m_timestamp);
1319 void CFSDClient::handlePong(
const QStringList &tokens)
1321 const Pong pong = Pong::fromTokens(tokens);
1323 const qint64 elapsedTime = msecSinceEpoch - pong.m_timestamp.toLongLong();
1324 emit pongReceived(pong.sender(),
static_cast<double>(elapsedTime));
1327 void CFSDClient::handleKillRequest(
const QStringList &tokens)
1329 KillRequest killRequest = KillRequest::fromTokens(tokens);
1330 emit killRequestReceived(killRequest.m_reason);
1331 disconnectFromServer();
1334 void CFSDClient::handleFlightPlan(
const QStringList &tokens)
1336 FlightPlan fp = FlightPlan::fromTokens(tokens);
1339 switch (fp.m_flightType)
1341 case FlightType::VFR: rules = CFlightPlan::VFR;
break;
1342 case FlightType::IFR: rules = CFlightPlan::IFR;
break;
1343 case FlightType::DVFR: rules = CFlightPlan::DVFR;
break;
1344 case FlightType::SVFR: rules = CFlightPlan::SVFR;
break;
1350 int ca = cruiseAltString.
toInt();
1354 if (rules == CFlightPlan::IFR)
1356 if (ca >= 1000) { cruiseAltString = u
"FL" %
QString::number(ca / 100); }
1357 else { cruiseAltString = u
"FL" % cruiseAltString; }
1361 if (ca >= 5000) { cruiseAltString = u
"FL" %
QString::number(ca / 100); }
1362 else { cruiseAltString = cruiseAltString % u
"ft"; }
1366 cruiseAlt.
parseFromString(cruiseAltString, CPqString::SeparatorBestGuess);
1371 const CCallsign callsign(fp.sender(), CCallsign::Aircraft);
1375 CTime(fp.m_hoursEnroute * 60 + fp.m_minutesEnroute, CTimeUnit::min()),
1376 CTime(fp.m_fuelAvailHours * 60 + fp.m_fuelAvailMinutes, CTimeUnit::min()), cruiseAlt,
1377 CSpeed(fp.m_trueCruisingSpeed, CSpeedUnit::kts()), rules, fp.m_route, fp.m_remarks);
1379 emit flightPlanReceived(callsign, flightPlan);
1382 void CFSDClient::handleClientQuery(
const QStringList &tokens)
1384 const ClientQuery clientQuery = ClientQuery::fromTokens(tokens);
1386 if (clientQuery.m_queryType == ClientQueryType::Unknown) {
return; }
1387 if (clientQuery.m_queryType == ClientQueryType::IsValidATC)
1391 else if (clientQuery.m_queryType == ClientQueryType::Capabilities)
1393 sendClientResponse(ClientQueryType::Capabilities, clientQuery.sender());
1395 else if (clientQuery.m_queryType == ClientQueryType::Com1Freq)
1397 sendClientResponse(ClientQueryType::Com1Freq, clientQuery.sender());
1399 else if (clientQuery.m_queryType == ClientQueryType::RealName)
1401 sendClientResponse(ClientQueryType::RealName, clientQuery.sender());
1403 else if (clientQuery.m_queryType == ClientQueryType::Server)
1405 sendClientResponse(ClientQueryType::Server, clientQuery.sender());
1407 else if (clientQuery.m_queryType == ClientQueryType::ATIS)
1411 else if (clientQuery.m_queryType == ClientQueryType::PublicIP)
1415 else if (clientQuery.m_queryType == ClientQueryType::INF)
1417 sendClientResponse(ClientQueryType::INF, clientQuery.sender());
1419 else if (clientQuery.m_queryType == ClientQueryType::FP)
1423 else if (clientQuery.m_queryType == ClientQueryType::AircraftConfig)
1426 QString aircraftConfigJson = aircraftConfigTokens.
join(
":");
1428 const CCallsign callsign(clientQuery.sender(), CCallsign::Aircraft);
1436 CLogMessage(
this).
warning(u
"Failed to parse aircraft config packet: '%1' packet: '%2'")
1442 if (packet == JsonPackets::aircraftConfigRequest())
1446 QJsonObject config = this->getOwnAircraftParts().toJson();
1447 config.
insert(CAircraftParts::attributeNameIsFullJson(),
true);
1449 data = convertToUnicodeEscaped(data);
1450 sendAircraftConfiguration(clientQuery.sender(), data);
1454 const bool inRange = isAircraftInRange(callsign);
1455 if (!inRange) {
return; }
1456 if (!getSetupForServer().receiveAircraftParts()) {
return; }
1458 if (config.
isEmpty()) {
return; }
1460 const qint64 offsetTimeMs = currentOffsetTime(callsign);
1461 emit aircraftConfigReceived(clientQuery.sender(), config, offsetTimeMs);
1466 void CFSDClient::handleClientResponse(
const QStringList &tokens)
1468 const ClientResponse clientResponse = ClientResponse::fromTokens(tokens);
1469 if (clientResponse.isUnknownQuery()) {
return; }
1470 const QString sender = clientResponse.sender();
1475 if (!clientResponse.m_responseData.empty()) { responseData1 = clientResponse.m_responseData.
at(0); }
1477 if (clientResponse.m_responseData.size() > 1) { responseData2 = clientResponse.m_responseData.
at(1); }
1479 if (clientResponse.m_queryType == ClientQueryType::IsValidATC)
1481 emit validAtcResponseReceived(responseData2, responseData1 == u
"Y");
1483 else if (clientResponse.m_queryType == ClientQueryType::Capabilities)
1486 for (
auto keyValuePair : clientResponse.m_responseData)
1488 if (keyValuePair.count(
'=') != 1) {
continue; }
1491 if (
split.size() < 2) {
continue; }
1495 if (value ==
"1") { capabilities |= fromQString<Capabilities>(key); }
1498 CClient::Capabilities caps = CClient::None;
1499 if (capabilities & Capabilities::AtcInfo) { caps |= CClient::FsdAtisCanBeReceived; }
1500 if (capabilities & Capabilities::FastPos) { caps |= CClient::FsdWithInterimPositions; }
1501 if (capabilities & Capabilities::VisPos) { caps |= CClient::FsdWithVisualPositions; }
1502 if (capabilities & Capabilities::AircraftInfo) { caps |= CClient::FsdWithIcaoCodes; }
1503 if (capabilities & Capabilities::AircraftConfig) { caps |= CClient::FsdWithAircraftConfig; }
1505 emit capabilityResponseReceived(clientResponse.sender(), caps);
1507 else if (clientResponse.m_queryType == ClientQueryType::Com1Freq)
1509 if (responseData1.
isEmpty()) {
return; }
1511 const double freqMHz = responseData1.
toDouble(&ok);
1512 if (!ok) {
return; }
1513 emit com1FrequencyResponseReceived(clientResponse.sender(),
CFrequency(freqMHz, CFrequencyUnit::MHz()));
1515 else if (clientResponse.m_queryType == ClientQueryType::RealName)
1518 emit realNameResponseReceived(clientResponse.sender(), responseData1);
1520 else if (clientResponse.m_queryType == ClientQueryType::Server)
1522 emit serverResponseReceived(clientResponse.sender(), responseData1);
1524 else if (clientResponse.m_queryType == ClientQueryType::ATIS)
1531 updateAtisMap(clientResponse.sender(), fromQString<AtisLineType>(responseData1), responseData2);
1533 else if (clientResponse.m_queryType == ClientQueryType::PublicIP)
1537 else if (clientResponse.m_queryType == ClientQueryType::INF)
1541 else if (clientResponse.m_queryType == ClientQueryType::FP)
1545 else if (clientResponse.m_queryType == ClientQueryType::AircraftConfig)
1552 void CFSDClient::handleServerError(
const QStringList &tokens)
1554 const ServerError serverError = ServerError::fromTokens(tokens);
1555 switch (serverError.m_errorNumber)
1557 case ServerErrorCode::CallsignInUse:
CLogMessage(
this).
error(u
"The requested callsign is already taken");
break;
1558 case ServerErrorCode::InvalidCallsign:
CLogMessage(
this).
error(u
"The requested callsign is not valid");
break;
1559 case ServerErrorCode::InvalidCidPassword:
1562 case ServerErrorCode::InvalidRevision:
1563 CLogMessage(
this).
error(u
"This server does not support our protocol version");
1565 case ServerErrorCode::ServerFull:
CLogMessage(
this).
error(u
"The server is full");
break;
1566 case ServerErrorCode::CidSuspended:
CLogMessage(
this).
error(u
"Your user account is suspended");
break;
1567 case ServerErrorCode::RatingTooLow:
1568 CLogMessage(
this).
error(u
"You are not authorized to use the requested rating");
1570 case ServerErrorCode::InvalidClient:
1571 CLogMessage(
this).
error(u
"This software is not authorized for use on this network");
1573 case ServerErrorCode::RequestedLevelTooHigh:
1574 CLogMessage(
this).
error(u
"You are not authorized to use the requested pilot rating");
1578 case ServerErrorCode::SyntaxError:
1580 u
"Malformed packet, syntax error: '%1'. This can also occur if an OBS sends frequency text messages.")
1581 << serverError.getCausingParameter();
1583 case ServerErrorCode::InvalidSrcCallsign:
1584 CLogMessage(
this).
info(u
"FSD message was using an invalid callsign: %1 (%2)")
1585 << serverError.getCausingParameter() << serverError.getDescription();
1587 case ServerErrorCode::NoSuchCallsign:
1589 << serverError.getCausingParameter() << serverError.getDescription();
1591 case ServerErrorCode::NoFlightPlan:
CLogMessage(
this).
info(u
"FSD Server: no flight plan");
break;
1592 case ServerErrorCode::NoWeatherProfile:
1593 CLogMessage(
this).
info(u
"FSD Server: requested weather profile does not exist");
1597 case ServerErrorCode::AlreadyRegistered:
1598 CLogMessage(
this).
warning(u
"Server says already registered: %1") << serverError.getDescription();
1600 case ServerErrorCode::InvalidCtrl:
1601 CLogMessage(
this).
warning(u
"Server invalid control: %1") << serverError.getDescription();
1603 case ServerErrorCode::Unknown:
1605 << serverError.getCausingParameter() << serverError.getDescription();
1607 case ServerErrorCode::AuthTimeout:
CLogMessage(
this).
warning(u
"Client did not authenticate in time");
break;
1609 if (serverError.isFatalError()) { disconnectFromServer(); }
1612 void CFSDClient::handleRehost(
const QStringList &tokens)
1614 const Rehost rehost = Rehost::fromTokens(tokens);
1616 CLogMessage(
this).
info(u
"Server requested we switch server to %1") << rehost.m_hostname;
1618 SWIFT_AUDIT_X(!m_rehosting, Q_FUNC_INFO,
"Rehosting already in progress");
1621 auto rehostingSocket = std::make_shared<QTcpSocket>();
1623 readDataFromSocket();
1624 CLogMessage(this).debug(u
"Successfully switched server");
1625 m_socket = rehostingSocket;
1626 m_rehosting = false;
1627 rehostingSocket->disconnect(this);
1628 connectSocketSignals();
1629 readDataFromSocket();
1632 CLogMessage(this).warning(u
"Failed to switch server: %1") << rehostingSocket->errorString();
1633 m_rehosting = false;
1634 rehostingSocket->disconnect(this);
1635 if (m_socket->state() != QAbstractSocket::ConnectedState)
1637 updateConnectionStatus(CConnectionStatus::Disconnected);
1641 initiateConnection(rehostingSocket, rehost.m_hostname);
1644 void CFSDClient::handleMute(
const QStringList &tokens)
1646 const Mute mute = Mute::fromTokens(tokens);
1647 if (mute.receiver() != m_ownCallsign.asString()) {
return; }
1648 emit muteRequestReceived(mute.m_mute);
1651 void CFSDClient::initiateConnection(std::shared_ptr<QTcpSocket> rehostingSocket,
const QString &rehostingHost)
1653 const CServer server = this->getServer();
1654 const auto socket = rehostingSocket ? rehostingSocket : m_socket;
1658 const quint16 port = rehostingSocket ? m_socket->peerPort() :
static_cast<quint16
>(getServer().getPort());
1661 resolveLoadBalancing(host, [=](
const QString &host) {
1662 socket->connectToHost(host, port);
1663 if (!rehostingSocket) { this->startPositionTimers(); }
1667 void CFSDClient::resolveLoadBalancing(
const QString &host, std::function<
void(
const QString &)> callback)
1669 if (
QHostAddress(host).isNull() && (getServer().getName() ==
"AUTOMATIC" || m_rehosting) &&
1670 getServer().getEcosystem() == CEcosystem::VATSIM)
1673 Q_ASSERT_X(
sApp, Q_FUNC_INFO,
"Need app");
1683 callback(addr.toString());
1690 else { callback(host); }
1693 void CFSDClient::handleCustomPilotPacket(
const QStringList &tokens)
1697 if (subType == u
"PIR")
1699 PlaneInfoRequest planeInfoRequest = PlaneInfoRequest::fromTokens(tokens);
1702 const QString airlineIcao = m_server.getFsdSetup().force3LetterAirlineCodes() ?
1706 const QString livery = this->getConfiguredLiveryString(myAircraft);
1708 sendPlaneInformation(planeInfoRequest.sender(), acTypeICAO, airlineIcao, livery);
1710 else if (subType ==
"PI")
1712 if (tokens.
size() > 6 && tokens.
at(3) ==
"X")
1716 else if (tokens.
size() > 4 && tokens.
at(3) ==
"GEN")
1718 const PlaneInformation planeInformation = PlaneInformation::fromTokens(tokens);
1719 emit planeInformationReceived(planeInformation.sender(), planeInformation.m_aircraft,
1720 planeInformation.m_airline, planeInformation.m_livery);
1723 else if (subType ==
"I")
1727 else if (subType ==
"VI")
1730 if (!isInterimPositionReceivingEnabledForServer()) {
return; }
1732 const InterimPilotDataUpdate interimPilotDataUpdate = InterimPilotDataUpdate::fromTokens(tokens);
1733 const CCallsign callsign(interimPilotDataUpdate.sender(), CCallsign::Aircraft);
1737 interimPilotDataUpdate.m_longitude,
1738 interimPilotDataUpdate.m_altitudeTrue),
1739 CHeading(interimPilotDataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1740 CAngle(interimPilotDataUpdate.m_pitch, CAngleUnit::deg()),
1741 CAngle(interimPilotDataUpdate.m_bank, CAngleUnit::deg()),
1742 CSpeed(interimPilotDataUpdate.m_groundSpeed, CSpeedUnit::kts()));
1744 const COnGroundInfo og(interimPilotDataUpdate.m_onGround ? COnGroundInfo::OnGround :
1745 COnGroundInfo::NotOnGround,
1746 COnGroundInfo::NotSetGroundDetails);
1751 const qint64 offsetTimeMs =
1755 emit interimPilotDataUpdatedReceived(situation);
1757 else if (subType ==
"FSIPI")
1759 const PlaneInformationFsinn planeInformationFsinn = PlaneInformationFsinn::fromTokens(tokens);
1760 emit planeInformationFsinnReceived(planeInformationFsinn.sender(), planeInformationFsinn.m_airlineIcao,
1761 planeInformationFsinn.m_aircraftIcao,
1762 planeInformationFsinn.m_aircraftIcaoCombinedType,
1763 planeInformationFsinn.m_sendMModelString);
1765 else if (subType ==
"FSIPIR")
1767 const PlaneInfoRequestFsinn planeInfoRequestFsinn = PlaneInfoRequestFsinn::fromTokens(tokens);
1768 sendPlaneInformationFsinn(planeInfoRequestFsinn.sender());
1769 emit planeInformationFsinnReceived(planeInfoRequestFsinn.sender(), planeInfoRequestFsinn.m_airlineIcao,
1770 planeInfoRequestFsinn.m_aircraftIcao,
1771 planeInfoRequestFsinn.m_aircraftIcaoCombinedType,
1772 planeInfoRequestFsinn.m_sendMModelString);
1779 emit customPilotPacketReceived(sender, data);
1783 #ifdef SWIFT_VATSIM_SUPPORT
1784 void CFSDClient::handleFsdIdentification(
const QStringList &tokens)
1788 const FSDIdentification fsdIdentification = FSDIdentification::fromTokens(tokens);
1789 vatsim_auth_set_initial_challenge(m_clientAuth, qPrintable(fsdIdentification.m_initialChallenge));
1791 char fsdChallenge[33];
1792 vatsim_auth_generate_challenge(m_serverAuth, fsdChallenge);
1793 vatsim_auth_set_initial_challenge(m_serverAuth, fsdChallenge);
1794 sendClientIdentification(
QString(fsdChallenge));
1799 u
"You tried to connect to a VATSIM server without using VATSIM protocol, disconnecting!");
1800 disconnectFromServer();
1805 void CFSDClient::handleUnknownPacket(
const QString &line)
1810 void CFSDClient::handleUnknownPacket(
const QStringList &tokens) { this->handleUnknownPacket(tokens.
join(
", ")); }
1814 if (m_rehosting) {
return; }
1816 CLogMessage(
this).
error(u
"FSD socket error: %1") << this->socketErrorString(socketError);
1821 if (m_rehosting) {
return; }
1823 const QString error = this->socketErrorString(socketError);
1824 switch (socketError)
1828 emit this->severeNetworkError(error);
1829 this->disconnectFromServer();
1835 void CFSDClient::handleSocketConnected()
1840 this->updateConnectionStatus(CConnectionStatus::Connected);
1846 if (this->getConnectionStatus() == newStatus) {
return; }
1852 m_server.setConnectedSinceNow();
1853 ecoSystem = m_server.getEcosystem();
1855 this->setCurrentEcosystem(ecoSystem);
1860 m_server.markAsDisconnected();
1865 this->stopPositionTimers();
1867 this->setLastEcosystem(m_server.getEcosystem());
1868 this->setCurrentEcosystem(CEcosystem::NoSystem);
1869 this->saveNetworkStatistics(m_server.getName());
1875 oldStatus = m_connectionStatus;
1876 m_connectionStatus = newStatus;
1879 emit this->connectionStatusChanged(oldStatus, newStatus);
1882 void CFSDClient::consolidateTextMessage(
const CTextMessage &textMessage)
1887 m_textMessagesToConsolidate.addConsolidatedTextMessage(textMessage);
1888 m_dsSendTextMessage.inputSignal();
1892 void CFSDClient::emitConsolidatedTextMessages()
1894 emit this->textMessagesReceived(m_textMessagesToConsolidate);
1895 m_textMessagesToConsolidate.clear();
1898 qint64 CFSDClient::receivedPositionFixTsAndGetOffsetTime(
const CCallsign &callsign, qint64 markerTs)
1900 Q_ASSERT_X(!callsign.
isEmpty(), Q_FUNC_INFO,
"Need callsign");
1903 if (!m_lastPositionUpdate.contains(callsign))
1905 m_lastPositionUpdate.insert(callsign, markerTs);
1906 return CFsdSetup::c_positionTimeOffsetMsec;
1908 const qint64 oldTs = m_lastPositionUpdate.value(callsign);
1909 m_lastPositionUpdate[callsign] = markerTs;
1912 const qint64 diff = qAbs(markerTs - oldTs);
1913 this->insertLatestOffsetTime(callsign, diff);
1916 const qint64 avgTimeMs = this->averageOffsetTimeMs(callsign, count, 3);
1917 qint64 offsetTime = CFsdSetup::c_positionTimeOffsetMsec;
1919 if (avgTimeMs < CFsdSetup::c_interimPositionTimeOffsetMsec && count >= 3)
1921 offsetTime = CFsdSetup::c_interimPositionTimeOffsetMsec;
1924 return m_additionalOffsetTime + offsetTime;
1927 qint64 CFSDClient::currentOffsetTime(
const CCallsign &callsign)
const
1929 Q_ASSERT_X(!callsign.
isEmpty(), Q_FUNC_INFO,
"Need callsign");
1931 if (!m_lastOffsetTimes.contains(callsign) || m_lastOffsetTimes[callsign].isEmpty())
1933 return CFsdSetup::c_positionTimeOffsetMsec;
1935 return m_lastOffsetTimes[callsign].front();
1938 void CFSDClient::clearState()
1940 m_rehosting =
false;
1941 m_stoppedSendingVisualPositions =
false;
1942 m_serverWantsVisualPositions =
false;
1943 m_visualPositionUpdateSentCount = 0;
1944 m_textMessagesToConsolidate.clear();
1945 m_pendingAtisQueries.clear();
1946 m_lastPositionUpdate.clear();
1947 m_lastOffsetTimes.clear();
1948 m_atcStations.clear();
1949 m_queuedFsdMessages.clear();
1950 m_sentAircraftConfig = CAircraftParts::null();
1954 void CFSDClient::clearState(
const CCallsign &callsign)
1956 if (callsign.
isEmpty()) {
return; }
1957 m_pendingAtisQueries.remove(callsign);
1958 m_lastPositionUpdate.remove(callsign);
1959 m_interimPositionReceivers.remove(callsign);
1960 m_lastOffsetTimes.remove(callsign);
1963 void CFSDClient::insertLatestOffsetTime(
const CCallsign &callsign, qint64 offsetMs)
1970 qint64 CFSDClient::averageOffsetTimeMs(
const CCallsign &callsign,
int &count,
int maxLastValues)
const
1973 if (offsets.
size() < 1) {
return -1; }
1976 for (qint64 v : offsets)
1980 if (count > maxLastValues) {
break; }
1982 return qRound(
static_cast<double>(sum) / count);
1985 bool CFSDClient::isInterimPositionSendingEnabledForServer()
const
1987 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
1988 return (d & CFsdSetup::SendInterimPositions);
1991 bool CFSDClient::isInterimPositionReceivingEnabledForServer()
const
1993 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
1994 return (d & CFsdSetup::ReceiveInterimPositions);
1997 bool CFSDClient::isVisualPositionSendingEnabledForServer()
const
1999 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
2000 return (d & CFsdSetup::SendVisualPositions);
2003 const CFsdSetup &CFSDClient::getSetupForServer()
const {
return m_server.getFsdSetup(); }
2007 Q_ASSERT(m_pendingAtisQueries.contains(sender));
2008 PendingAtisQuery &pendingQuery = m_pendingAtisQueries[sender];
2009 pendingQuery.m_atisMessage.push_back(message);
2016 tm.setCurrentUtcTime();
2017 this->consolidateTextMessage(tm);
2018 m_pendingAtisQueries.remove(sender);
2026 if (reLogoff.match(message).hasMatch())
2028 emit atisLogoffTimeReplyReceived(sender, message);
2030 for (
const auto &line : std::as_const(pendingQuery.m_atisMessage))
2032 if (!atisMessage.isEmpty()) atisMessage.appendMessage(
"\n");
2033 atisMessage.appendMessage(line);
2036 m_pendingAtisQueries.remove(sender);
2041 void CFSDClient::fsdMessageSettingsChanged()
2043 if (m_rawFsdMessageLogFile.isOpen()) { m_rawFsdMessageLogFile.close(); }
2050 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(),
"rawfsdmessages.log");
2051 m_rawFsdMessageLogFile.setFileName(filePath);
2057 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(),
"rawfsdmessages.log");
2058 m_rawFsdMessageLogFile.setFileName(filePath);
2062 else if (setting.
getFileWriteMode() == CRawFsdMessageSettings::Timestamped)
2064 QString filename(
"rawfsdmessages");
2068 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(), filename);
2069 m_rawFsdMessageLogFile.setFileName(filePath);
2077 return m_interimPositionReceivers;
2082 m_interimPositionReceivers = interimPositionReceivers;
2085 bool CFSDClient::isPendingConnection()
const
2087 return m_connectionStatus.isConnecting() || m_connectionStatus.isDisconnecting();
2090 int CFSDClient::increaseStatisticsValue(
const QString &identifier,
const QString &appendix)
2092 if (identifier.
isEmpty() || !m_statistics) {
return -1; }
2095 const QString i = appendix.
isEmpty() ? identifier : identifier % u
"." % appendix;
2096 int &v = m_callStatistics[i];
2099 constexpr
int MaxTimeValues = 50;
2101 if (m_callByTime.size() > MaxTimeValues) { m_callByTime.removeLast(); }
2105 int CFSDClient::increaseStatisticsValue(
const QString &identifier,
int value)
2110 void CFSDClient::clearStatistics()
2113 m_callStatistics.clear();
2114 m_callByTime.clear();
2125 callStatistics = m_callStatistics;
2126 callByTime = m_callByTime;
2129 if (callStatistics.
isEmpty()) {
return {}; }
2130 for (
const auto [key, value] :
makePairsRange(std::as_const(callStatistics)))
2137 std::sort(transformed.
begin(), transformed.
end(), std::greater<>());
2139 for (
const auto &pair : transformed)
2144 for (
const auto &pair : transformed)
2151 const qint64 lastTs = callByTime.
front().
first;
2152 for (
const auto &pair : std::as_const(callByTime))
2154 const qint64 deltaTs = lastTs - pair.first;
2155 stats += separator % QStringLiteral(
"%1").
arg(deltaTs, 5, 10,
QChar(
'0')) % u
": " % pair.second;
2159 if (reset) { this->clearStatistics(); }
2163 void CFSDClient::gracefulShutdown()
2165 disconnectFromServer();
2169 void CFSDClient::readDataFromSocketMaxLines(
int maxLines)
2171 if (m_socket->bytesAvailable() < 1) {
return; }
2176 while (m_socket->canReadLine())
2178 const QByteArray dataEncoded = m_socket->readLine();
2179 if (dataEncoded.
isEmpty()) {
continue; }
2180 const QString data = m_decoder(dataEncoded);
2181 this->parseMessage(data);
2184 static constexpr
int MaxLines = 75 - 1;
2185 if (maxLines < 0) { maxLines = MaxLines; }
2187 if (lines > maxLines)
2189 static constexpr
int DelayMs = 10;
2190 const int newMax = qRound(1.2 * lines);
2192 CLogMessage(
this).
debug(u
"ReadDataFromSocket has too many lines (>%1), will read again in %2ms")
2193 << MaxLines << DelayMs;
2197 if (myself) { myself->readDataFromSocketMaxLines(newMax); }
2206 QString e = CFSDClient::socketErrorToQString(error);
2207 if (!m_socket->errorString().isEmpty()) { e += QStringLiteral(
": ") % m_socket->errorString(); }
2213 static const QMetaEnum metaEnum = QMetaEnum::fromType<QAbstractSocket::SocketError>();
2217 void CFSDClient::parseMessage(
const QString &lineRaw)
2223 if (m_printToConsole) { qDebug() <<
"FSD Recv=>" << line; }
2224 emitRawFsdMessage(line,
false);
2231 messageType = m_messageTypeMapping.value(str, MessageType::Unknown);
2239 increaseStatisticsValue(QStringLiteral(
"parseMessage"), this->messageTypeToString(messageType));
2242 if (messageType != MessageType::Unknown)
2248 if (payload.
length() == 0) {
return; }
2251 switch (messageType)
2254 case MessageType::AddAtc:
2255 case MessageType::AddPilot:
2256 case MessageType::ServerHeartbeat:
2257 case MessageType::ProController:
2258 case MessageType::ClientIdentification:
break;
2261 case MessageType::AtcDataUpdate: handleAtcDataUpdate(tokens);
break;
2262 #ifdef SWIFT_VATSIM_SUPPORT
2263 case MessageType::AuthChallenge: handleAuthChallenge(tokens);
break;
2264 case MessageType::AuthResponse: handleAuthResponse(tokens);
break;
2266 case MessageType::ClientQuery: handleClientQuery(tokens);
break;
2267 case MessageType::ClientResponse: handleClientResponse(tokens);
break;
2268 case MessageType::DeleteATC: handleDeleteATC(tokens);
break;
2269 case MessageType::DeletePilot: handleDeletePilot(tokens);
break;
2270 case MessageType::FlightPlan: handleFlightPlan(tokens);
break;
2271 #ifdef SWIFT_VATSIM_SUPPORT
2272 case MessageType::FsdIdentification: handleFsdIdentification(tokens);
break;
2274 case MessageType::KillRequest: handleKillRequest(tokens);
break;
2275 case MessageType::PilotDataUpdate: handlePilotDataUpdate(tokens);
break;
2276 case MessageType::Ping: handlePing(tokens);
break;
2277 case MessageType::Pong: handlePong(tokens);
break;
2278 case MessageType::ServerError: handleServerError(tokens);
break;
2279 case MessageType::TextMessage: handleTextMessage(tokens);
break;
2280 case MessageType::PilotClientCom: handleCustomPilotPacket(tokens);
break;
2281 case MessageType::VisualPilotDataUpdate:
2282 case MessageType::VisualPilotDataPeriodic:
2283 case MessageType::VisualPilotDataStopped: handleVisualPilotDataUpdate(tokens, messageType);
break;
2284 case MessageType::VisualPilotDataToggle: handleVisualPilotDataToggle(tokens);
break;
2285 case MessageType::EuroscopeSimData: handleEuroscopeSimData(tokens);
break;
2286 case MessageType::Rehost: handleRehost(tokens);
break;
2287 case MessageType::Mute: handleMute(tokens);
break;
2291 case MessageType::Unknown: handleUnknownPacket(tokens);
break;
2294 else { handleUnknownPacket(line); }
2297 void CFSDClient::emitRawFsdMessage(
const QString &fsdMessage,
bool isSent)
2299 if (!m_unitTestMode && !m_rawFsdMessagesEnabled) {
return; }
2300 QString fsdMessageFiltered(fsdMessage);
2301 if (m_filterPasswordFromLogin)
2303 if (fsdMessageFiltered.startsWith(
"#AP"))
2305 thread_local
const QRegularExpression re(R
"(^(#AP\w+:SERVER:\d+:)[^:]+(:\d+:\d+:\d+:.+)$)");
2306 fsdMessageFiltered.replace(re, "\\1<password>\\2");
2307 m_filterPasswordFromLogin =
false;
2311 const QString prefix = isSent ?
"FSD Sent=>" :
"FSD Recv=>";
2313 rawMessage.setCurrentUtcTime();
2314 if (m_rawFsdMessageLogFile.isOpen())
2317 stream << rawMessage.toQString().trimmed() <<
Qt::endl;
2319 emit rawFsdMessage(rawMessage);
2322 bool CFSDClient::saveNetworkStatistics(
const QString &server)
2324 if (m_callStatistics.isEmpty()) {
return false; }
2326 const QString s = this->getNetworkStatisticsAsText(
false,
"\n");
2327 if (s.
isEmpty()) {
return false; }
2328 const QString fn = QStringLiteral(
"networkstatistics_%1_%2.log")
2330 const QString fp = CFileUtils::appendFilePaths(CSwiftDirectories::logDirectory(), fn);
2331 return CFileUtils::writeStringToFile(s, fp);
2334 void CFSDClient::startPositionTimers()
2336 m_positionUpdateTimer.start(c_updatePositionIntervalMsec);
2337 m_scheduledConfigUpdate.start(c_processingIntervalMsec);
2338 m_fsdSendMessageTimer.start(c_sendFsdMsgIntervalMsec);
2339 m_queuedFsdMessages.clear();
2343 if (this->isInterimPositionSendingEnabledForServer())
2345 m_interimPositionUpdateTimer.start(c_updateInterimPositionIntervalMsec);
2347 else { m_interimPositionUpdateTimer.stop(); }
2348 if (this->isVisualPositionSendingEnabledForServer())
2350 m_visualPositionUpdateTimer.start(c_updateVisualPositionIntervalMsec);
2352 else { m_visualPositionUpdateTimer.stop(); }
2356 void CFSDClient::stopPositionTimers()
2358 m_positionUpdateTimer.stop();
2359 m_interimPositionUpdateTimer.stop();
2360 m_visualPositionUpdateTimer.stop();
2361 m_scheduledConfigUpdate.stop();
2362 m_fsdSendMessageTimer.stop();
2365 void CFSDClient::updateAtisMap(
const QString &callsign, AtisLineType type,
const QString &line)
2368 if (type == AtisLineType::VoiceRoom)
2370 m_mapAtisMessages[callsign].m_voiceRoom = line;
2371 m_mapAtisMessages[callsign].m_lineCount++;
2374 if (type == AtisLineType::TextMessage)
2376 m_mapAtisMessages[callsign].m_textLines.
push_back(line);
2377 m_mapAtisMessages[callsign].m_lineCount++;
2380 if (type == AtisLineType::ZuluLogoff)
2382 m_mapAtisMessages[callsign].m_zuluLogoff = line;
2383 m_mapAtisMessages[callsign].m_lineCount++;
2386 if (!m_mapAtisMessages.contains(callsign)) {
return; }
2389 m_mapAtisMessages[callsign].m_lineCount++;
2391 const CCallsign cs(callsign, CCallsign::Atc);
2393 emit atisLogoffTimeReplyReceived(cs, m_mapAtisMessages[callsign].m_zuluLogoff);
2396 for (
const QString &tm : std::as_const(m_mapAtisMessages[callsign].m_textLines))
2399 if (!
fixed.isEmpty())
2405 if (test ==
"z")
return;
2407 if (test.
length() == 1)
return;
2410 if (!atisMessage.isEmpty()) atisMessage.appendMessage(
"\n");
2411 atisMessage.appendMessage(fixed);
2415 emit this->atisReplyReceived(cs, atisMessage);
2417 m_mapAtisMessages.remove(callsign);
2421 void CFSDClient::pendingTimeoutCheck()
2423 if (!this->isPendingConnection()) {
return; }
2426 if (age < PendingConnectionTimeoutMs) {
return; }
2429 CLogMessage(
this).
warning(u
"Timeout on pending connection to '%1'") << this->getServer().getName();
2430 this->disconnectFromServer();
2448 static const CLength l_Atis(150.0, CLengthUnit::NM());
2449 return maxOrNotNull(networkRange, l_Atis);
2453 static const CLength l_Gnd(10.0, CLengthUnit::NM());
2454 return maxOrNotNull(networkRange, l_Gnd);
2458 static const CLength l_Twr(25.0, CLengthUnit::NM());
2459 return maxOrNotNull(networkRange, l_Twr);
2463 static const CLength l_Dep(150.0, CLengthUnit::NM());
2464 return maxOrNotNull(networkRange, l_Dep);
2468 static const CLength l_App(150.0, CLengthUnit::NM());
2469 return maxOrNotNull(networkRange, l_App);
2473 static const CLength l_Ctr(300.0, CLengthUnit::NM());
2474 return maxOrNotNull(networkRange, l_Ctr);
2478 static const CLength l_Fss(1500.0, CLengthUnit::NM());
2479 return maxOrNotNull(networkRange, l_Fss);
2482 return networkRange;
2487 if (l1.
isNull()) {
return l2; }
2488 if (l2.
isNull()) {
return l1; }
2489 return (l2 > l1) ? l2 : l1;
2494 if (!input.
contains(
':')) {
return input; }
2496 return copy.remove(
':');
2501 switch (flightRules)
2503 case CFlightPlan::IFR:
return FlightType::IFR;
2504 case CFlightPlan::VFR:
return FlightType::VFR;
2505 case CFlightPlan::SVFR:
return FlightType::SVFR;
2506 case CFlightPlan::DVFR:
return FlightType::DVFR;
2507 default:
return FlightType::IFR;
2514 while (i != m_messageTypeMapping.constEnd())
2516 if (i.
value() == mt) {
return i.
key(); }
2524 void CFSDClient::handleIllegalFsdState(
const QString &message)
2526 if (CBuildConfig::isLocalDeveloperDebugBuild()) {
SWIFT_VERIFY_X(
false, Q_FUNC_INFO,
"Illegal FSD state"); }
2530 const QJsonObject &CFSDClient::JsonPackets::aircraftConfigRequest()
2532 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(const Key &key, const 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.