6 #include <QHostAddress>
8 #include <QNetworkReply>
53 using namespace swift::config;
54 using namespace swift::core::vatsim;
56 using namespace swift::misc::aviation;
57 using namespace swift::misc::geo;
58 using namespace swift::misc::json;
59 using namespace swift::misc::network;
60 using namespace swift::misc::physical_quantities;
61 using namespace swift::misc::simulation;
63 namespace swift::core::fsd
68 for (
const auto &ch : str)
70 const ushort code = ch.
unicode();
71 if (code < 0x80) { escaped += ch; }
80 QStringList cl = CContinuousWorker::getLogCategories();
96 initializeMessageTypes();
97 connectSocketSignals();
114 fsdMessageSettingsChanged();
124 void CFSDClient::connectSocketSignals()
132 #ifdef SWIFT_VATSIM_SUPPORT
133 void CFSDClient::setClientIdAndKey(quint16
id,
const QByteArray &key)
136 m_clientAuth = vatsim_auth_create(
id, qPrintable(key));
137 m_serverAuth = vatsim_auth_create(
id, qPrintable(key));
144 "Can't change server details while still connected");
149 const int protocolRev = (server.
getServerType() == CServer::FSDServerVatsim) ?
155 m_protocolRevision = protocolRev;
163 "Can't change callsign while still connected");
167 m_ownCallsign = callsign;
173 "Can't change ICAO codes while still connected");
190 bool sendModelString)
193 m_ownLivery = livery;
194 m_ownModelString = modelString;
195 m_sendLiveryString = sendLiveryString;
196 m_sendModelString = sendModelString;
206 case CSimulatorInfo::FSX: m_simType = SimType::MSFSX;
break;
207 case CSimulatorInfo::P3D: m_simType = SimType::P3Dv4;
break;
208 case CSimulatorInfo::FS9: m_simType = SimType::MSFS2004;
break;
209 case CSimulatorInfo::FG: m_simType = SimType::FlightGear;
break;
210 case CSimulatorInfo::XPLANE: m_simType = SimType::XPLANE11;
break;
211 case CSimulatorInfo::MSFS: m_simType = SimType::MSFS;
break;
212 case CSimulatorInfo::MSFS2024: m_simType = SimType::MSFS2024;
break;
213 default: m_simType = SimType::Unknown;
break;
240 if (m_socket->isOpen()) {
return; }
241 Q_ASSERT(!m_clientName.
isEmpty());
242 Q_ASSERT((m_versionMajor + m_versionMinor) > 0);
243 Q_ASSERT(m_capabilities != Capabilities::None);
248 m_filterPasswordFromLogin =
true;
251 const qint64 timerMs = qRound(PendingConnectionTimeoutMs * 1.25);
256 this->pendingTimeoutCheck();
259 this->updateConnectionStatus(CConnectionStatus::Connecting);
261 initiateConnection();
274 this->stopPositionTimers();
275 this->updateConnectionStatus(CConnectionStatus::Disconnecting);
279 if (m_socket->isOpen())
281 if (mode.
isPilot()) { this->sendDeletePilot(); }
282 else if (mode.
isObserver()) { this->sendDeleteAtc(); }
286 this->updateConnectionStatus(CConnectionStatus::Disconnected);
290 void CFSDClient::sendLogin(
const QString &token)
301 const AddPilot pilotLogin(callsign, cid, password, m_pilotRating, m_protocolRevision, m_simType, name);
302 sendQueuedMessage(pilotLogin);
304 << callsign << cid << toQString(m_pilotRating) << m_protocolRevision << toQString(m_simType) << name;
308 const AddAtc addAtc(callsign, name, cid, password, m_atcRating, m_protocolRevision);
309 sendQueuedMessage(addAtc);
311 << callsign << cid << toQString(m_atcRating) << m_protocolRevision << name;
316 this->sendClientQuery(ClientQueryType::EuroscopeSimData, {}, {});
320 void CFSDClient::sendDeletePilot()
324 sendQueuedMessage(deletePilot);
327 void CFSDClient::sendDeleteAtc()
330 const DeleteAtc deleteAtc(getOwnCallsignAsString(), cid);
331 sendQueuedMessage(deleteAtc);
334 void CFSDClient::sendPilotDataUpdate()
338 if (m_loginMode == CLoginMode::Observer)
340 sendAtcDataUpdate(myAircraft.latitude().value(CAngleUnit::deg()),
341 myAircraft.longitude().value(CAngleUnit::deg()));
345 if (this->isVisualPositionSendingEnabledForServer())
350 sendVisualPilotDataUpdate(
true);
354 PilotDataUpdate pilotDataUpdate(
355 myAircraft.getTransponderMode(), getOwnCallsignAsString(),
356 static_cast<qint16
>(myAircraft.getTransponderCode()), r, myAircraft.latitude().value(CAngleUnit::deg()),
357 myAircraft.longitude().value(CAngleUnit::deg()),
358 myAircraft.getAltitude().valueInteger(CLengthUnit::ft()),
359 myAircraft.getPressureAltitude().valueInteger(CLengthUnit::ft()),
360 myAircraft.getGroundSpeed().valueInteger(CSpeedUnit::kts()),
361 myAircraft.getPitch().value(CAngleUnit::deg()), myAircraft.getBank().value(CAngleUnit::deg()),
362 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
363 myAircraft.getParts().isOnGround());
364 sendQueuedMessage(pilotDataUpdate);
368 void CFSDClient::sendInterimPilotDataUpdate()
372 InterimPilotDataUpdate interimPilotDataUpdate(
373 getOwnCallsignAsString(),
QString(), myAircraft.latitude().value(CAngleUnit::deg()),
374 myAircraft.longitude().value(CAngleUnit::deg()), myAircraft.getAltitude().valueInteger(CLengthUnit::ft()),
375 myAircraft.getGroundSpeed().valueInteger(CSpeedUnit::kts()), myAircraft.getPitch().value(CAngleUnit::deg()),
376 myAircraft.getBank().value(CAngleUnit::deg()),
377 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
378 myAircraft.getParts().isOnGround());
380 for (
const auto &receiver : std::as_const(m_interimPositionReceivers))
382 interimPilotDataUpdate.setReceiver(receiver.asString());
383 sendQueuedMessage(interimPilotDataUpdate);
388 void CFSDClient::sendVisualPilotDataUpdate(
bool slowUpdate)
391 if (m_loginMode == CLoginMode::Observer || !isVisualPositionSendingEnabledForServer()) {
return; }
396 static constexpr
double minVelocity = 0.00005;
397 if (std::abs(myAircraft.getVelocity().getVelocityX(CSpeedUnit::m_s())) < minVelocity &&
398 std::abs(myAircraft.getVelocity().getVelocityY(CSpeedUnit::m_s())) < minVelocity &&
399 std::abs(myAircraft.getVelocity().getVelocityZ(CSpeedUnit::m_s())) < minVelocity &&
400 std::abs(myAircraft.getVelocity().getPitchVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity &&
401 std::abs(myAircraft.getVelocity().getRollVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity &&
402 std::abs(myAircraft.getVelocity().getHeadingVelocity(CAngleUnit::rad(), CTimeUnit::s())) < minVelocity)
404 if (m_stoppedSendingVisualPositions) {
return; }
405 m_stoppedSendingVisualPositions =
true;
406 m_visualPositionUpdateSentCount = 0;
408 else { m_stoppedSendingVisualPositions =
false; }
410 if (!m_serverWantsVisualPositions) {
return; }
412 VisualPilotDataUpdate visualPilotDataUpdate(
413 getOwnCallsignAsString(), myAircraft.latitude().value(CAngleUnit::deg()),
414 myAircraft.longitude().value(CAngleUnit::deg()), myAircraft.getAltitude().value(CLengthUnit::ft()),
415 myAircraft.getAltitude().value(CLengthUnit::ft()) -
416 myAircraft.getGroundElevation().value(CLengthUnit::ft()),
417 myAircraft.getPitch().value(CAngleUnit::deg()), myAircraft.getBank().value(CAngleUnit::deg()),
418 myAircraft.getHeading().normalizedTo360Degrees().value(CAngleUnit::deg()),
419 myAircraft.getVelocity().getVelocityX(CSpeedUnit::m_s()),
420 myAircraft.getVelocity().getVelocityY(CSpeedUnit::m_s()),
421 myAircraft.getVelocity().getVelocityZ(CSpeedUnit::m_s()),
422 myAircraft.getVelocity().getPitchVelocity(CAngleUnit::rad(), CTimeUnit::s()),
423 myAircraft.getVelocity().getRollVelocity(CAngleUnit::rad(), CTimeUnit::s()),
424 myAircraft.getVelocity().getHeadingVelocity(CAngleUnit::rad(), CTimeUnit::s()));
426 if (m_stoppedSendingVisualPositions) { sendQueuedMessage(visualPilotDataUpdate.toStopped()); }
427 else if (m_visualPositionUpdateSentCount++ % 25 == 0) { sendQueuedMessage(visualPilotDataUpdate.toPeriodic()); }
428 else { sendQueuedMessage(visualPilotDataUpdate); }
431 void CFSDClient::sendAtcDataUpdate(
double latitude,
double longitude)
433 const AtcDataUpdate atcDataUpdate(getOwnCallsignAsString(), 199998, CFacilityType::OBS, 300,
434 AtcRating::Observer, latitude, longitude, 0);
435 sendQueuedMessage(atcDataUpdate);
438 void CFSDClient::sendPing(
const QString &receiver)
443 const Ping ping(getOwnCallsignAsString(), receiver, timeString);
444 sendQueuedMessage(ping);
447 increaseStatisticsValue(QStringLiteral(
"sendPing"));
450 void CFSDClient::sendClientQueryIsValidAtc(
const CCallsign &callsign)
452 sendClientQuery(ClientQueryType::IsValidATC, {}, { callsign.
asString() });
455 void CFSDClient::sendClientQueryCapabilities(
const CCallsign &callsign)
457 sendClientQuery(ClientQueryType::Capabilities, callsign);
462 sendClientQuery(ClientQueryType::Com1Freq, callsign);
467 sendClientQuery(ClientQueryType::RealName, callsign);
472 sendClientQuery(ClientQueryType::Server, callsign);
477 sendClientQuery(ClientQueryType::ATIS, callsign);
482 sendClientQuery(ClientQueryType::FP, {}, { callsign.
toQString() });
488 data = convertToUnicodeEscaped(data);
489 sendClientQuery(ClientQueryType::AircraftConfig, callsign, { data });
494 if (queryType == ClientQueryType::Unknown) {
return; }
504 if (queryType == ClientQueryType::IsValidATC)
506 const ClientQuery clientQuery(getOwnCallsignAsString(),
"SERVER", ClientQueryType::IsValidATC, queryData);
507 sendQueuedMessage(clientQuery);
509 else if (queryType == ClientQueryType::Capabilities)
511 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Capabilities);
512 sendQueuedMessage(clientQuery);
514 else if (queryType == ClientQueryType::Com1Freq)
516 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Com1Freq);
517 sendQueuedMessage(clientQuery);
519 else if (queryType == ClientQueryType::RealName)
521 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::RealName);
522 sendQueuedMessage(clientQuery);
524 else if (queryType == ClientQueryType::Server)
526 ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::Server);
527 sendQueuedMessage(clientQuery);
529 else if (queryType == ClientQueryType::ATIS)
531 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::ATIS);
532 sendQueuedMessage(clientQuery);
533 if (m_serverType != ServerType::Vatsim) { m_pendingAtisQueries.
insert(receiver, {}); }
535 else if (queryType == ClientQueryType::PublicIP)
537 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::PublicIP);
538 sendQueuedMessage(clientQuery);
540 else if (queryType == ClientQueryType::INF)
542 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::INF);
543 sendQueuedMessage(clientQuery);
545 else if (queryType == ClientQueryType::FP)
547 if (queryData.
isEmpty()) {
return; }
548 const ClientQuery clientQuery(getOwnCallsignAsString(),
"SERVER", ClientQueryType::FP, queryData);
549 sendQueuedMessage(clientQuery);
551 else if (queryType == ClientQueryType::AircraftConfig)
553 if (queryData.
isEmpty()) {
return; }
554 const ClientQuery clientQuery(getOwnCallsignAsString(), receiverCallsign, ClientQueryType::AircraftConfig,
556 sendQueuedMessage(clientQuery);
558 else if (queryType == ClientQueryType::EuroscopeSimData)
560 const ClientQuery clientQuery(getOwnCallsignAsString(),
"@94835", ClientQueryType::EuroscopeSimData,
562 sendQueuedMessage(clientQuery);
565 increaseStatisticsValue(QStringLiteral(
"sendClientQuery"), toQString(queryType));
570 if (messages.
isEmpty()) {
return; }
580 const QString ownCallsign = getOwnCallsignAsString();
582 for (
const auto &message : privateMessages)
584 if (message.getRecipientCallsign().isEmpty()) {
continue; }
585 const TextMessage textMessage(ownCallsign, message.getRecipientCallsign().getFsdCallsignString(),
586 message.getMessage());
587 sendQueuedMessage(textMessage);
588 increaseStatisticsValue(QStringLiteral(
"sendTextMessages.PM"));
594 for (
const auto &message : radioMessages)
599 message.getFrequency() :
607 frequencies.push_back(freqkHz);
609 increaseStatisticsValue(QStringLiteral(
"sendTextMessages.FREQ"));
616 if (message.
isEmpty()) {
return; }
622 if (message.
isEmpty()) {
return; }
632 if (receiverGroup == TextMessageGroups::AllClients) { receiver =
'*'; }
633 else if (receiverGroup == TextMessageGroups::AllAtcClients) { receiver = QStringLiteral(
"*A"); }
634 else if (receiverGroup == TextMessageGroups::AllPilotClients) { receiver = QStringLiteral(
"*P"); }
635 else if (receiverGroup == TextMessageGroups::AllSups) { receiver = QStringLiteral(
"*S"); }
637 const TextMessage textMessage(getOwnCallsignAsString(), receiver, message);
638 sendQueuedMessage(textMessage);
639 if (receiver == QStringLiteral(
"*S"))
647 increaseStatisticsValue(QStringLiteral(
"sendTextMessages"));
659 for (
const int &frequency : frequencieskHz)
661 receivers.push_back(QStringLiteral(
"@%1").arg(frequency - 100000));
665 sendQueuedMessage(radioMessage);
666 increaseStatisticsValue(QStringLiteral(
"sendTextMessages"));
701 Q_ASSERT_X(!act.
isEmpty(), Q_FUNC_INFO,
"Aircraft type must not be empty");
705 const FlightPlan fp(getOwnCallsignAsString(),
"SERVER", flightType, act,
711 timePartsEnroute[CTime::Minutes], timePartsFuel[CTime::Hours],
715 sendQueuedMessage(fp);
716 increaseStatisticsValue(QStringLiteral(
"sendFlightPlan"));
730 sendQueuedMessage(planeInfoRequest);
731 increaseStatisticsValue(QStringLiteral(
"sendPlaneInfoRequest"));
745 SWIFT_VERIFY_X(connected, Q_FUNC_INFO,
"Can't send to server when disconnected");
746 if (!connected) {
return; }
749 const QString modelString = this->getConfiguredModelString(myAircraft);
753 sendQueuedMessage(planeInfoRequestFsinn);
754 increaseStatisticsValue(QStringLiteral(
"sendPlaneInfoRequestFsinn"));
757 void CFSDClient::sendPlaneInformation(
const QString &receiver,
const QString &aircraft,
const QString &airline,
760 const PlaneInformation planeInformation(getOwnCallsignAsString(), receiver, aircraft, airline, livery);
761 sendQueuedMessage(planeInformation);
762 increaseStatisticsValue(QStringLiteral(
"sendPlaneInformation"));
765 void CFSDClient::sendPlaneInformationFsinn(
const CCallsign &callsign)
769 const QString modelString = this->getConfiguredModelString(myAircraft);
770 const PlaneInformationFsinn planeInformationFsinn(
771 getOwnCallsignAsString(), callsign.
toQString(), myAircraft.getAirlineIcaoCodeDesignator(),
772 myAircraft.getAircraftIcaoCodeDesignator(), myAircraft.getAircraftIcaoCombinedType(), modelString);
773 sendQueuedMessage(planeInformationFsinn);
774 increaseStatisticsValue(QStringLiteral(
"sendPlaneInformationFsinn"));
777 void CFSDClient::sendAircraftConfiguration(
const QString &receiver,
const QString &aircraftConfigJson)
779 if (aircraftConfigJson.
size() == 0) {
return; }
780 const ClientQuery clientQuery(getOwnCallsignAsString(), receiver, ClientQueryType::AircraftConfig,
781 { aircraftConfigJson });
782 sendQueuedMessage(clientQuery);
785 void CFSDClient::sendMessageString(
const QString &message)
787 if (message.
isEmpty()) {
return; }
788 const QByteArray bufferEncoded = m_encoder(message);
789 if (m_printToConsole) { qDebug() <<
"FSD Sent=>" << bufferEncoded; }
790 if (!m_unitTestMode) { m_socket->write(bufferEncoded); }
793 emitRawFsdMessage(message.
trimmed(),
true);
796 void CFSDClient::sendQueuedMessage()
798 if (m_queuedFsdMessages.isEmpty()) {
return; }
799 const qsizetype s = m_queuedFsdMessages.size();
800 this->sendMessageString(m_queuedFsdMessages.
dequeue());
803 if (s > 5) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
804 if (s > 10) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
805 if (s > 20) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
806 if (s > 30) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
812 const StatusSeverity severity = s > 75 ? SeverityWarning : SeverityInfo;
813 CLogMessage(
this).
log(severity, u
"Too many queued messages (%1), bulk send!") << s;
815 if (s > 75) { sendNo = 20; }
816 if (s > 100) { sendNo = 30; }
818 for (
int i = 0; i < sendNo; i++) { this->sendMessageString(m_queuedFsdMessages.
dequeue()); }
822 void CFSDClient::sendFsdMessage(
const QString &message)
825 parseMessage(message);
830 if (!m_sendModelString) {
return noModelString(); }
833 return ms.
isEmpty() ? noModelString() : ms;
838 if (!m_sendLiveryString) {
return {}; }
844 void CFSDClient::sendAuthChallenge(
const QString &challenge)
846 const AuthChallenge pduAuthChallenge(getOwnCallsignAsString(),
"SERVER", challenge);
847 sendDirectMessage(pduAuthChallenge);
848 increaseStatisticsValue(QStringLiteral(
"sendAuthChallenge"));
851 void CFSDClient::sendAuthResponse(
const QString &response)
853 const AuthResponse pduAuthResponse(getOwnCallsignAsString(),
"SERVER", response);
854 sendDirectMessage(pduAuthResponse);
855 increaseStatisticsValue(QStringLiteral(
"sendAuthResponse"));
858 void CFSDClient::sendPong(
const QString &receiver,
const QString ×tamp)
860 const Pong pong(getOwnCallsignAsString(), receiver, timestamp);
861 sendQueuedMessage(pong);
862 increaseStatisticsValue(QStringLiteral(
"sendPong"));
865 void CFSDClient::sendClientResponse(ClientQueryType queryType,
const QString &receiver)
867 if (queryType == ClientQueryType::Unknown) {
return; }
868 if (queryType == ClientQueryType::IsValidATC)
870 this->handleIllegalFsdState(
"Never use sendClientResponse with IsValidATC from the client");
874 increaseStatisticsValue(QStringLiteral(
"sendClientResponse"), toQString(queryType));
877 const QString ownCallsign = getOwnCallsignAsString();
879 if (queryType == ClientQueryType::Capabilities)
881 responseData.
clear();
882 if (m_capabilities & Capabilities::AtcInfo) responseData.
push_back(toQString(Capabilities::AtcInfo) %
"=1");
883 if (m_capabilities & Capabilities::SecondaryPos)
884 responseData.
push_back(toQString(Capabilities::SecondaryPos) %
"=1");
885 if (m_capabilities & Capabilities::AircraftInfo)
886 responseData.
push_back(toQString(Capabilities::AircraftInfo) %
"=1");
887 if (m_capabilities & Capabilities::OngoingCoord)
888 responseData.
push_back(toQString(Capabilities::OngoingCoord) %
"=1");
889 if (m_capabilities & Capabilities::InterminPos)
890 responseData.
push_back(toQString(Capabilities::InterminPos) %
"=1");
891 if (m_capabilities & Capabilities::FastPos) responseData.
push_back(toQString(Capabilities::FastPos) %
"=1");
892 if (m_capabilities & Capabilities::VisPos) responseData.
push_back(toQString(Capabilities::VisPos) %
"=1");
893 if (m_capabilities & Capabilities::Stealth) responseData.
push_back(toQString(Capabilities::Stealth) %
"=1");
894 if (m_capabilities & Capabilities::AircraftConfig)
895 responseData.
push_back(toQString(Capabilities::AircraftConfig) %
"=1");
896 if (m_capabilities & Capabilities::IcaoEquipment)
897 responseData.
push_back(toQString(Capabilities::IcaoEquipment) %
"=1");
898 const ClientResponse clientResponse(ownCallsign, receiver, ClientQueryType::Capabilities, responseData);
899 sendQueuedMessage(clientResponse);
901 else if (queryType == ClientQueryType::Com1Freq)
904 getOwnAircraft().getCom1System().getFrequencyActive().value(CFrequencyUnit::MHz()),
'f', 3);
906 const ClientResponse pduClientResponse(ownCallsign, receiver, ClientQueryType::Com1Freq, responseData);
907 sendQueuedMessage(pduClientResponse);
909 else if (queryType == ClientQueryType::RealName)
918 else { responseData.
push_back(toQString(m_pilotRating)); }
920 const ClientResponse pduClientQueryResponse(ownCallsign, receiver, ClientQueryType::RealName, responseData);
921 sendQueuedMessage(pduClientQueryResponse);
923 else if (queryType == ClientQueryType::Server)
926 const ClientResponse pduClientQueryResponse(ownCallsign, receiver, ClientQueryType::Server, responseData);
927 sendQueuedMessage(pduClientQueryResponse);
929 else if (queryType == ClientQueryType::ATIS)
931 this->handleIllegalFsdState(
932 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::ATIS)));
934 else if (queryType == ClientQueryType::PublicIP)
936 this->handleIllegalFsdState(
937 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::PublicIP)));
939 else if (queryType == ClientQueryType::INF)
945 const double latitude = situation.
latitude().
value(CAngleUnit::deg());
946 const double longitude = situation.
longitude().
value(CAngleUnit::deg());
949 std::array<char, 50> sysuid = {};
950 #ifdef SWIFT_VATSIM_SUPPORT
951 vatsim_get_system_unique_id(sysuid.data());
954 const QString userInfo = QStringLiteral(
"CID=") % cid %
" " % m_clientName %
" IP=" %
955 m_socket->localAddress().toString() %
" SYS_UID=" % sysuid.data() %
" FSVER=" %
959 const TextMessage textMessage(ownCallsign, receiver, userInfo);
960 sendQueuedMessage(textMessage);
962 else if (queryType == ClientQueryType::FP)
964 this->handleIllegalFsdState(
965 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::FP)));
967 else if (queryType == ClientQueryType::AircraftConfig)
969 this->handleIllegalFsdState(
970 QStringLiteral(
"Dont send '%1' as pilot client!").arg(toQString(ClientQueryType::AircraftConfig)));
974 #ifdef SWIFT_VATSIM_SUPPORT
975 void CFSDClient::sendClientIdentification(
const QString &fsdChallenge)
977 std::array<char, 50> sysuid = {};
978 vatsim_get_system_unique_id(sysuid.data());
980 const ClientIdentification clientIdentification(
981 getOwnCallsignAsString(), vatsim_auth_get_client_id(m_clientAuth), m_clientName, m_versionMajor,
982 m_versionMinor, cid, sysuid.
data(), fsdChallenge);
983 this->sendQueuedMessage(clientIdentification);
985 if (
getServer().getEcosystem().isSystem(CEcosystem::VATSIM))
988 { this, [this](const QString &token) {
989 this->sendLogin(token);
990 this->updateConnectionStatus(CConnectionStatus::Connected);
996 this->updateConnectionStatus(CConnectionStatus::Connected);
998 increaseStatisticsValue(QStringLiteral(
"sendClientIdentification"));
1002 void CFSDClient::getVatsimAuthToken(
const QString &cid,
const QString &password,
1005 Q_ASSERT_X(
sApp, Q_FUNC_INFO,
"Need app");
1008 const QJsonObject jsonRequest { {
"cid", cid }, {
"password", password } };
1020 nwReply->errorString();
1022 disconnectFromServer();
1024 nwReply->deleteLater();
1028 void CFSDClient::sendIncrementalAircraftConfig()
1030 if (!m_unitTestMode && (!this->isConnected() || !this->getSetupForServer().sendAircraftParts())) {
return; }
1034 if (m_sentAircraftConfig == currentParts) {
return; }
1036 if (!m_tokenBucket.tryConsume()) {
return; }
1038 const QJsonObject previousConfig = m_sentAircraftConfig.toJson();
1039 const QJsonObject currentConfig = currentParts.toJson();
1040 const QJsonObject incrementalConfig = getIncrementalObject(previousConfig, currentConfig);
1042 const QString dataStr = convertToUnicodeEscaped(
1045 sendAircraftConfiguration(
"@94836", dataStr);
1046 m_sentAircraftConfig = currentParts;
1049 void CFSDClient::initializeMessageTypes()
1051 m_messageTypeMapping[
"#AA"] = MessageType::AddAtc;
1052 m_messageTypeMapping[
"#AP"] = MessageType::AddPilot;
1053 m_messageTypeMapping[
"%"] = MessageType::AtcDataUpdate;
1054 m_messageTypeMapping[
"$ZC"] = MessageType::AuthChallenge;
1055 m_messageTypeMapping[
"$ZR"] = MessageType::AuthResponse;
1056 m_messageTypeMapping[
"$ID"] = MessageType::ClientIdentification;
1057 m_messageTypeMapping[
"$CQ"] = MessageType::ClientQuery;
1058 m_messageTypeMapping[
"$CR"] = MessageType::ClientResponse;
1059 m_messageTypeMapping[
"#DA"] = MessageType::DeleteATC;
1060 m_messageTypeMapping[
"#DP"] = MessageType::DeletePilot;
1061 m_messageTypeMapping[
"$FP"] = MessageType::FlightPlan;
1062 m_messageTypeMapping[
"#PC"] = MessageType::ProController;
1063 m_messageTypeMapping[
"$DI"] = MessageType::FsdIdentification;
1064 m_messageTypeMapping[
"$!!"] = MessageType::KillRequest;
1065 m_messageTypeMapping[
"@"] = MessageType::PilotDataUpdate;
1066 m_messageTypeMapping[
"^"] = MessageType::VisualPilotDataUpdate;
1067 m_messageTypeMapping[
"#SL"] = MessageType::VisualPilotDataPeriodic;
1068 m_messageTypeMapping[
"#ST"] = MessageType::VisualPilotDataStopped;
1069 m_messageTypeMapping[
"$SF"] = MessageType::VisualPilotDataToggle;
1070 m_messageTypeMapping[
"$PI"] = MessageType::Ping;
1071 m_messageTypeMapping[
"$PO"] = MessageType::Pong;
1072 m_messageTypeMapping[
"$ER"] = MessageType::ServerError;
1073 m_messageTypeMapping[
"#DL"] = MessageType::ServerHeartbeat;
1074 m_messageTypeMapping[
"#TM"] = MessageType::TextMessage;
1075 m_messageTypeMapping[
"#SB"] = MessageType::PilotClientCom;
1076 m_messageTypeMapping[
"$XX"] = MessageType::Rehost;
1077 m_messageTypeMapping[
"#MU"] = MessageType::Mute;
1080 m_messageTypeMapping[
"SIMDATA"] = MessageType::EuroscopeSimData;
1084 m_messageTypeMapping[
"!R"] = MessageType::RegistrationInfo;
1085 m_messageTypeMapping[
"-MD"] = MessageType::RevBClientParts;
1086 m_messageTypeMapping[
"-PD"] = MessageType::RevBPilotDescription;
1093 void CFSDClient::handleAtcDataUpdate(
const QStringList &tokens)
1095 const AtcDataUpdate atcDataUpdate = AtcDataUpdate::fromTokens(tokens);
1096 const QString senderCs = atcDataUpdate.sender();
1097 const CCallsign cs(senderCs, CCallsign::Atc);
1100 if (atcDataUpdate.m_facility == CFacilityType::Unknown && !cs.isObserverCallsign())
1104 if (atcDataUpdate.m_facility == CFacilityType::OBS && !cs.hasSuffix()) {
return; }
1106 CFrequency freq(atcDataUpdate.m_frequencykHz, CFrequencyUnit::kHz());
1107 freq.switchUnit(CFrequencyUnit::MHz());
1113 const CLength networkRange(atcDataUpdate.m_visibleRange, CLengthUnit::NM());
1114 const CLength range = fixAtcRange(networkRange, cs);
1115 const CCoordinateGeodetic position(atcDataUpdate.m_latitude, atcDataUpdate.m_longitude, 0);
1117 emit this->atcDataUpdateReceived(cs, freq, position, range);
1119 m_atcStations.replaceOrAddObjectByCallsign({ cs, {}, freq, position, range });
1122 #ifdef SWIFT_VATSIM_SUPPORT
1123 void CFSDClient::handleAuthChallenge(
const QStringList &tokens)
1125 const AuthChallenge authChallenge = AuthChallenge::fromTokens(tokens);
1127 vatsim_auth_generate_response(m_clientAuth, qPrintable(authChallenge.m_challengeKey), response);
1128 sendAuthResponse(
QString(response));
1131 vatsim_auth_generate_challenge(m_serverAuth, challenge);
1132 m_lastServerAuthChallenge =
QString(challenge);
1133 sendAuthChallenge(m_lastServerAuthChallenge);
1137 #ifdef SWIFT_VATSIM_SUPPORT
1138 void CFSDClient::handleAuthResponse(
const QStringList &tokens)
1140 const AuthResponse authResponse = AuthResponse::fromTokens(tokens);
1142 char expectedResponse[33];
1143 vatsim_auth_generate_response(m_serverAuth, qPrintable(m_lastServerAuthChallenge), expectedResponse);
1144 if (authResponse.m_response !=
QString(expectedResponse))
1146 CLogMessage().
error(u
"The server you are connected to is not a VATSIM server. Disconnecting!");
1147 disconnectFromServer();
1152 void CFSDClient::handleDeleteATC(
const QStringList &tokens)
1154 const DeleteAtc deleteAtc = DeleteAtc::fromTokens(tokens);
1155 emit deleteAtcReceived(deleteAtc.m_cid);
1157 m_atcStations.removeByCallsign(deleteAtc.m_cid);
1160 void CFSDClient::handleDeletePilot(
const QStringList &tokens)
1162 const DeletePilot deletePilot = DeletePilot::fromTokens(tokens);
1163 const CCallsign cs(deletePilot.sender(), CCallsign::Aircraft);
1165 emit deletePilotReceived(deletePilot.m_cid);
1168 void CFSDClient::handleTextMessage(
const QStringList &tokens)
1170 const TextMessage textMessage = TextMessage::fromTokens(tokens);
1172 const CCallsign sender(textMessage.sender());
1173 const CCallsign receiver(textMessage.receiver());
1175 if (textMessage.m_type == TextMessage::PrivateMessage)
1181 if (m_server.getServerType() != CServer::FSDServerVatsim &&
1182 m_ownCallsign.asString() == textMessage.receiver() && m_pendingAtisQueries.contains(sender))
1184 maybeHandleAtisReply(sender, receiver, textMessage.m_message);
1188 CTextMessage tm(textMessage.m_message, sender, receiver);
1189 tm.setCurrentUtcTime();
1190 this->consolidateTextMessage(tm);
1192 else if (textMessage.m_type == TextMessage::RadioMessage)
1194 const CFrequency com1 = getOwnAircraft().getCom1System().getFrequencyActive();
1195 const CFrequency com2 = getOwnAircraft().getCom2System().getFrequencyActive();
1198 for (
int freqKhz : textMessage.m_frequencies)
1200 CFrequency f(freqKhz, CFrequencyUnit::kHz());
1201 CComSystem::roundToChannelSpacing(f, CComSystem::ChannelSpacing8_33KHz);
1202 if (f == com1 || f == com2) { frequencies.
push_back(f); }
1204 if (frequencies.
isEmpty()) {
return; }
1206 messages.setCurrentUtcTime();
1207 emit textMessagesReceived(messages);
1211 void CFSDClient::handlePilotDataUpdate(
const QStringList &tokens)
1213 const PilotDataUpdate dataUpdate = PilotDataUpdate::fromTokens(tokens);
1214 const CCallsign callsign(dataUpdate.sender(), CCallsign::Aircraft);
1217 callsign,
CCoordinateGeodetic(dataUpdate.m_latitude, dataUpdate.m_longitude, dataUpdate.m_altitudeTrue),
1218 CHeading(dataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1219 CAngle(dataUpdate.m_pitch, CAngleUnit::deg()),
CAngle(dataUpdate.m_bank, CAngleUnit::deg()),
1220 CSpeed(dataUpdate.m_groundSpeed, CSpeedUnit::kts()));
1222 CAltitude::PressureAltitude, CLengthUnit::ft()));
1224 const COnGroundInfo og(dataUpdate.m_onGround ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
1225 COnGroundInfo::NotSetGroundDetails);
1230 const qint64 offsetTimeMs =
1237 if (CTransponder::isValidTransponderCode(dataUpdate.m_transponderCode))
1239 transponder =
CTransponder(dataUpdate.m_transponderCode, dataUpdate.m_transponderMode);
1243 if (CBuildConfig::isLocalDeveloperDebugBuild())
1246 << dataUpdate.m_transponderCode << callsign;
1250 transponder =
CTransponder(2000, CTransponder::StateStandby);
1252 emit pilotDataUpdateReceived(situation, transponder);
1255 void CFSDClient::handleEuroscopeSimData(
const QStringList &tokens)
1261 CHeading(data.m_heading, CAngleUnit::deg()),
1262 CAngle(-data.m_pitch, CAngleUnit::deg()),
CAngle(-data.m_bank, CAngleUnit::deg()),
1263 CSpeed(data.m_groundSpeed, CSpeedUnit::kts()));
1265 const COnGroundInfo og(data.m_onGround ? COnGroundInfo::OnGround : COnGroundInfo::NotOnGround,
1266 COnGroundInfo::NotSetGroundDetails);
1271 const qint64 offsetTimeMs =
1280 emit euroscopeSimDataUpdatedReceived(situation, parts, currentOffsetTime(data.sender()), data.m_model,
1287 VisualPilotDataUpdate dataUpdate;
1288 switch (messageType)
1290 case MessageType::VisualPilotDataUpdate: dataUpdate = VisualPilotDataUpdate::fromTokens(tokens);
break;
1291 case MessageType::VisualPilotDataPeriodic: dataUpdate = VisualPilotDataPeriodic::fromTokens(tokens).toUpdate();
break;
1292 case MessageType::VisualPilotDataStopped: dataUpdate = VisualPilotDataStopped::fromTokens(tokens).toUpdate();
break;
1293 default: qFatal(
"Precondition violated");
break;
1295 const CCallsign callsign(dataUpdate.sender(), CCallsign::Aircraft);
1299 CCoordinateGeodetic(dataUpdate.m_latitude, dataUpdate.m_longitude, dataUpdate.m_altitudeTrue),
1300 CHeading(dataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1301 CAngle(dataUpdate.m_pitch, CAngleUnit::deg()),
1302 CAngle(dataUpdate.m_bank, CAngleUnit::deg()));
1314 emit visualPilotDataUpdateReceived(situation);
1318 void CFSDClient::handleVisualPilotDataToggle(
const QStringList &tokens)
1320 const VisualPilotDataToggle toggle = VisualPilotDataToggle::fromTokens(tokens);
1321 m_serverWantsVisualPositions = toggle.m_active;
1324 void CFSDClient::handlePing(
const QStringList &tokens)
1326 const Ping ping = Ping::fromTokens(tokens);
1327 sendPong(ping.sender(), ping.m_timestamp);
1330 void CFSDClient::handlePong(
const QStringList &tokens)
1332 const Pong pong = Pong::fromTokens(tokens);
1334 const qint64 elapsedTime = msecSinceEpoch - pong.m_timestamp.toLongLong();
1335 emit pongReceived(pong.sender(),
static_cast<double>(elapsedTime));
1338 void CFSDClient::handleKillRequest(
const QStringList &tokens)
1340 KillRequest killRequest = KillRequest::fromTokens(tokens);
1341 emit killRequestReceived(killRequest.m_reason);
1342 disconnectFromServer();
1345 void CFSDClient::handleFlightPlan(
const QStringList &tokens)
1347 FlightPlan fp = FlightPlan::fromTokens(tokens);
1350 switch (fp.m_flightType)
1352 case FlightType::VFR: rules = CFlightPlan::VFR;
break;
1353 case FlightType::IFR: rules = CFlightPlan::IFR;
break;
1354 case FlightType::DVFR: rules = CFlightPlan::DVFR;
break;
1355 case FlightType::SVFR: rules = CFlightPlan::SVFR;
break;
1361 int ca = cruiseAltString.
toInt();
1365 if (rules == CFlightPlan::IFR)
1367 if (ca >= 1000) { cruiseAltString = u
"FL" %
QString::number(ca / 100); }
1368 else { cruiseAltString = u
"FL" % cruiseAltString; }
1372 if (ca >= 5000) { cruiseAltString = u
"FL" %
QString::number(ca / 100); }
1373 else { cruiseAltString = cruiseAltString % u
"ft"; }
1377 cruiseAlt.
parseFromString(cruiseAltString, CPqString::SeparatorBestGuess);
1382 const CCallsign callsign(fp.sender(), CCallsign::Aircraft);
1386 CTime(fp.m_hoursEnroute * 60 + fp.m_minutesEnroute, CTimeUnit::min()),
1387 CTime(fp.m_fuelAvailHours * 60 + fp.m_fuelAvailMinutes, CTimeUnit::min()), cruiseAlt,
1388 CSpeed(fp.m_trueCruisingSpeed, CSpeedUnit::kts()), rules, fp.m_route, fp.m_remarks);
1390 emit flightPlanReceived(callsign, flightPlan);
1393 void CFSDClient::handleClientQuery(
const QStringList &tokens)
1395 const ClientQuery clientQuery = ClientQuery::fromTokens(tokens);
1397 if (clientQuery.m_queryType == ClientQueryType::Unknown) {
return; }
1398 if (clientQuery.m_queryType == ClientQueryType::IsValidATC)
1402 else if (clientQuery.m_queryType == ClientQueryType::Capabilities)
1404 sendClientResponse(ClientQueryType::Capabilities, clientQuery.sender());
1406 else if (clientQuery.m_queryType == ClientQueryType::Com1Freq)
1408 sendClientResponse(ClientQueryType::Com1Freq, clientQuery.sender());
1410 else if (clientQuery.m_queryType == ClientQueryType::RealName)
1412 sendClientResponse(ClientQueryType::RealName, clientQuery.sender());
1414 else if (clientQuery.m_queryType == ClientQueryType::Server)
1416 sendClientResponse(ClientQueryType::Server, clientQuery.sender());
1418 else if (clientQuery.m_queryType == ClientQueryType::ATIS)
1422 else if (clientQuery.m_queryType == ClientQueryType::PublicIP)
1426 else if (clientQuery.m_queryType == ClientQueryType::INF)
1428 sendClientResponse(ClientQueryType::INF, clientQuery.sender());
1430 else if (clientQuery.m_queryType == ClientQueryType::FP)
1434 else if (clientQuery.m_queryType == ClientQueryType::AircraftConfig)
1437 QString aircraftConfigJson = aircraftConfigTokens.
join(
":");
1439 const CCallsign callsign(clientQuery.sender(), CCallsign::Aircraft);
1447 CLogMessage(
this).
warning(u
"Failed to parse aircraft config packet: '%1' packet: '%2'")
1453 if (packet == JsonPackets::aircraftConfigRequest())
1457 QJsonObject config = this->getOwnAircraftParts().toJson();
1458 config.
insert(CAircraftParts::attributeNameIsFullJson(),
true);
1460 data = convertToUnicodeEscaped(data);
1461 sendAircraftConfiguration(clientQuery.sender(), data);
1465 const bool inRange = isAircraftInRange(callsign);
1466 if (!inRange) {
return; }
1467 if (!getSetupForServer().receiveAircraftParts()) {
return; }
1469 if (config.
isEmpty()) {
return; }
1471 const qint64 offsetTimeMs = currentOffsetTime(callsign);
1472 emit aircraftConfigReceived(clientQuery.sender(), config, offsetTimeMs);
1477 void CFSDClient::handleClientResponse(
const QStringList &tokens)
1479 const ClientResponse clientResponse = ClientResponse::fromTokens(tokens);
1480 if (clientResponse.isUnknownQuery()) {
return; }
1481 const QString sender = clientResponse.sender();
1486 if (!clientResponse.m_responseData.empty()) { responseData1 = clientResponse.m_responseData.
at(0); }
1488 if (clientResponse.m_responseData.size() > 1) { responseData2 = clientResponse.m_responseData.
at(1); }
1490 if (clientResponse.m_queryType == ClientQueryType::IsValidATC)
1492 emit validAtcResponseReceived(responseData2, responseData1 == u
"Y");
1494 else if (clientResponse.m_queryType == ClientQueryType::Capabilities)
1497 for (
auto keyValuePair : clientResponse.m_responseData)
1499 if (keyValuePair.count(
'=') != 1) {
continue; }
1502 if (
split.size() < 2) {
continue; }
1506 if (value ==
"1") { capabilities |= fromQString<Capabilities>(key); }
1509 CClient::Capabilities caps = CClient::None;
1510 if (capabilities & Capabilities::AtcInfo) { caps |= CClient::FsdAtisCanBeReceived; }
1511 if (capabilities & Capabilities::FastPos) { caps |= CClient::FsdWithInterimPositions; }
1512 if (capabilities & Capabilities::VisPos) { caps |= CClient::FsdWithVisualPositions; }
1513 if (capabilities & Capabilities::AircraftInfo) { caps |= CClient::FsdWithIcaoCodes; }
1514 if (capabilities & Capabilities::AircraftConfig) { caps |= CClient::FsdWithAircraftConfig; }
1516 emit capabilityResponseReceived(clientResponse.sender(), caps);
1518 else if (clientResponse.m_queryType == ClientQueryType::Com1Freq)
1520 if (responseData1.
isEmpty()) {
return; }
1522 const double freqMHz = responseData1.
toDouble(&ok);
1523 if (!ok) {
return; }
1524 emit com1FrequencyResponseReceived(clientResponse.sender(),
CFrequency(freqMHz, CFrequencyUnit::MHz()));
1526 else if (clientResponse.m_queryType == ClientQueryType::RealName)
1529 emit realNameResponseReceived(clientResponse.sender(), responseData1);
1531 else if (clientResponse.m_queryType == ClientQueryType::Server)
1533 emit serverResponseReceived(clientResponse.sender(), responseData1);
1535 else if (clientResponse.m_queryType == ClientQueryType::ATIS)
1542 updateAtisMap(clientResponse.sender(), fromQString<AtisLineType>(responseData1), responseData2);
1544 else if (clientResponse.m_queryType == ClientQueryType::PublicIP)
1548 else if (clientResponse.m_queryType == ClientQueryType::INF)
1552 else if (clientResponse.m_queryType == ClientQueryType::FP)
1556 else if (clientResponse.m_queryType == ClientQueryType::AircraftConfig)
1563 void CFSDClient::handleServerError(
const QStringList &tokens)
1565 const ServerError serverError = ServerError::fromTokens(tokens);
1566 switch (serverError.m_errorNumber)
1568 case ServerErrorCode::CallsignInUse:
CLogMessage(
this).
error(u
"The requested callsign is already taken");
break;
1569 case ServerErrorCode::InvalidCallsign:
CLogMessage(
this).
error(u
"The requested callsign is not valid");
break;
1570 case ServerErrorCode::InvalidCidPassword:
1573 case ServerErrorCode::InvalidRevision:
1574 CLogMessage(
this).
error(u
"This server does not support our protocol version");
1576 case ServerErrorCode::ServerFull:
CLogMessage(
this).
error(u
"The server is full");
break;
1577 case ServerErrorCode::CidSuspended:
CLogMessage(
this).
error(u
"Your user account is suspended");
break;
1578 case ServerErrorCode::RatingTooLow:
1579 CLogMessage(
this).
error(u
"You are not authorized to use the requested rating");
1581 case ServerErrorCode::InvalidClient:
1582 CLogMessage(
this).
error(u
"This software is not authorized for use on this network");
1584 case ServerErrorCode::RequestedLevelTooHigh:
1585 CLogMessage(
this).
error(u
"You are not authorized to use the requested pilot rating");
1589 case ServerErrorCode::SyntaxError:
1591 u
"Malformed packet, syntax error: '%1'. This can also occur if an OBS sends frequency text messages.")
1592 << serverError.getCausingParameter();
1594 case ServerErrorCode::InvalidSrcCallsign:
1595 CLogMessage(
this).
info(u
"FSD message was using an invalid callsign: %1 (%2)")
1596 << serverError.getCausingParameter() << serverError.getDescription();
1598 case ServerErrorCode::NoSuchCallsign:
1600 << serverError.getCausingParameter() << serverError.getDescription();
1602 case ServerErrorCode::NoFlightPlan:
CLogMessage(
this).
info(u
"FSD Server: no flight plan");
break;
1603 case ServerErrorCode::NoWeatherProfile:
1604 CLogMessage(
this).
info(u
"FSD Server: requested weather profile does not exist");
1608 case ServerErrorCode::AlreadyRegistered:
1609 CLogMessage(
this).
warning(u
"Server says already registered: %1") << serverError.getDescription();
1611 case ServerErrorCode::InvalidCtrl:
1612 CLogMessage(
this).
warning(u
"Server invalid control: %1") << serverError.getDescription();
1614 case ServerErrorCode::Unknown:
1616 << serverError.getCausingParameter() << serverError.getDescription();
1618 case ServerErrorCode::AuthTimeout:
CLogMessage(
this).
warning(u
"Client did not authenticate in time");
break;
1620 if (serverError.isFatalError()) { disconnectFromServer(); }
1623 void CFSDClient::handleRevBClientPartsPacket(
const QStringList &tokens)
1627 const RevBClientParts RevBClientParts = RevBClientParts::fromTokens(tokens);
1628 const CCallsign callsign(RevBClientParts.sender(), CCallsign::Aircraft);
1630 const bool inRange = isAircraftInRange(callsign);
1632 if (!inRange) {
return; }
1633 if (!getSetupForServer().receiveAircraftParts()) {
return; }
1635 const qint64 offsetTimeMs = currentOffsetTime(callsign);
1636 emit revbAircraftConfigReceived(RevBClientParts.sender(), RevBClientParts.m_partsval1, offsetTimeMs);
1640 void CFSDClient::handleRehost(
const QStringList &tokens)
1642 const Rehost rehost = Rehost::fromTokens(tokens);
1644 CLogMessage(
this).
info(u
"Server requested we switch server to %1") << rehost.m_hostname;
1646 SWIFT_AUDIT_X(!m_rehosting, Q_FUNC_INFO,
"Rehosting already in progress");
1649 auto rehostingSocket = std::make_shared<QTcpSocket>();
1651 readDataFromSocket();
1652 CLogMessage(this).debug(u
"Successfully switched server");
1653 m_socket = rehostingSocket;
1654 m_rehosting = false;
1655 rehostingSocket->disconnect(this);
1656 connectSocketSignals();
1657 readDataFromSocket();
1660 CLogMessage(this).warning(u
"Failed to switch server: %1") << rehostingSocket->errorString();
1661 m_rehosting = false;
1662 rehostingSocket->disconnect(this);
1663 if (m_socket->state() != QAbstractSocket::ConnectedState)
1665 updateConnectionStatus(CConnectionStatus::Disconnected);
1669 initiateConnection(rehostingSocket, rehost.m_hostname);
1672 void CFSDClient::handleMute(
const QStringList &tokens)
1674 const Mute mute = Mute::fromTokens(tokens);
1675 if (mute.receiver() != m_ownCallsign.asString()) {
return; }
1676 emit muteRequestReceived(mute.m_mute);
1679 void CFSDClient::initiateConnection(std::shared_ptr<QTcpSocket> rehostingSocket,
const QString &rehostingHost)
1681 const CServer server = this->getServer();
1682 const auto socket = rehostingSocket ? rehostingSocket : m_socket;
1686 const quint16 port = rehostingSocket ? m_socket->peerPort() :
static_cast<quint16
>(getServer().getPort());
1689 resolveLoadBalancing(host, [=](
const QString &host) {
1690 socket->connectToHost(host, port);
1691 if (!rehostingSocket) { this->startPositionTimers(); }
1695 void CFSDClient::resolveLoadBalancing(
const QString &host, std::function<
void(
const QString &)> callback)
1697 if (
QHostAddress(host).isNull() && (getServer().getName() ==
"AUTOMATIC" || m_rehosting) &&
1698 getServer().getEcosystem() == CEcosystem::VATSIM)
1701 Q_ASSERT_X(
sApp, Q_FUNC_INFO,
"Need app");
1711 callback(addr.toString());
1718 else { callback(host); }
1721 void CFSDClient::handleCustomPilotPacket(
const QStringList &tokens)
1725 if (subType == u
"PIR")
1727 PlaneInfoRequest planeInfoRequest = PlaneInfoRequest::fromTokens(tokens);
1730 const QString airlineIcao = m_server.getFsdSetup().force3LetterAirlineCodes() ?
1734 const QString livery = this->getConfiguredLiveryString(myAircraft);
1736 sendPlaneInformation(planeInfoRequest.sender(), acTypeICAO, airlineIcao, livery);
1738 else if (subType ==
"PI")
1740 if (tokens.
size() > 6 && tokens.
at(3) ==
"X")
1744 else if (tokens.
size() > 4 && tokens.
at(3) ==
"GEN")
1746 const PlaneInformation planeInformation = PlaneInformation::fromTokens(tokens);
1747 emit planeInformationReceived(planeInformation.sender(), planeInformation.m_aircraft,
1748 planeInformation.m_airline, planeInformation.m_livery);
1751 else if (subType ==
"I")
1755 else if (subType ==
"VI")
1758 if (!isInterimPositionReceivingEnabledForServer()) {
return; }
1760 const InterimPilotDataUpdate interimPilotDataUpdate = InterimPilotDataUpdate::fromTokens(tokens);
1761 const CCallsign callsign(interimPilotDataUpdate.sender(), CCallsign::Aircraft);
1765 interimPilotDataUpdate.m_longitude,
1766 interimPilotDataUpdate.m_altitudeTrue),
1767 CHeading(interimPilotDataUpdate.m_heading, CHeading::True, CAngleUnit::deg()),
1768 CAngle(interimPilotDataUpdate.m_pitch, CAngleUnit::deg()),
1769 CAngle(interimPilotDataUpdate.m_bank, CAngleUnit::deg()),
1770 CSpeed(interimPilotDataUpdate.m_groundSpeed, CSpeedUnit::kts()));
1772 const COnGroundInfo og(interimPilotDataUpdate.m_onGround ? COnGroundInfo::OnGround :
1773 COnGroundInfo::NotOnGround,
1774 COnGroundInfo::NotSetGroundDetails);
1779 const qint64 offsetTimeMs =
1783 emit interimPilotDataUpdatedReceived(situation);
1785 else if (subType ==
"FSIPI")
1787 const PlaneInformationFsinn planeInformationFsinn = PlaneInformationFsinn::fromTokens(tokens);
1788 emit planeInformationFsinnReceived(planeInformationFsinn.sender(), planeInformationFsinn.m_airlineIcao,
1789 planeInformationFsinn.m_aircraftIcao,
1790 planeInformationFsinn.m_aircraftIcaoCombinedType,
1791 planeInformationFsinn.m_sendMModelString);
1793 else if (subType ==
"FSIPIR")
1795 const PlaneInfoRequestFsinn planeInfoRequestFsinn = PlaneInfoRequestFsinn::fromTokens(tokens);
1796 sendPlaneInformationFsinn(planeInfoRequestFsinn.sender());
1797 emit planeInformationFsinnReceived(planeInfoRequestFsinn.sender(), planeInfoRequestFsinn.m_airlineIcao,
1798 planeInfoRequestFsinn.m_aircraftIcao,
1799 planeInfoRequestFsinn.m_aircraftIcaoCombinedType,
1800 planeInfoRequestFsinn.m_sendMModelString);
1807 emit customPilotPacketReceived(sender, data);
1811 #ifdef SWIFT_VATSIM_SUPPORT
1812 void CFSDClient::handleFsdIdentification(
const QStringList &tokens)
1816 const FSDIdentification fsdIdentification = FSDIdentification::fromTokens(tokens);
1817 vatsim_auth_set_initial_challenge(m_clientAuth, qPrintable(fsdIdentification.m_initialChallenge));
1819 char fsdChallenge[33];
1820 vatsim_auth_generate_challenge(m_serverAuth, fsdChallenge);
1821 vatsim_auth_set_initial_challenge(m_serverAuth, fsdChallenge);
1822 sendClientIdentification(
QString(fsdChallenge));
1827 u
"You tried to connect to a VATSIM server without using VATSIM protocol, disconnecting!");
1828 disconnectFromServer();
1833 void CFSDClient::handleUnknownPacket(
const QString &line)
1838 void CFSDClient::handleUnknownPacket(
const QStringList &tokens) { this->handleUnknownPacket(tokens.
join(
", ")); }
1842 if (m_rehosting) {
return; }
1844 CLogMessage(
this).
error(u
"FSD socket error: %1") << this->socketErrorString(socketError);
1849 if (m_rehosting) {
return; }
1851 const QString error = this->socketErrorString(socketError);
1852 switch (socketError)
1856 emit this->severeNetworkError(error);
1857 this->disconnectFromServer();
1863 void CFSDClient::handleSocketConnected()
1868 this->updateConnectionStatus(CConnectionStatus::Connected);
1874 if (this->getConnectionStatus() == newStatus) {
return; }
1880 m_server.setConnectedSinceNow();
1881 ecoSystem = m_server.getEcosystem();
1883 this->setCurrentEcosystem(ecoSystem);
1888 m_server.markAsDisconnected();
1893 this->stopPositionTimers();
1895 this->setLastEcosystem(m_server.getEcosystem());
1896 this->setCurrentEcosystem(CEcosystem::NoSystem);
1897 this->saveNetworkStatistics(m_server.getName());
1903 oldStatus = m_connectionStatus;
1904 m_connectionStatus = newStatus;
1907 emit this->connectionStatusChanged(oldStatus, newStatus);
1910 void CFSDClient::consolidateTextMessage(
const CTextMessage &textMessage)
1915 m_textMessagesToConsolidate.addConsolidatedTextMessage(textMessage);
1916 m_dsSendTextMessage.inputSignal();
1920 void CFSDClient::emitConsolidatedTextMessages()
1922 emit this->textMessagesReceived(m_textMessagesToConsolidate);
1923 m_textMessagesToConsolidate.clear();
1926 qint64 CFSDClient::receivedPositionFixTsAndGetOffsetTime(
const CCallsign &callsign, qint64 markerTs)
1928 Q_ASSERT_X(!callsign.
isEmpty(), Q_FUNC_INFO,
"Need callsign");
1931 if (!m_lastPositionUpdate.contains(callsign))
1933 m_lastPositionUpdate.insert(callsign, markerTs);
1934 return CFsdSetup::c_positionTimeOffsetMsec;
1936 const qint64 oldTs = m_lastPositionUpdate.value(callsign);
1937 m_lastPositionUpdate[callsign] = markerTs;
1940 const qint64 diff = qAbs(markerTs - oldTs);
1941 this->insertLatestOffsetTime(callsign, diff);
1944 const qint64 avgTimeMs = this->averageOffsetTimeMs(callsign, count, 3);
1945 qint64 offsetTime = CFsdSetup::c_positionTimeOffsetMsec;
1947 if (avgTimeMs < CFsdSetup::c_interimPositionTimeOffsetMsec && count >= 3)
1949 offsetTime = CFsdSetup::c_interimPositionTimeOffsetMsec;
1952 return m_additionalOffsetTime + offsetTime;
1955 qint64 CFSDClient::currentOffsetTime(
const CCallsign &callsign)
const
1957 Q_ASSERT_X(!callsign.
isEmpty(), Q_FUNC_INFO,
"Need callsign");
1959 if (!m_lastOffsetTimes.contains(callsign) || m_lastOffsetTimes[callsign].isEmpty())
1961 return CFsdSetup::c_positionTimeOffsetMsec;
1963 return m_lastOffsetTimes[callsign].front();
1966 void CFSDClient::clearState()
1968 m_rehosting =
false;
1969 m_stoppedSendingVisualPositions =
false;
1970 m_serverWantsVisualPositions =
false;
1971 m_visualPositionUpdateSentCount = 0;
1972 m_textMessagesToConsolidate.clear();
1973 m_pendingAtisQueries.clear();
1974 m_lastPositionUpdate.clear();
1975 m_lastOffsetTimes.clear();
1976 m_atcStations.clear();
1977 m_queuedFsdMessages.clear();
1978 m_sentAircraftConfig = CAircraftParts::null();
1982 void CFSDClient::clearState(
const CCallsign &callsign)
1984 if (callsign.
isEmpty()) {
return; }
1985 m_pendingAtisQueries.remove(callsign);
1986 m_lastPositionUpdate.remove(callsign);
1987 m_interimPositionReceivers.remove(callsign);
1988 m_lastOffsetTimes.remove(callsign);
1991 void CFSDClient::insertLatestOffsetTime(
const CCallsign &callsign, qint64 offsetMs)
1998 qint64 CFSDClient::averageOffsetTimeMs(
const CCallsign &callsign,
int &count,
int maxLastValues)
const
2001 if (offsets.
size() < 1) {
return -1; }
2004 for (qint64 v : offsets)
2008 if (count > maxLastValues) {
break; }
2010 return qRound(
static_cast<double>(sum) / count);
2013 bool CFSDClient::isInterimPositionSendingEnabledForServer()
const
2015 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
2016 return (d & CFsdSetup::SendInterimPositions);
2019 bool CFSDClient::isInterimPositionReceivingEnabledForServer()
const
2021 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
2022 return (d & CFsdSetup::ReceiveInterimPositions);
2025 bool CFSDClient::isVisualPositionSendingEnabledForServer()
const
2027 const CFsdSetup::SendReceiveDetails d = this->getSetupForServer().getSendReceiveDetails();
2028 return (d & CFsdSetup::SendVisualPositions);
2031 const CFsdSetup &CFSDClient::getSetupForServer()
const {
return m_server.getFsdSetup(); }
2035 Q_ASSERT(m_pendingAtisQueries.contains(sender));
2036 PendingAtisQuery &pendingQuery = m_pendingAtisQueries[sender];
2037 pendingQuery.m_atisMessage.push_back(message);
2044 tm.setCurrentUtcTime();
2045 this->consolidateTextMessage(tm);
2046 m_pendingAtisQueries.remove(sender);
2054 if (reLogoff.match(message).hasMatch())
2056 emit atisLogoffTimeReplyReceived(sender, message);
2058 for (
const auto &line : std::as_const(pendingQuery.m_atisMessage))
2060 if (!atisMessage.isEmpty()) atisMessage.appendMessage(
"\n");
2061 atisMessage.appendMessage(line);
2064 m_pendingAtisQueries.remove(sender);
2069 void CFSDClient::fsdMessageSettingsChanged()
2071 if (m_rawFsdMessageLogFile.isOpen()) { m_rawFsdMessageLogFile.close(); }
2078 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(),
"rawfsdmessages.log");
2079 m_rawFsdMessageLogFile.setFileName(filePath);
2085 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(),
"rawfsdmessages.log");
2086 m_rawFsdMessageLogFile.setFileName(filePath);
2090 else if (setting.
getFileWriteMode() == CRawFsdMessageSettings::Timestamped)
2092 QString filename(
"rawfsdmessages");
2096 const QString filePath = CFileUtils::appendFilePaths(setting.
getFileDir(), filename);
2097 m_rawFsdMessageLogFile.setFileName(filePath);
2105 return m_interimPositionReceivers;
2110 m_interimPositionReceivers = interimPositionReceivers;
2113 bool CFSDClient::isPendingConnection()
const
2115 return m_connectionStatus.isConnecting() || m_connectionStatus.isDisconnecting();
2118 int CFSDClient::increaseStatisticsValue(
const QString &identifier,
const QString &appendix)
2120 if (identifier.
isEmpty() || !m_statistics) {
return -1; }
2123 const QString i = appendix.
isEmpty() ? identifier : identifier % u
"." % appendix;
2124 int &v = m_callStatistics[i];
2127 constexpr
int MaxTimeValues = 50;
2129 if (m_callByTime.size() > MaxTimeValues) { m_callByTime.removeLast(); }
2133 int CFSDClient::increaseStatisticsValue(
const QString &identifier,
int value)
2138 void CFSDClient::clearStatistics()
2141 m_callStatistics.clear();
2142 m_callByTime.clear();
2153 callStatistics = m_callStatistics;
2154 callByTime = m_callByTime;
2157 if (callStatistics.
isEmpty()) {
return {}; }
2158 for (
const auto [key, value] :
makePairsRange(std::as_const(callStatistics)))
2165 std::sort(transformed.
begin(), transformed.
end(), std::greater<>());
2167 for (
const auto &pair : transformed)
2172 for (
const auto &pair : transformed)
2179 const qint64 lastTs = callByTime.
front().
first;
2180 for (
const auto &pair : std::as_const(callByTime))
2182 const qint64 deltaTs = lastTs - pair.first;
2183 stats += separator % QStringLiteral(
"%1").
arg(deltaTs, 5, 10,
QChar(
'0')) % u
": " % pair.second;
2187 if (reset) { this->clearStatistics(); }
2191 void CFSDClient::gracefulShutdown()
2193 disconnectFromServer();
2197 void CFSDClient::readDataFromSocketMaxLines(
int maxLines)
2199 if (m_socket->bytesAvailable() < 1) {
return; }
2204 while (m_socket->canReadLine())
2206 const QByteArray dataEncoded = m_socket->readLine();
2207 if (dataEncoded.
isEmpty()) {
continue; }
2208 const QString data = m_decoder(dataEncoded);
2209 this->parseMessage(data);
2212 static constexpr
int MaxLines = 75 - 1;
2213 if (maxLines < 0) { maxLines = MaxLines; }
2215 if (lines > maxLines)
2217 static constexpr
int DelayMs = 10;
2218 const int newMax = qRound(1.2 * lines);
2220 CLogMessage(
this).
debug(u
"ReadDataFromSocket has too many lines (>%1), will read again in %2ms")
2221 << MaxLines << DelayMs;
2225 if (myself) { myself->readDataFromSocketMaxLines(newMax); }
2234 QString e = CFSDClient::socketErrorToQString(error);
2235 if (!m_socket->errorString().isEmpty()) { e += QStringLiteral(
": ") % m_socket->errorString(); }
2241 static const QMetaEnum metaEnum = QMetaEnum::fromType<QAbstractSocket::SocketError>();
2245 void CFSDClient::parseMessage(
const QString &lineRaw)
2251 if (m_printToConsole) { qDebug() <<
"FSD Recv=>" << line; }
2252 emitRawFsdMessage(line,
false);
2259 messageType = m_messageTypeMapping.value(str, MessageType::Unknown);
2267 increaseStatisticsValue(QStringLiteral(
"parseMessage"), this->messageTypeToString(messageType));
2270 if (messageType != MessageType::Unknown)
2276 if (payload.
length() == 0) {
return; }
2279 switch (messageType)
2282 case MessageType::AddAtc:
2283 case MessageType::AddPilot:
2284 case MessageType::ServerHeartbeat:
2285 case MessageType::ProController:
2286 case MessageType::ClientIdentification:
2287 case MessageType::RegistrationInfo:
2288 case MessageType::RevBPilotDescription:
break;
2291 case MessageType::AtcDataUpdate: handleAtcDataUpdate(tokens);
break;
2292 #ifdef SWIFT_VATSIM_SUPPORT
2293 case MessageType::AuthChallenge: handleAuthChallenge(tokens);
break;
2294 case MessageType::AuthResponse: handleAuthResponse(tokens);
break;
2296 case MessageType::ClientQuery: handleClientQuery(tokens);
break;
2297 case MessageType::ClientResponse: handleClientResponse(tokens);
break;
2298 case MessageType::DeleteATC: handleDeleteATC(tokens);
break;
2299 case MessageType::DeletePilot: handleDeletePilot(tokens);
break;
2300 case MessageType::FlightPlan: handleFlightPlan(tokens);
break;
2301 #ifdef SWIFT_VATSIM_SUPPORT
2302 case MessageType::FsdIdentification: handleFsdIdentification(tokens);
break;
2304 case MessageType::KillRequest: handleKillRequest(tokens);
break;
2305 case MessageType::PilotDataUpdate: handlePilotDataUpdate(tokens);
break;
2306 case MessageType::Ping: handlePing(tokens);
break;
2307 case MessageType::Pong: handlePong(tokens);
break;
2308 case MessageType::ServerError: handleServerError(tokens);
break;
2309 case MessageType::TextMessage: handleTextMessage(tokens);
break;
2310 case MessageType::PilotClientCom: handleCustomPilotPacket(tokens);
break;
2311 case MessageType::RevBClientParts: handleRevBClientPartsPacket(tokens);
break;
2312 case MessageType::VisualPilotDataUpdate:
2313 case MessageType::VisualPilotDataPeriodic:
2314 case MessageType::VisualPilotDataStopped: handleVisualPilotDataUpdate(tokens, messageType);
break;
2315 case MessageType::VisualPilotDataToggle: handleVisualPilotDataToggle(tokens);
break;
2316 case MessageType::EuroscopeSimData: handleEuroscopeSimData(tokens);
break;
2317 case MessageType::Rehost: handleRehost(tokens);
break;
2318 case MessageType::Mute: handleMute(tokens);
break;
2322 case MessageType::Unknown: handleUnknownPacket(tokens);
break;
2325 else { handleUnknownPacket(line); }
2328 void CFSDClient::emitRawFsdMessage(
const QString &fsdMessage,
bool isSent)
2330 if (!m_unitTestMode && !m_rawFsdMessagesEnabled) {
return; }
2331 QString fsdMessageFiltered(fsdMessage);
2332 if (m_filterPasswordFromLogin)
2334 if (fsdMessageFiltered.startsWith(
"#AP"))
2336 thread_local
const QRegularExpression re(R
"(^(#AP\w+:SERVER:\d+:)[^:]+(:\d+:\d+:\d+:.+)$)");
2337 fsdMessageFiltered.replace(re, "\\1<password>\\2");
2338 m_filterPasswordFromLogin =
false;
2342 const QString prefix = isSent ?
"FSD Sent=>" :
"FSD Recv=>";
2344 rawMessage.setCurrentUtcTime();
2345 if (m_rawFsdMessageLogFile.isOpen())
2348 stream << rawMessage.toQString().trimmed() <<
Qt::endl;
2350 emit rawFsdMessage(rawMessage);
2353 bool CFSDClient::saveNetworkStatistics(
const QString &server)
2355 if (m_callStatistics.isEmpty()) {
return false; }
2357 const QString s = this->getNetworkStatisticsAsText(
false,
"\n");
2358 if (s.
isEmpty()) {
return false; }
2359 const QString fn = QStringLiteral(
"networkstatistics_%1_%2.log")
2361 const QString fp = CFileUtils::appendFilePaths(CSwiftDirectories::logDirectory(), fn);
2362 return CFileUtils::writeStringToFile(s, fp);
2365 void CFSDClient::startPositionTimers()
2367 m_positionUpdateTimer.start(c_updatePositionIntervalMsec);
2368 m_scheduledConfigUpdate.start(c_processingIntervalMsec);
2369 m_fsdSendMessageTimer.start(c_sendFsdMsgIntervalMsec);
2370 m_queuedFsdMessages.clear();
2374 if (this->isInterimPositionSendingEnabledForServer())
2376 m_interimPositionUpdateTimer.start(c_updateInterimPositionIntervalMsec);
2378 else { m_interimPositionUpdateTimer.stop(); }
2379 if (this->isVisualPositionSendingEnabledForServer())
2381 m_visualPositionUpdateTimer.start(c_updateVisualPositionIntervalMsec);
2383 else { m_visualPositionUpdateTimer.stop(); }
2387 void CFSDClient::stopPositionTimers()
2389 m_positionUpdateTimer.stop();
2390 m_interimPositionUpdateTimer.stop();
2391 m_visualPositionUpdateTimer.stop();
2392 m_scheduledConfigUpdate.stop();
2393 m_fsdSendMessageTimer.stop();
2396 void CFSDClient::updateAtisMap(
const QString &callsign, AtisLineType type,
const QString &line)
2399 if (type == AtisLineType::VoiceRoom)
2401 m_mapAtisMessages[callsign].m_voiceRoom = line;
2402 m_mapAtisMessages[callsign].m_lineCount++;
2405 if (type == AtisLineType::TextMessage)
2407 m_mapAtisMessages[callsign].m_textLines.
push_back(line);
2408 m_mapAtisMessages[callsign].m_lineCount++;
2411 if (type == AtisLineType::ZuluLogoff)
2413 m_mapAtisMessages[callsign].m_zuluLogoff = line;
2414 m_mapAtisMessages[callsign].m_lineCount++;
2417 if (!m_mapAtisMessages.contains(callsign)) {
return; }
2420 m_mapAtisMessages[callsign].m_lineCount++;
2422 const CCallsign cs(callsign, CCallsign::Atc);
2424 emit atisLogoffTimeReplyReceived(cs, m_mapAtisMessages[callsign].m_zuluLogoff);
2427 for (
const QString &tm : std::as_const(m_mapAtisMessages[callsign].m_textLines))
2430 if (!
fixed.isEmpty())
2436 if (test ==
"z")
return;
2438 if (test.
length() == 1)
return;
2441 if (!atisMessage.isEmpty()) atisMessage.appendMessage(
"\n");
2442 atisMessage.appendMessage(fixed);
2446 emit this->atisReplyReceived(cs, atisMessage);
2448 m_mapAtisMessages.remove(callsign);
2452 void CFSDClient::pendingTimeoutCheck()
2454 if (!this->isPendingConnection()) {
return; }
2457 if (age < PendingConnectionTimeoutMs) {
return; }
2460 CLogMessage(
this).
warning(u
"Timeout on pending connection to '%1'") << this->getServer().getName();
2461 this->disconnectFromServer();
2479 static const CLength l_Atis(150.0, CLengthUnit::NM());
2480 return maxOrNotNull(networkRange, l_Atis);
2484 static const CLength l_Gnd(10.0, CLengthUnit::NM());
2485 return maxOrNotNull(networkRange, l_Gnd);
2489 static const CLength l_Twr(25.0, CLengthUnit::NM());
2490 return maxOrNotNull(networkRange, l_Twr);
2494 static const CLength l_Dep(150.0, CLengthUnit::NM());
2495 return maxOrNotNull(networkRange, l_Dep);
2499 static const CLength l_App(150.0, CLengthUnit::NM());
2500 return maxOrNotNull(networkRange, l_App);
2504 static const CLength l_Ctr(300.0, CLengthUnit::NM());
2505 return maxOrNotNull(networkRange, l_Ctr);
2509 static const CLength l_Fss(1500.0, CLengthUnit::NM());
2510 return maxOrNotNull(networkRange, l_Fss);
2513 return networkRange;
2518 if (l1.
isNull()) {
return l2; }
2519 if (l2.
isNull()) {
return l1; }
2520 return (l2 > l1) ? l2 : l1;
2525 if (!input.
contains(
':')) {
return input; }
2527 return copy.remove(
':');
2532 switch (flightRules)
2534 case CFlightPlan::IFR:
return FlightType::IFR;
2535 case CFlightPlan::VFR:
return FlightType::VFR;
2536 case CFlightPlan::SVFR:
return FlightType::SVFR;
2537 case CFlightPlan::DVFR:
return FlightType::DVFR;
2538 default:
return FlightType::IFR;
2545 while (i != m_messageTypeMapping.constEnd())
2547 if (i.
value() == mt) {
return i.
key(); }
2555 void CFSDClient::handleIllegalFsdState(
const QString &message)
2557 if (CBuildConfig::isLocalDeveloperDebugBuild()) {
SWIFT_VERIFY_X(
false, Q_FUNC_INFO,
"Illegal FSD state"); }
2561 const QJsonObject &CFSDClient::JsonPackets::aircraftConfigRequest()
2563 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.
const CCallsign & getCallsign() const
Corresponding callsign.
virtual geo::CLatitude latitude() const
Latitude.
void setOnGroundInfo(const aviation::COnGroundInfo &info)
Set the on ground info.
const CAltitude & getAltitude() const
Get altitude.
virtual geo::CLongitude longitude() const
Longitude.
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.