swift
fs9host.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 "fs9host.h"
7 
8 #include <QScopedArrayPointer>
9 #include <QVector>
10 
11 #include "directplayerror.h"
12 #include "directplayutils.h"
14 #include "multiplayerpackets.h"
15 
16 #include "core/application.h"
17 #include "misc/logmessage.h"
18 
19 using namespace swift::misc;
20 
21 namespace swift::simplugin::fs9
22 {
23  CFs9Host::CFs9Host(QObject *parent) : CDirectPlayPeer(sApp->swiftVersionString(), parent)
24  {
27  startHosting(sApp->swiftVersionString(), m_callsign.toQString());
28  }
29 
30  CFs9Host::~CFs9Host() { stopHosting(); }
31 
33  {
34  QString address;
35  if (m_hostStatus == Hosting)
36  {
37  DWORD dwNumAddresses = 0;
38 
39  HRESULT hr;
40  QVector<LPDIRECTPLAY8ADDRESS> addresses(static_cast<int>(dwNumAddresses));
41  m_directPlayPeer->GetLocalHostAddresses(addresses.data(), &dwNumAddresses, 0);
42  addresses.resize(static_cast<int>(dwNumAddresses));
43  ZeroMemory(addresses.data(), dwNumAddresses * sizeof(LPDIRECTPLAY8ADDRESS));
44  if (FAILED(hr = m_directPlayPeer->GetLocalHostAddresses(addresses.data(), &dwNumAddresses, 0)))
45  {
46  logDirectPlayError(hr);
47  return address;
48  }
49 
50  if (dwNumAddresses < 1) { return {}; }
51  char url[250];
52 
53  /*
54  DWORD size = 250;
55  addresses[0]->GetURLA(url, &size);
56  address = QString(url);
57  */
58 
59  // try to find URL address in any address
60  for (uint ii = 0; ii < dwNumAddresses; ++ii)
61  {
62  DWORD size = 250;
63  addresses[static_cast<int>(ii)]->GetURLA(url, &size);
64  address = QString(url);
65  if (!address.isEmpty()) { break; }
66  }
67 
68  for (uint ii = 0; ii < dwNumAddresses; ++ii)
69  {
70  LPDIRECTPLAY8ADDRESS pAddress = addresses[static_cast<int>(ii)];
71  SafeRelease(pAddress);
72  }
73  }
74  return address;
75  }
76 
77  void CFs9Host::sendTextMessage(const QString &textMessage)
78  {
79  MPChatText mpChatText;
80  mpChatText.chat_data = textMessage;
81  QByteArray message;
82  MultiPlayerPacketParser::writeType(message, CFs9Sdk::MPCHAT_PACKET_ID_CHAT_TEXT_SEND);
83  MultiPlayerPacketParser::writeSize(message, mpChatText.chat_data.size() + 1);
84  message = MultiPlayerPacketParser::writeMessage(message, mpChatText);
85  qDebug() << "Message:" << textMessage;
86  sendMessage(message);
87  }
88 
89  HRESULT CFs9Host::startHosting(const QString &session, const QString &callsign)
90  {
91  HRESULT hr = S_OK;
92 
93  if (m_hostStatus == Hosting) { return hr; }
94  if (!m_directPlayPeer) { return S_FALSE; }
95 
96  DPN_APPLICATION_DESC dpAppDesc;
97 
98  QScopedArrayPointer<wchar_t> wszSession(new wchar_t[static_cast<unsigned>(session.size()) + 1]);
99  QScopedArrayPointer<wchar_t> wszPlayername(new wchar_t[static_cast<unsigned>(callsign.size()) + 1]);
100 
101  session.toWCharArray(wszSession.data());
102  wszSession[session.size()] = 0;
103  callsign.toWCharArray(wszPlayername.data());
104  wszPlayername[callsign.size()] = 0;
105 
106  PLAYER_INFO_STRUCT playerInfo;
107  ZeroMemory(&playerInfo, sizeof(PLAYER_INFO_STRUCT));
108  strcpy(playerInfo.szAircraft, "Boeing 737-400");
109 
110  playerInfo.dwFlags = PLAYER_INFO_STRUCT::PARAMS_RECV | PLAYER_INFO_STRUCT::PARAMS_SEND;
111 
112  // Prepare and set the player information structure.
113  DPN_PLAYER_INFO player;
114  ZeroMemory(&player, sizeof(DPN_PLAYER_INFO));
115  player.dwSize = sizeof(DPN_PLAYER_INFO);
116  player.pvData = &playerInfo;
117  player.dwDataSize = sizeof(PLAYER_INFO_STRUCT);
118  player.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA;
119  player.pwszName = wszPlayername.data();
120  if (FAILED(hr = m_directPlayPeer->SetPeerInfo(&player, nullptr, nullptr, DPNSETPEERINFO_SYNC)))
121  {
122  logDirectPlayError(hr);
123  return hr;
124  }
125 
126  // Now set up the Application Description
127  ZeroMemory(&dpAppDesc, sizeof(DPN_APPLICATION_DESC));
128  dpAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
129  dpAppDesc.guidApplication = CFs9Sdk::guid();
130  dpAppDesc.pwszSessionName = wszSession.data();
131 
132  // We are now ready to host the app
133  if (FAILED(hr = m_directPlayPeer->Host(&dpAppDesc, // AppDesc
134  &m_deviceAddress, 1, // Device Address
135  nullptr,
136  nullptr, // Reserved
137  nullptr, // Player Context
138  0))) // dwFlags
139  {
140  logDirectPlayError(hr);
141  return hr;
142  }
143  else
144  {
145  CLogMessage(this).info(u"Hosting successfully started");
146  m_hostStatus = Hosting;
147  }
148 
149  // Enumerate the number of stalled DirectPlay peers
150  DWORD dwNumPlayers = 0;
151  hr = m_directPlayPeer->EnumPlayersAndGroups(nullptr, &dwNumPlayers, DPNENUM_PLAYERS);
152 
153  if (hr == DPNERR_BUFFERTOOSMALL)
154  {
155  QScopedArrayPointer<DPNID> stalledPeers(new DPNID[dwNumPlayers]);
156  hr = m_directPlayPeer->EnumPlayersAndGroups(stalledPeers.data(), &dwNumPlayers, DPNENUM_PLAYERS);
157 
158  // Destroy all stalled peers
159  for (DWORD i = 0; i < dwNumPlayers; ++i)
160  {
161  m_directPlayPeer->DestroyPeer(stalledPeers[static_cast<int>(i)], nullptr, 0, 0);
162  }
163  }
164 
165  emit statusChanged(m_hostStatus);
166  return hr;
167  }
168 
169  HRESULT CFs9Host::stopHosting()
170  {
171  HRESULT hr = S_OK;
172 
173  if (m_hostStatus == Terminated) return hr;
174 
175  CLogMessage(this).info(u"Hosting terminated!");
176  if (FAILED(hr = m_directPlayPeer->TerminateSession(nullptr, 0, 0))) { return logDirectPlayError(hr); }
177 
178  if (FAILED(hr = m_directPlayPeer->Close(0))) { return logDirectPlayError(hr); }
179 
180  m_hostStatus = Terminated;
181 
182  // Enumerate the number of stalled DirectPlay peers
183  DWORD dwNumPlayers = 0;
184  hr = m_directPlayPeer->EnumPlayersAndGroups(nullptr, &dwNumPlayers, DPNENUM_PLAYERS);
185 
186  if (hr == DPNERR_BUFFERTOOSMALL)
187  {
188  QScopedArrayPointer<DPNID> stalledPeers(new DPNID[dwNumPlayers]);
189  hr = m_directPlayPeer->EnumPlayersAndGroups(stalledPeers.data(), &dwNumPlayers, DPNENUM_PLAYERS);
190 
191  // Destroy all stalled peers
192  for (DWORD i = 0; i < dwNumPlayers; ++i)
193  {
194  m_directPlayPeer->DestroyPeer(stalledPeers[static_cast<int>(i)], nullptr, 0, 0);
195  }
196  }
197 
198  emit statusChanged(m_hostStatus);
199  return hr;
200  }
201 } // namespace swift::simplugin::fs9
SWIFT_CORE_EXPORT swift::core::CApplication * sApp
Single instance of application object.
Definition: application.cpp:71
const QString & swiftVersionString() const
swift info string
Class for emitting a log message.
Definition: logmessage.h:27
Derived & info(const char16_t(&format)[N])
Set the severity to info, providing a format string.
QString toQString(bool i18n=false) const
Cast as QString.
Definition: mixinstring.h:76
DirectPlay peer implementation More information can be found in the DirectX9 SDK documentation http:/...
HRESULT createHostAddress()
Creates a new DirectPlay device address.
IDirectPlay8Peer * m_directPlayPeer
DirectPlay peer address.
HRESULT initDirectPlay()
Initialize DirectPlay.
IDirectPlay8Address * m_deviceAddress
DirectPlay device address.
HRESULT sendMessage(const QByteArray &data)
Send a custom DirectPlay message.
const swift::misc::aviation::CCallsign m_callsign
Peer callsign.
void sendTextMessage(const QString &textMessage)
Send new text message.
Definition: fs9host.cpp:77
QString getHostAddress()
Get DirectPlay host url.
Definition: fs9host.cpp:32
void statusChanged(swift::simplugin::fs9::CFs9Host::HostStatus)
Hosting status changed.
virtual ~CFs9Host()
Destructor.
Definition: fs9host.cpp:30
static GUID guid()
Get FS9 application GUID.
Definition: fs9.h:47
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.
Free functions in swift::misc.
unsigned long DWORD
Fake Windows DWORD.
Multiplayer packet - chat text.