swift
fs9client.cpp
1 // SPDX-FileCopyrightText: Copyright (C) 2014 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #define _CRT_SECURE_NO_WARNINGS
5 
6 #include "fs9client.h"
7 
8 #include <QScopedArrayPointer>
9 
10 #include "../fscommon/simulatorfscommonfunctions.h"
11 #include "directplayerror.h"
12 #include "directplayutils.h"
14 #include "multiplayerpackets.h"
15 
16 #include "core/simulator.h"
19 #include "misc/logmessage.h"
20 
21 using namespace swift::misc;
22 using namespace swift::misc::aviation;
23 using namespace swift::misc::simulation;
24 using namespace swift::misc::physical_quantities;
25 using namespace swift::misc::geo;
26 using namespace swift::core;
27 using namespace swift::simplugin::fscommon;
28 
29 namespace swift::simplugin::fs9
30 {
31  CFs9Client::CFs9Client(const CSimulatedAircraft &remoteAircraft, const CTime &updateInterval,
32  CInterpolationLogger *logger, ISimulator *simulator)
33  : CDirectPlayPeer(remoteAircraft.getCallsign(), simulator), m_remoteAircraft(remoteAircraft),
34  m_updateInterval(updateInterval), m_interpolator(remoteAircraft.getCallsign(), simulator, simulator,
35  simulator->getRemoteAircraftProvider(), logger),
36  m_modelName(remoteAircraft.getModelString())
37  {
38  m_interpolator.attachLogger(logger);
39  Q_ASSERT_X(this->simulator(), Q_FUNC_INFO, "Wrong owner, expect simulator object");
40 
41  connect(this, &CFs9Client::connectionComplete, this, &CFs9Client::handleConnectionCompleted);
42  }
43 
44  MPPositionVelocity aircraftSituationToFS9(const CAircraftSituation &oldSituation,
45  const CAircraftSituation &newSituation, double updateInterval)
46  {
47  Q_UNUSED(oldSituation)
48  Q_UNUSED(updateInterval)
49  MPPositionVelocity positionVelocity;
50 
51  // Latitude - integer and decimal places
52  const double latitude = newSituation.getPosition().latitude().value(CAngleUnit::deg()) * 10001750.0 / 90.0;
53  positionVelocity.lat_i = static_cast<qint32>(latitude);
54  positionVelocity.lat_f = static_cast<quint16>(qRound(qAbs((latitude - positionVelocity.lat_i) * 65536)));
55 
56  // Longitude - integer and decimal places
57  const double longitude =
58  newSituation.getPosition().longitude().value(CAngleUnit::deg()) * (65536.0 * 65536.0) / 360.0;
59  positionVelocity.lon_hi = static_cast<qint32>(longitude);
60  positionVelocity.lon_lo = static_cast<quint16>(qRound(qAbs((longitude - positionVelocity.lon_hi) * 65536)));
61 
62  // Altitude - integer and decimal places
63  const double altitude = newSituation.getAltitude().value(CLengthUnit::m());
64  positionVelocity.alt_i = static_cast<qint32>(altitude);
65  positionVelocity.alt_f = static_cast<quint16>(qRound((altitude - positionVelocity.alt_i) * 65536));
66 
67  // Pitch, Bank and Heading
68  FS_PBH pbhstrct;
69  pbhstrct.hdg = static_cast<unsigned int>(
70  qRound(newSituation.getHeading().value(CAngleUnit::deg()) * CFs9Sdk::headingMultiplier()));
71  pbhstrct.pitch =
72  qRound(std::floor(newSituation.getPitch().value(CAngleUnit::deg()) * CFs9Sdk::pitchMultiplier()));
73  pbhstrct.bank = qRound(std::floor(newSituation.getBank().value(CAngleUnit::deg()) * CFs9Sdk::bankMultiplier()));
74  // MSFS has inverted pitch and bank angles
75  pbhstrct.pitch = ~pbhstrct.pitch;
76  pbhstrct.bank = ~pbhstrct.bank;
77  pbhstrct.onground = newSituation.isOnGround() ? 1 : 0;
78  positionVelocity.pbh = pbhstrct.pbh;
79 
80  // Ground velocity
81  positionVelocity.ground_velocity =
82  static_cast<quint16>(newSituation.getGroundSpeed().valueInteger(CSpeedUnit::m_s()));
83 
84  // Altitude velocity
85  CCoordinateGeodetic oldPosition = oldSituation.getPosition();
86  CCoordinateGeodetic newPosition = newSituation.getPosition();
87  CCoordinateGeodetic helperPosition;
88 
89  // We want the distance in Latitude direction. Longitude must be equal for old and new position.
90  helperPosition.setLatitude(newPosition.latitude());
91  helperPosition.setLongitude(oldPosition.longitude());
92  const CLength distanceLatitudeObj = calculateGreatCircleDistance(oldPosition, helperPosition);
93 
94  // Now we want the Longitude distance. Latitude must be equal for old and new position.
95  helperPosition.setLatitude(oldPosition.latitude());
96  helperPosition.setLongitude(newSituation.longitude());
97  const CLength distanceLongitudeObj = calculateGreatCircleDistance(oldPosition, helperPosition);
98 
99  // Latitude and Longitude velocity
100  positionVelocity.lat_velocity = qRound(distanceLatitudeObj.value(CLengthUnit::ft()) * 65536.0 / updateInterval);
101  if (oldPosition.latitude().value() > newSituation.latitude().value()) positionVelocity.lat_velocity *= -1;
102  positionVelocity.lon_velocity =
103  qRound(distanceLongitudeObj.value(CLengthUnit::ft()) * 65536.0 / updateInterval);
104  if (oldPosition.longitude().value() > newSituation.longitude().value()) positionVelocity.lon_velocity *= -1;
105 
106  return positionVelocity;
107  }
108 
109  MPPositionSlewMode aircraftSituationToFS9(const CAircraftSituation &situation)
110  {
111  MPPositionSlewMode positionSlewMode;
112 
113  // Latitude - integer and decimal places
114  const double latitude = situation.getPosition().latitude().value(CAngleUnit::deg()) * 10001750.0 / 90.0;
115  positionSlewMode.lat_i = static_cast<qint32>(latitude);
116  positionSlewMode.lat_f = static_cast<quint16>(qAbs((latitude - positionSlewMode.lat_i) * 65536));
117 
118  // Longitude - integer and decimal places
119  const double longitude =
120  situation.getPosition().longitude().value(CAngleUnit::deg()) * (65536.0 * 65536.0) / 360.0;
121  positionSlewMode.lon_hi = static_cast<qint32>(longitude);
122  positionSlewMode.lon_lo = static_cast<quint16>(qAbs((longitude - positionSlewMode.lon_hi) * 65536));
123 
124  // Altitude - integer and decimal places
125  double altitude = situation.getAltitude().value(CLengthUnit::m());
126  positionSlewMode.alt_i = static_cast<qint32>(altitude);
127  positionSlewMode.alt_f = static_cast<quint16>((altitude - positionSlewMode.alt_i) * 65536);
128 
129  // Pitch, Bank and Heading
130  FS_PBH pbhstrct;
131  pbhstrct.hdg = static_cast<unsigned int>(
132  qRound(situation.getHeading().value(CAngleUnit::deg()) * CFs9Sdk::headingMultiplier()));
133  pbhstrct.pitch = qRound(std::floor(situation.getPitch().value(CAngleUnit::deg()) * CFs9Sdk::pitchMultiplier()));
134  pbhstrct.bank = qRound(std::floor(situation.getBank().value(CAngleUnit::deg()) * CFs9Sdk::bankMultiplier()));
135  // MSFS has inverted pitch and bank angles
136  pbhstrct.pitch = ~pbhstrct.pitch;
137  pbhstrct.bank = ~pbhstrct.bank;
138 
139  pbhstrct.onground = situation.isOnGround() ? 1 : 0;
140  positionSlewMode.pbh = pbhstrct.pbh;
141 
142  return positionSlewMode;
143  }
144 
145  MPParam aircraftPartsToFS9(const CAircraftParts &parts)
146  {
147  MPParam param;
148  if (parts.isAnyEngineOn())
149  {
150  param.engine_1 = 140;
151  param.engine_2 = 140;
152  param.unknown14 = 60;
153  }
154 
155  if (parts.getFlapsPercent() > 50.0)
156  {
157  param.flaps_left = 228;
158  param.flaps_right = 228;
159  }
160 
161  if (parts.isFixedGearDown())
162  {
163  param.gear_center = 0xA1;
164  param.gear_left = 0xA1;
165  param.gear_right = 0xA1;
166  }
167  return param;
168  }
169 
171  {
172  closeConnection();
173  SafeRelease(m_hostAddress);
174 
175  // This needs to be disconnected before calling the base class destructor,
176  // otherwise the slot can be called after the derived class is destroyed.
177  disconnect(this, &CFs9Client::connectionComplete, this, &CFs9Client::handleConnectionCompleted);
178  }
179 
180  void CFs9Client::sendTextMessage(const QString &textMessage)
181  {
182  MPChatText mpChatText;
183  mpChatText.chat_data = textMessage;
184  QByteArray message;
185  MultiPlayerPacketParser::writeType(message, CFs9Sdk::MPCHAT_PACKET_ID_CHAT_TEXT_SEND);
186  MultiPlayerPacketParser::writeSize(message, mpChatText.size());
187  message = MultiPlayerPacketParser::writeMessage(message, mpChatText);
188  sendMessage(message);
189  }
190 
191  void CFs9Client::setHostAddress(const QString &hostAddress)
192  {
193  HRESULT hr = s_ok();
194 
195  // Create our IDirectPlay8Address Host Address
196  if (isFailure(hr = CoCreateInstance(CLSID_DirectPlay8Address, nullptr, CLSCTX_INPROC_SERVER,
197  IID_IDirectPlay8Address, reinterpret_cast<void **>(&m_hostAddress))))
198  {
199  logDirectPlayError(hr);
200  return;
201  }
202 
203  if (isFailure(hr = m_hostAddress->BuildFromURLA(hostAddress.toLatin1().data())))
204  {
205  logDirectPlayError(hr);
206  return;
207  }
208  }
209 
211  {
212  initDirectPlay();
214  connectToSession(m_callsign);
215  }
216 
219  {
220  if (!this->getInterpolator()) { return CStatusMessageList(); }
221  return this->getInterpolator()->getInterpolationMessages(mode);
222  }
223 
224  void CFs9Client::timerEvent(QTimerEvent *event)
225  {
226  Q_UNUSED(event)
227  sendMultiplayerPositionAndPartsFromInterpolation();
228  }
229 
230  HRESULT CFs9Client::enumDirectPlayHosts()
231  {
232  HRESULT hr = s_ok();
233  if (isFailure(hr = createHostAddress()))
234  {
235  CLogMessage(this).warning(u"FS9Client isFailure to create host address!");
236  return hr;
237  }
238 
239  // Now set up the Application Description
240  DPN_APPLICATION_DESC dpAppDesc;
241  ZeroMemory(&dpAppDesc, sizeof(DPN_APPLICATION_DESC));
242  dpAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
243  dpAppDesc.guidApplication = CFs9Sdk::guid();
244 
245  // We now have the host address so lets enum
246  if (isFailure(hr = m_directPlayPeer->EnumHosts(&dpAppDesc, // pApplicationDesc
247  m_hostAddress, // pdpaddrHost
248  m_deviceAddress, // pdpaddrDeviceInfo
249  nullptr, 0, // pvUserEnumData, size
250  0, // dwEnumCount
251  0, // dwRetryInterval
252  0, // dwTimeOut
253  nullptr, // pvUserContext
254  nullptr, // pAsyncHandle
255  DPNENUMHOSTS_SYNC))) // dwFlags
256  {
257  return logDirectPlayError(hr);
258  }
259  return hr;
260  }
261 
262  HRESULT CFs9Client::createHostAddress()
263  {
264  HRESULT hr = s_ok();
265 
266  // Create our IDirectPlay8Address Host Address
267  if (isFailure(hr = CoCreateInstance(CLSID_DirectPlay8Address, nullptr, CLSCTX_INPROC_SERVER,
268  IID_IDirectPlay8Address, reinterpret_cast<void **>(&m_hostAddress))))
269  {
270  return logDirectPlayError(hr);
271  }
272 
273  // Set the SP for our Host Address
274  if (isFailure(hr = m_hostAddress->SetSP(&CLSID_DP8SP_TCPIP))) { return logDirectPlayError(hr); }
275 
276  // FIXME: Test if this is also working via network or if we have to use the IP address
277  const wchar_t hostname[] = L"localhost";
278 
279  // Set the hostname into the address
280  if (isFailure(hr =
281  m_hostAddress->AddComponent(DPNA_KEY_HOSTNAME, hostname, 2 * (wcslen(hostname) + 1), /*bytes*/
282  DPNA_DATATYPE_STRING)))
283  {
284  return logDirectPlayError(hr);
285  }
286 
287  return hr;
288  }
289 
290  HRESULT CFs9Client::connectToSession(const CCallsign &callsign)
291  {
292  HRESULT hr = s_ok();
293  if (m_clientStatus == Connected) { return hr; }
294 
295  QScopedArrayPointer<wchar_t> wszPlayername(new wchar_t[static_cast<uint>(callsign.toQString().size() + 1)]);
296  callsign.toQString().toWCharArray(wszPlayername.data());
297  wszPlayername[callsign.toQString().size()] = 0;
298 
299  Q_ASSERT(!m_modelName.isEmpty());
300  ZeroMemory(&m_playerInfo, sizeof(PLAYER_INFO_STRUCT));
301  strcpy(m_playerInfo.szAircraft, qPrintable(m_modelName));
302  m_playerInfo.dwFlags = 6;
303 
304  // Prepare and set the player information structure.
305  ZeroMemory(&m_player, sizeof(DPN_PLAYER_INFO));
306  m_player.dwSize = sizeof(DPN_PLAYER_INFO);
307  m_player.pvData = &m_playerInfo;
308  m_player.dwDataSize = sizeof(PLAYER_INFO_STRUCT);
309  m_player.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA;
310  m_player.pwszName = wszPlayername.data();
311  hr = m_directPlayPeer->SetPeerInfo(&m_player, nullptr, nullptr, DPNSETPEERINFO_SYNC);
312  if (isFailure(hr)) { return logDirectPlayError(hr); }
313 
314  // Now set up the Application Description
315  DPN_APPLICATION_DESC dpAppDesc;
316  ZeroMemory(&dpAppDesc, sizeof(DPN_APPLICATION_DESC));
317  dpAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
318  dpAppDesc.guidApplication = CFs9Sdk::guid();
319 
320  DPNHANDLE asyncOpHandle;
321  hr = m_directPlayPeer->Connect(&dpAppDesc, m_hostAddress, m_deviceAddress, nullptr, nullptr, nullptr, 0,
322  nullptr, nullptr, &asyncOpHandle, 0);
323  if (!isPending(hr) && isFailure(hr)) { return logDirectPlayError(hr); }
324  return hr;
325  }
326 
327  HRESULT CFs9Client::closeConnection()
328  {
329  HRESULT hr = s_ok();
330 
331  if (m_clientStatus == Disconnected) { return hr; }
332  CLogMessage(this).info(u"Closing DirectPlay connection for '%1'") << m_callsign;
333  if (isFailure(hr = m_directPlayPeer->Close(0))) { return logDirectPlayError(hr); }
334 
335  m_clientStatus = Disconnected;
336  emit statusChanged(m_remoteAircraft, m_clientStatus);
337  return hr;
338  }
339 
340  void CFs9Client::sendMultiplayerPositionAndPartsFromInterpolation()
341  {
342  // remark: in FS9 there is no central updateRemoteAircraft() function, each FS9 client updates itself
343  if (m_clientStatus == Disconnected) { return; }
344  const bool forceFullUpdate = false;
346  this->simulator()->getInterpolationSetupConsolidated(m_callsign, forceFullUpdate);
347  const CInterpolationResult result =
348  m_interpolator.getInterpolation(QDateTime::currentMSecsSinceEpoch(), setup, 0);
349 
350  // Test only for successful position. FS9 requires constant positions
351  if (!result.getInterpolationStatus().hasValidSituation()) { return; }
354  }
355 
357  {
358  MPPositionSlewMode positionSlewMode = aircraftSituationToFS9(situation);
359 
360  QByteArray positionMessage;
361  MultiPlayerPacketParser::writeType(positionMessage, CFs9Sdk::MULTIPLAYER_PACKET_ID_POSITION_SLEWMODE);
362  MultiPlayerPacketParser::writeSize(positionMessage, positionSlewMode.size());
363  positionSlewMode.packet_index = m_packetIndex;
364  ++m_packetIndex;
365  positionMessage = MultiPlayerPacketParser::writeMessage(positionMessage, positionSlewMode);
366 
367  sendMessage(positionMessage);
368  }
369 
371  {
372  Q_UNUSED(parts)
373  QByteArray paramMessage;
374  MPParam param = aircraftPartsToFS9(parts);
375 
376  MultiPlayerPacketParser::writeType(paramMessage, CFs9Sdk::MULTIPLAYER_PACKET_ID_PARAMS);
377  MultiPlayerPacketParser::writeSize(paramMessage, param.size());
378  param.packet_index = m_packetIndex;
379  ++m_packetIndex;
380  paramMessage = MultiPlayerPacketParser::writeMessage(paramMessage, param);
381  sendMessage(paramMessage);
382  }
383 
384  CFs9Sdk::EngineType aircraftToFS9EngineType(const CSimulatedAircraft &aircraft)
385  {
386  const QChar engine = aircraft.getAircraftIcaoCode().getEngineTypeChar();
387  const QChar type = aircraft.getAircraftIcaoCode().getAircraftTypeChar();
388 
389  if (type == 'H') return CFs9Sdk::ENGINE_TYPE_HELO_TURBINE;
390  if (engine == 'J') return CFs9Sdk::ENGINE_TYPE_JET;
391  if (engine == 'P') return CFs9Sdk::ENGINE_TYPE_PISTON;
392  if (engine == 'T') return CFs9Sdk::ENGINE_TYPE_TURBOPROP;
393 
394  return CFs9Sdk::ENGINE_TYPE_JET;
395  }
396 
397  void CFs9Client::sendMultiplayerChangePlayerPlane()
398  {
399  MPChangePlayerPlane mpChangePlayerPlane;
400  mpChangePlayerPlane.engine = aircraftToFS9EngineType(m_remoteAircraft);
401  mpChangePlayerPlane.aircraft_name = m_modelName;
402  QByteArray message;
403  MultiPlayerPacketParser::writeType(message, CFs9Sdk::MULTIPLAYER_PACKET_ID_CHANGE_PLAYER_PLANE);
404  MultiPlayerPacketParser::writeSize(message, mpChangePlayerPlane.size());
405  message = MultiPlayerPacketParser::writeMessage(message, mpChangePlayerPlane);
406  sendMessage(message);
407  }
408 
409  void CFs9Client::handleConnectionCompleted()
410  {
411  CLogMessage(this).info(u"Callsign '%1' connected to session.") << m_callsign;
412 
413  m_clientStatus = Connected; // will not send position in disconnected mode
414  sendMultiplayerChangePlayerPlane();
415  sendMultiplayerPositionAndPartsFromInterpolation();
416  startTimer(m_updateInterval.valueInteger(CTimeUnit::ms()));
417 
418  emit statusChanged(m_remoteAircraft, m_clientStatus);
419  }
420 
421  const ISimulator *CFs9Client::simulator() const { return qobject_cast<const ISimulator *>(this->parent()); }
422 } // namespace swift::simplugin::fs9
Interface to a simulator.
Definition: simulator.h:59
Class for emitting a log message.
Definition: logmessage.h:27
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
Status messages, e.g. from Core -> GUI.
QChar getEngineTypeChar() const
Get engine type, e.g. "J".
QChar getAircraftTypeChar() const
Aircraft type, such a L(andplane), S(eaplane), H(elicopter)
Value object encapsulating information of aircraft's parts.
Definition: aircraftparts.h:26
bool isFixedGearDown() const
Is fixed gear down?
bool isAnyEngineOn() const
Any engine on?
int getFlapsPercent() const
Get flaps position in percent.
Value object encapsulating information of an aircraft's situation.
const CHeading & getHeading() const
Get heading.
virtual geo::CLatitude latitude() const
Latitude.
const physical_quantities::CSpeed & getGroundSpeed() const
Get ground speed.
const CAltitude & getAltitude() const
Get altitude.
const physical_quantities::CAngle & getBank() const
Get bank (angle)
const physical_quantities::CAngle & getPitch() const
Get pitch.
const geo::CCoordinateGeodetic & getPosition() const
Get position.
virtual geo::CLongitude longitude() const
Longitude.
Value object encapsulating information of a callsign.
Definition: callsign.h:30
void setLatitude(const CLatitude &latitude)
Set latitude.
virtual CLatitude latitude() const
Latitude.
virtual CLongitude longitude() const
Longitude.
void setLongitude(const CLongitude &longitude)
Set longitude.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
Physical unit length (length)
Definition: length.h:18
int valueInteger(MU unit) const
As integer value.
double value(MU unit) const
Value in given unit.
Value object for interpolator and rendering per callsign.
Record internal state of interpolator for debugging.
const CInterpolationStatus & getInterpolationStatus() const
Get status.
const aviation::CAircraftSituation & getInterpolatedSituation() const
Get situation.
const aviation::CAircraftParts & getInterpolatedParts() const
Get parts (interpolated or guessed)
bool hasValidSituation() const
Is the corresponding position valid?
CInterpolationResult getInterpolation(qint64 currentTimeSinceEpoch, const CInterpolationAndRenderingSetupPerCallsign &setup, uint32_t aircraftNumber)
Get interpolated situation.
const CStatusMessageList & getInterpolationMessages(CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
Interpolation messages.
void attachLogger(CInterpolationLogger *logger)
Attach an observer to read the interpolator's state for debugging.
Comprehensive information of an aircraft.
const aviation::CAircraftIcaoCode & getAircraftIcaoCode() const
Get aircraft ICAO info.
DirectPlay peer implementation More information can be found in the DirectX9 SDK documentation http:/...
IDirectPlay8Peer * m_directPlayPeer
DirectPlay peer address.
HRESULT initDirectPlay()
Initialize DirectPlay.
IDirectPlay8Address * m_deviceAddress
DirectPlay device address.
void connectionComplete()
Async operatione complete.
HRESULT sendMessage(const QByteArray &data)
Send a custom DirectPlay message.
quint32 m_packetIndex
Multiplayer packet index.
const swift::misc::aviation::CCallsign m_callsign
Peer callsign.
HRESULT createDeviceAddress()
Creates a new DirectPlay device address.
void setHostAddress(const QString &hostAddress)
Set DirectPlay host address.
Definition: fs9client.cpp:191
virtual ~CFs9Client()
Destructor.
Definition: fs9client.cpp:170
void sendMultiplayerParts(const swift::misc::aviation::CAircraftParts &parts)
Send parts (lights, gear ...)
Definition: fs9client.cpp:370
swift::misc::simulation::CInterpolatorMulti * getInterpolator()
Get interpolator.
Definition: fs9client.h:54
void sendMultiplayerPosition(const swift::misc::aviation::CAircraftSituation &situation)
Send a situation (position)
Definition: fs9client.cpp:356
void sendTextMessage(const QString &textMessage)
Send new text message.
Definition: fs9client.cpp:180
void start()
Starts the FS9 client messaging.
Definition: fs9client.cpp:210
swift::misc::CStatusMessageList getInterpolationMessages(swift::misc::simulation::CInterpolationAndRenderingSetupBase::InterpolatorMode mode) const
Interpolation messages.
Definition: fs9client.cpp:218
void statusChanged(const swift::misc::simulation::CSimulatedAircraft &remoteAircraft, swift::simplugin::fs9::CFs9Client::ClientStatus)
Client status changed.
virtual void timerEvent(QTimerEvent *event)
Definition: fs9client.cpp:224
static double bankMultiplier()
Return the FS9 bank multiplier.
Definition: fs9.h:56
static GUID guid()
Get FS9 application GUID.
Definition: fs9.h:47
static double headingMultiplier()
Returns the FS9 heading multiplier.
Definition: fs9.h:59
EngineType
Engine type.
Definition: fs9.h:27
static double pitchMultiplier()
Returns the FS9 pitch multiplier.
Definition: fs9.h:53
static void writeSize(QByteArray &data, qint32 size)
Write the multiplayer packet size.
static QByteArray writeMessage(QByteArray &data, const Message &message)
Write message to byte stream.
static void writeType(QByteArray &data, CFs9Sdk::MULTIPLAYER_PACKET_ID type)
Write the multiplayer packet type.
void SafeRelease(T *&pT)
Safely release a COM allocated object.
Backend services of the swift project, like dealing with the network or the simulators.
Definition: actionbind.cpp:7
Free functions in swift::misc.
HRESULT s_ok()
Correctly casted values/checks.
bool isFailure(HRESULT result)
Correctly casted values/checks.
Multiplayer packet - chat text.
qint32 size() const
Struct size.
Multiplayer param packet - aircraft configuration.
qint32 size() const
Struct size.
Multiplayer packet in slew mode.
Multiplayer packet - position and velocity.
quint32 dwFlags
Player flags.
Definition: fs9.h:181
char szAircraft[MAX_PATH+1]
Aircraft model type.
Definition: fs9.h:182
Pitch/Bank/Heading.
Definition: fs9.h:198
unsigned int pbh
Pitch/Bank/Heading as integer value.
Definition: fs9.h:199
unsigned int onground
Onground flag.
Definition: fs9.h:205
unsigned int hdg
Heading.
Definition: fs9.h:206