11 #include "../fscommon/simulatorfscommonfunctions.h"
22 #include "misc/propertyindexallclasses.h"
28 using namespace swift::misc::aviation;
29 using namespace swift::misc::network;
30 using namespace swift::misc::physical_quantities;
31 using namespace swift::misc::geo;
32 using namespace swift::misc::network;
33 using namespace swift::misc::simulation;
34 using namespace swift::misc::simulation::fscommon;
36 using namespace swift::simplugin::fscommon;
37 using namespace swift::config;
39 namespace swift::simplugin::fs9
42 const QSharedPointer<CLobbyClient> &lobbyClient,
46 :
CSimulatorFsCommon(info, ownAircraftProvider, remoteAircraftProvider, clientProvider, parent),
47 m_fs9Host(fs9Host), m_lobbyClient(lobbyClient), m_fsuipc(new
CFsuipc(this))
50 this->setSimulationProviderEnabled(
false,
false);
56 this->setDefaultModel({
"Boeing 737-400", CAircraftModel::TypeModelMatchingDefaultModel,
64 Q_ASSERT_X(m_fs9Host, Q_FUNC_INFO,
"No FS9 host");
65 if (!m_fs9Host->isConnected()) {
return false; }
67 Q_ASSERT_X(m_fsuipc, Q_FUNC_INFO,
"No FSUIPC");
68 m_connectionHostMessages =
73 this->initSimulatorInternals();
74 m_timerId = startTimer(50);
80 if (!m_simConnected) {
return true; }
83 disconnect(m_connectionHostMessages);
85 disconnectAllClients();
87 if (m_fsuipc) { m_fsuipc->
close(); }
90 CSimulatorFsCommon::disconnectFrom();
91 m_simConnected =
false;
92 emitSimulatorCombinedStatus();
99 this->logAddingAircraftModel(newRemoteAircraft);
101 if (m_hashFs9Clients.contains(callsign))
108 new CFs9Client(newRemoteAircraft,
CTime(25, CTimeUnit::ms()), &m_interpolationLogger,
this);
114 m_hashFs9Clients.insert(callsign, client);
120 if (!m_hashFs9Clients.contains(callsign)) {
return false; }
122 auto fs9Client = m_hashFs9Clients.value(callsign);
124 m_hashFs9Clients.remove(callsign);
125 updateAircraftRendered(callsign,
false);
127 return CSimulatorFsCommon::physicallyRemoveRemoteAircraft(callsign);
132 if (m_hashFs9Clients.isEmpty()) {
return 0; }
133 QList<CCallsign> callsigns(m_hashFs9Clients.keys());
140 CSimulatorFsCommon::physicallyRemoveAllRemoteAircraft();
151 if (originator == this->identifier()) {
return false; }
157 bool changed =
false;
158 if (newTransponder.
getTransponderCode() != m_simTransponder.getTransponderCode()) { changed =
true; }
159 if (newTransponder.
getTransponderMode() != m_simTransponder.getTransponderMode()) { changed =
true; }
161 m_fsuipc->
write(newTransponder);
164 if (changed) { m_skipCockpitUpdateCycles = SkipUpdateCyclesForCockpit; }
172 if (originator == this->identifier()) {
return false; }
175 bool changed =
false;
176 if (selcal != m_selcal)
190 if (!m_fs9Host.data()) {
return; }
198 if (message.
getSeverity() != CStatusMessage::SeverityDebug)
200 QMetaObject::invokeMethod(m_fs9Host.data(),
"sendTextMessage", Q_ARG(QString, message.
toQString()));
206 if (!m_fs9Host.data()) {
return; }
207 QMetaObject::invokeMethod(m_fs9Host.data(),
"sendTextMessage", Q_ARG(QString, message.
asString(
true,
true)));
213 const CFs9Client *client = m_hashFs9Clients[callsign].data();
216 this->getInterpolationSetupPerCallsignOrDefault(callsign);
223 if (!m_hashFs9Clients.contains(callsign)) {
return false; }
224 CFs9Client *client = m_hashFs9Clients[callsign].data();
225 if (!client) {
return false; }
240 return m_hashFs9Clients.contains(callsign);
249 void CSimulatorFs9::dispatch()
251 if (m_fsuipc && m_fsuipc->
isOpened())
254 const bool ok = m_fsuipc->
read(fsuipcAircraft,
true,
true,
true);
255 if (ok) { updateOwnAircraftFromSimulator(fsuipcAircraft); }
266 QString getChangedParamsAsString(
const MPParam &old,
const MPParam &newParam)
270 if (old.unknown8 != newParam.unknown8) str +=
"unknown8 " + QString::number(newParam.unknown8) +
"\n";
271 if (old.unknown9 != newParam.unknown9) str +=
"unknown9 " + QString::number(newParam.unknown9) +
"\n";
272 if (old.flaps_left != newParam.flaps_left) str +=
"flaps_left " + QString::number(newParam.flaps_left) +
"\n";
273 if (old.flaps_right != newParam.flaps_right)
274 str +=
"flaps_right " + QString::number(newParam.flaps_right) +
"\n";
275 if (old.unknown12 != newParam.unknown12) str +=
"unknown12 " + QString::number(newParam.unknown12) +
"\n";
276 if (old.unknown13 != newParam.unknown13) str +=
"unknown13 " + QString::number(newParam.unknown13) +
"\n";
277 if (old.unknown14 != newParam.unknown14) str +=
"unknown14 " + QString::number(newParam.unknown14) +
"\n";
278 if (old.unknown15 != newParam.unknown15) str +=
"unknown15 " + QString::number(newParam.unknown15) +
"\n";
279 if (old.unknown16 != newParam.unknown16) str +=
"unknown16 " + QString::number(newParam.unknown16) +
"\n";
280 if (old.unknown17 != newParam.unknown17) str +=
"unknown17 " + QString::number(newParam.unknown17) +
"\n";
281 if (old.unknown18 != newParam.unknown18) str +=
"unknown18 " + QString::number(newParam.unknown18) +
"\n";
282 if (old.unknown19 != newParam.unknown19) str +=
"unknown19 " + QString::number(newParam.unknown19) +
"\n";
283 if (old.gear_center != newParam.gear_center)
284 str +=
"gear_center " + QString::number(newParam.gear_center) +
"\n";
285 if (old.gear_left != newParam.gear_left) str +=
"gear_left " + QString::number(newParam.gear_left) +
"\n";
286 if (old.gear_right != newParam.gear_right) str +=
"gear_right " + QString::number(newParam.gear_right) +
"\n";
287 if (old.engine_1 != newParam.engine_1) str +=
"engine_1 " + QString::number(newParam.engine_1) +
"\n";
288 if (old.engine_2 != newParam.engine_2) str +=
"engine_2 " + QString::number(newParam.engine_2) +
"\n";
289 if (old.unknown25 != newParam.unknown25) str +=
"unknown25 " + QString::number(newParam.unknown25) +
"\n";
290 if (old.unknown26 != newParam.unknown26) str +=
"unknown26 " + QString::number(newParam.unknown26) +
"\n";
291 if (old.unknown27 != newParam.unknown27) str +=
"unknown27 " + QString::number(newParam.unknown27) +
"\n";
295 void CSimulatorFs9::processFs9Message(
const QByteArray &message)
299 m_simConnected =
true;
300 emitSimulatorCombinedStatus();
305 case CFs9Sdk::MULTIPLAYER_PACKET_ID_PARAMS:
320 case CFs9Sdk::MULTIPLAYER_PACKET_ID_CHANGE_PLAYER_PLANE:
322 MPChangePlayerPlane mpChangePlayerPlane;
324 reverseLookupAndUpdateOwnAircraftModel(mpChangePlayerPlane.aircraft_name);
327 case CFs9Sdk::MULTIPLAYER_PACKET_ID_POSITION_VELOCITY:
331 case CFs9Sdk::MPCHAT_PACKET_ID_CHAT_TEXT_SEND:
333 MPChatText mpChatText;
341 void CSimulatorFs9::updateOwnAircraftFromSimulator(
const CSimulatedAircraft &simDataOwnAircraft)
346 if (m_skipCockpitUpdateCycles < 1)
349 const CComSystem oldCom1 = getOwnComSystem(CComSystem::Com1);
350 const CComSystem oldCom2 = getOwnComSystem(CComSystem::Com2);
352 this->updateCockpit(oldCom1, oldCom2, simDataOwnAircraft.
getTransponder(), this->identifier());
354 else { --m_skipCockpitUpdateCycles; }
357 this->updateOwnSituationAndGroundElevation(aircraftSituation);
359 this->updateOwnParts(simDataOwnAircraft.
getParts());
362 if (m_ownAircraftUpdateCycles % 25 == 0)
364 this->reverseLookupAndUpdateOwnAircraftModel(simDataOwnAircraft.
getModelString());
366 if (!cg.
isNull()) { this->updateOwnCG(cg); }
369 m_ownAircraftUpdateCycles++;
374 const bool updated = updateAircraftRendered(remoteAircraft.
getCallsign(),
true);
376 remoteAircraftCopy.setRendered(
true);
377 if (updated) { emit aircraftRenderingChanged(remoteAircraftCopy); }
381 void CSimulatorFs9::disconnectAllClients()
384 const QList<CCallsign> callsigns(m_hashFs9Clients.keys());
388 void CSimulatorFs9::synchronizeTime()
390 if (!m_simTimeSynced) {
return; }
392 if (!m_fsuipc) {
return; }
393 if (!m_fsuipc->
isOpened()) {
return; }
395 QDateTime myDateTime = QDateTime::currentDateTimeUtc();
396 if (!m_syncTimeOffset.isZeroEpsilonConsidered())
398 int offsetSeconds = m_syncTimeOffset.valueInteger(CTimeUnit::s());
399 myDateTime = myDateTime.addSecs(offsetSeconds);
402 const QTime myTime = myDateTime.time();
403 const int h = myTime.hour();
404 const int m = myTime.minute();
409 const QSharedPointer<CFs9Host> &fs9Host,
410 const QSharedPointer<CLobbyClient> &lobbyClient)
411 :
ISimulatorListener(info), m_timer(new QTimer(this)), m_fs9Host(fs9Host), m_lobbyClient(lobbyClient),
414 const int QueryInterval = 5 * 1000;
415 m_timer->setInterval(QueryInterval);
416 m_timer->setObjectName(this->objectName() +
":m_timer");
419 const bool canLobbyConnect = m_lobbyClient->canLobbyConnect();
422 QPointer<CSimulatorFs9Listener> myself(
this);
423 connect(m_timer, &QTimer::timeout, [=]() {
424 if (!myself) {
return; }
425 this->checkConnection(canLobbyConnect);
440 if (m_timer) { m_timer->start(); }
442 QPointer<CSimulatorFs9Listener> myself(
this);
444 if (!myself) {
return; }
445 const bool canLobbyConnect = m_lobbyClient->canLobbyConnect();
446 this->checkConnection(canLobbyConnect);
450 bool CSimulatorFs9Listener::checkConnection(
bool canLobbyConnect)
453 if (!m_fsuipc->isOpen()) {
return false; }
456 if (m_fs9Host->getHostAddress().isEmpty()) {
return false; }
459 if (m_isConnecting || isOk(m_lobbyClient->connectFs9ToHost(m_fs9Host->getHostAddress())))
461 m_isConnecting =
true;
462 CLogMessage(
this).
info(u
"swift is joining FS9 to the multiplayer session ...");
466 if (!m_isStarted && m_fs9Host->isConnected())
469 m_isConnecting =
false;
472 return m_isConnecting;
475 static void cleanupFs9Host(CFs9Host *host) {
delete host; }
477 static void cleanupLobbyClient(CLobbyClient *lobbyClient) {
delete lobbyClient; }
480 : QObject(parent), m_fs9Host(new
CFs9Host, cleanupFs9Host), m_lobbyClient(new
CLobbyClient, cleanupLobbyClient)
495 return new CSimulatorFs9(info, m_fs9Host, m_lobbyClient, ownAircraftProvider, remoteAircraftProvider,
496 clientProvider,
this);
Interface to a simulator.
Interface to a simulator listener.
const swift::misc::simulation::CSimulatorPluginInfo & getPluginInfo() const
Corresponding info.
void simulatorStarted(const swift::misc::simulation::CSimulatorPluginInfo &info)
Emitted when the listener discovers the simulator running.
virtual bool isShuttingDown() const
Overall (swift) application shutting down.
Value object encapsulating information identifying a component of a modular distributed swift process...
Class for emitting a log message.
Derived & debug()
Set the severity to debug.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Streamable status message, e.g.
bool isFromClass(const T *pointer=nullptr) const
Returns true if this message was sent by an instance of class T.
StatusSeverity getSeverity() const
Message severity.
Status messages, e.g. from Core -> GUI.
Value object for ICAO classification.
Value object encapsulating information of aircraft's parts.
bool isNull() const
NULL parts object?
Value object encapsulating information of an aircraft's situation.
virtual bool isNull() const
Null situation.
Value object encapsulating information of a callsign.
Value object for a set of callsigns.
int getTransponderCode() const
Transponder code.
TransponderMode getTransponderMode() const
Transponder mode.
QString toQString(bool i18n=false) const
Cast as QString.
Value object encapsulating information of a text message.
QString asString(bool withSender, bool withRecipient, const QString &separator=", ") const
Whole message as formatted string. Used to display message in a console window.
Direct in memory access to client (network client) data.
Physical unit length (length)
bool isNull() const
Is quantity null?
InterpolatorMode getInterpolatorMode() const
Interpolator mode.
Value object for interpolator and rendering per callsign.
Comprehensive information of an aircraft.
const aviation::CAircraftSituation & getSituation() const
Get situation.
const aviation::CTransponder & getTransponder() const
Get transponder.
const aviation::CCallsign & getCallsign() const
Get callsign.
QString getCallsignAsString() const
Get callsign.
const physical_quantities::CLength & getCG() const
Get CG from model.
const aviation::CAircraftParts & getParts() const
Get aircraft parts.
const QString & getModelString() const
Get model string.
Describing a simulator plugin.
Direct threadsafe in memory access to own aircraft.
Direct thread safe in memory access to remote aircraft.
DirectPlay peer implementation More information can be found in the DirectX9 SDK documentation http:/...
void customPacketReceived(const QByteArray &data)
Received custom FS9 packet.
void setPlayerUserId(DPNID id)
Sets users DirectPlay ID.
Class faking a FS9 multiplayer client connection.
void setHostAddress(const QString &hostAddress)
Set DirectPlay host address.
void sendMultiplayerParts(const swift::misc::aviation::CAircraftParts &parts)
Send parts (lights, gear ...)
void sendMultiplayerPosition(const swift::misc::aviation::CAircraftSituation &situation)
Send a situation (position)
void start()
Starts the FS9 client messaging.
swift::misc::CStatusMessageList getInterpolationMessages(swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
Interpolation messages.
void statusChanged(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft, swift::simplugin::fs9::CFs9Client::ClientStatus)
Client status changed.
ClientStatus
Connection status.
Class encapsulating a FS9 host.
MULTIPLAYER_PACKET_ID
Multiplayer packet id.
Lobby client launching and connecting FS9.
void disconnected()
Emitted when FS9 is closed.
virtual swift::core::ISimulatorListener * createListener(const swift::misc::simulation::CSimulatorPluginInfo &info)
Simulator listener instance.
virtual swift::core::ISimulator * create(const swift::misc::simulation::CSimulatorPluginInfo &info, swift::misc::simulation::IOwnAircraftProvider *ownAircraftProvider, swift::misc::simulation::IRemoteAircraftProvider *remoteAircraftProvider, swift::misc::network::IClientProvider *clientProvider)
Create a new instance of a driver.
virtual ~CSimulatorFs9Factory()
Destructor.
CSimulatorFs9Factory(QObject *parent=nullptr)
Constructor.
FS9 Simulator Implementation.
virtual bool isConnected() const
virtual bool physicallyAddRemoteAircraft(const swift::misc::simulation::CSimulatedAircraft &newRemoteAircraft)
virtual void timerEvent(QTimerEvent *event)
Timer event dispatching.
virtual swift::misc::CStatusMessageList getInterpolationMessages(const swift::misc::aviation::CCallsign &callsign) const
virtual swift::misc::aviation::CCallsignSet physicallyRenderedAircraft() const
virtual bool isPhysicallyRenderedAircraft(const swift::misc::aviation::CCallsign &callsign) const
virtual bool isSimulating() const
virtual void displayTextMessage(const swift::misc::network::CTextMessage &message) const
virtual bool disconnectFrom()
virtual bool physicallyRemoveRemoteAircraft(const swift::misc::aviation::CCallsign &callsign)
virtual void displayStatusMessage(const swift::misc::CStatusMessage &message) const
virtual bool updateOwnSimulatorCockpit(const swift::misc::simulation::CSimulatedAircraft &ownAircraft, const swift::misc::CIdentifier &originator)
virtual bool testSendSituationAndParts(const swift::misc::aviation::CCallsign &callsign, const swift::misc::aviation::CAircraftSituation &situation, const swift::misc::aviation::CAircraftParts &parts)
virtual bool updateOwnSimulatorSelcal(const swift::misc::aviation::CSelcal &selcal, const swift::misc::CIdentifier &originator)
virtual int physicallyRemoveAllRemoteAircraft()
Listener for FS9 Listener starts the FS9 multiplayer host and tries to make the running instance of s...
virtual void stopImpl()
Plugin specific implementation to stop listener.
CSimulatorFs9Listener(const swift::misc::simulation::CSimulatorPluginInfo &info, const QSharedPointer< CFs9Host > &fs9Host, const QSharedPointer< CLobbyClient > &lobbyClient)
Constructor.
virtual void startImpl()
Plugin specific implementation to start listener.
virtual void checkImpl()
Plugin specific implementation to check.
static CFs9Sdk::MULTIPLAYER_PACKET_ID readType(const QByteArray &data)
Read the multiplayer packet type.
static QByteArray readMessage(const QByteArray &data, Message &message)
Read message from byte stream.
Class representing a FSUIPC "interface".
bool open(bool force=false)
Open connection with FSUIPC.
bool isOpened() const
Is opened?
bool write(const swift::misc::aviation::CTransponder &xpdr)
Write variables.
bool read(swift::misc::simulation::CSimulatedAircraft &aircraft, bool cockpit, bool situation, bool aircraftParts)
Read data from FSUIPC.
bool setSimulatorTime(int hour, int minute)
Set simulator time.
Common base class for MS flight simulators.
Backend services of the swift project, like dealing with the network or the simulators.
void registerMetadata()
Register all relevant metadata in swift::core.
Free functions in swift::misc.
auto singleShot(int msec, QObject *target, F &&task)
Starts a single-shot timer which will call a task in the thread of the given object when it times out...