mirror of
https://github.com/swift-project/pilotclient.git
synced 2026-03-31 04:25:35 +08:00
refs #308 lobby client
This commit is contained in:
@@ -17,6 +17,33 @@ namespace BlackSimPlugin
|
||||
{
|
||||
namespace Fs9
|
||||
{
|
||||
//! Safely release a COM allocated object
|
||||
template <class T>
|
||||
void SafeRelease(T*& pT)
|
||||
{
|
||||
if (pT)
|
||||
pT->Release();
|
||||
pT = nullptr;
|
||||
}
|
||||
|
||||
//! Safely delete an allocated pointer
|
||||
template <class T>
|
||||
void SafeDelete(T*& pT)
|
||||
{
|
||||
if( pT != nullptr )
|
||||
delete pT;
|
||||
pT = nullptr;
|
||||
}
|
||||
|
||||
//! Safely delete an allocated array
|
||||
template <class T>
|
||||
void SafeDeleteArray(T*& pT)
|
||||
{
|
||||
if(pT)
|
||||
delete[] pT;
|
||||
pT = nullptr;
|
||||
}
|
||||
|
||||
//! Register all relevant metadata in BlackMisc
|
||||
void registerMetadata();
|
||||
|
||||
@@ -29,6 +56,8 @@ namespace BlackSimPlugin
|
||||
double updateInterval);
|
||||
|
||||
HRESULT printDirectPlayError(HRESULT error);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
289
src/plugins/simulator/fs9/lobby_client.cpp
Normal file
289
src/plugins/simulator/fs9/lobby_client.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
/* Copyright (C) 2014
|
||||
* swift project community / contributors
|
||||
*
|
||||
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
|
||||
* directory of this distribution and at http://www.swift-project.org/license.html. No part of Swift Project,
|
||||
* including this file, may be copied, modified, propagated, or distributed except according to the terms
|
||||
* contained in the LICENSE file.
|
||||
*/
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include "blacksimplugin_freefunctions.h"
|
||||
#include "blackmisc/project.h"
|
||||
#include "fs9.h"
|
||||
#include "lobby_client.h"
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QScopedPointer>
|
||||
#include <QMutexLocker>
|
||||
|
||||
namespace BlackSimPlugin
|
||||
{
|
||||
namespace Fs9
|
||||
{
|
||||
CLobbyClient::CLobbyClient(QObject *parent)
|
||||
: QObject(parent),
|
||||
m_callbackWrapper(this, &CLobbyClient::directPlayMessageHandler),
|
||||
m_lobbyCallbackWrapper(this, &CLobbyClient::directPlayLobbyMessageHandler)
|
||||
{
|
||||
initDirectPlay();
|
||||
}
|
||||
|
||||
CLobbyClient::~CLobbyClient()
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
HRESULT CLobbyClient::initDirectPlay()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
// Create and init IDirectPlay8Peer
|
||||
if (FAILED(hr = CoCreateInstance(CLSID_DirectPlay8Peer, nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IDirectPlay8Peer,
|
||||
(LPVOID *) &m_directPlayPeer)))
|
||||
return printDirectPlayError(hr);
|
||||
|
||||
// Turn off parameter validation in release builds
|
||||
const DWORD dwInitFlags = 0;
|
||||
// const DWORD dwInitFlags = DPNINITIALIZE_DISABLEPARAMVAL;
|
||||
|
||||
if (FAILED(hr = m_directPlayPeer->Initialize(&m_callbackWrapper, m_callbackWrapper.messageHandler, dwInitFlags)))
|
||||
return printDirectPlayError(hr);
|
||||
|
||||
|
||||
// Create and init IDirectPlay8LobbyClient
|
||||
if (FAILED(hr = CoCreateInstance(CLSID_DirectPlay8LobbyClient, nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IDirectPlay8LobbyClient,
|
||||
(LPVOID *) &m_dpLobbyClient)))
|
||||
return printDirectPlayError(hr);
|
||||
|
||||
if (FAILED(hr = m_dpLobbyClient->Initialize(&m_lobbyCallbackWrapper, m_lobbyCallbackWrapper.messageHandler, dwInitFlags)))
|
||||
return printDirectPlayError(hr);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CLobbyClient::connectFs9ToHost(const QString address)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
GUID pAppGuid = CFs9Sdk::guid();
|
||||
|
||||
// Set to true in order to automatically launch FS9. Perfect for testing.
|
||||
bool bLaunchNotFound = false;
|
||||
|
||||
// Setup the DPL_CONNECT_INFO struct
|
||||
DPL_CONNECT_INFO dnConnectInfo;
|
||||
ZeroMemory(&dnConnectInfo, sizeof(DPL_CONNECT_INFO));
|
||||
dnConnectInfo.dwSize = sizeof(DPL_CONNECT_INFO);
|
||||
dnConnectInfo.pvLobbyConnectData = nullptr;
|
||||
dnConnectInfo.dwLobbyConnectDataSize = 0;
|
||||
dnConnectInfo.dwFlags = 0;
|
||||
if (bLaunchNotFound) dnConnectInfo.dwFlags |= DPLCONNECT_LAUNCHNOTFOUND;
|
||||
dnConnectInfo.guidApplication = pAppGuid;
|
||||
|
||||
if (FAILED(hr = allocAndInitConnectSettings(address, &pAppGuid, &dnConnectInfo.pdplConnectionSettings)))
|
||||
return S_FALSE;
|
||||
|
||||
hr = m_dpLobbyClient->ConnectApplication(&dnConnectInfo,
|
||||
nullptr,
|
||||
&m_applicationHandle,
|
||||
INFINITE,
|
||||
0);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
if (hr == DPNERR_NOCONNECTION && !bLaunchNotFound)
|
||||
qWarning() << "There were no waiting application.";
|
||||
else
|
||||
return printDirectPlayError(hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Connected!";
|
||||
}
|
||||
|
||||
freeConnectSettings(dnConnectInfo.pdplConnectionSettings);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CLobbyClient::allocAndInitConnectSettings(const QString &address, GUID *pAppGuid, DPL_CONNECTION_SETTINGS **ppdplConnectSettings)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
IDirectPlay8Address *pHostAddress = nullptr;
|
||||
IDirectPlay8Address *pDeviceAddress = nullptr;
|
||||
|
||||
QScopedPointer<GUID> pSPGuid(new GUID);
|
||||
memcpy(pSPGuid.data(), &CLSID_DP8SP_TCPIP, sizeof(GUID));
|
||||
|
||||
|
||||
// Create a host address
|
||||
if (FAILED(hr = CoCreateInstance(CLSID_DirectPlay8Address, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IDirectPlay8Address, reinterpret_cast<void **>(&pHostAddress))))
|
||||
{
|
||||
return printDirectPlayError(hr);
|
||||
}
|
||||
|
||||
// Set the SP to pHostAddress
|
||||
if (FAILED(hr = pHostAddress->SetSP(pSPGuid.data())))
|
||||
{
|
||||
return printDirectPlayError(hr);
|
||||
}
|
||||
|
||||
// Create a device address to specify which device we are using
|
||||
if (FAILED(hr = CoCreateInstance(CLSID_DirectPlay8Address, NULL, CLSCTX_INPROC_SERVER,
|
||||
IID_IDirectPlay8Address, reinterpret_cast<void **>(&pDeviceAddress))))
|
||||
{
|
||||
return printDirectPlayError(hr);
|
||||
}
|
||||
|
||||
// Set the SP to pDeviceAddress
|
||||
if (FAILED(hr = pDeviceAddress->SetSP(&CLSID_DP8SP_TCPIP)))
|
||||
{
|
||||
return printDirectPlayError(hr);
|
||||
}
|
||||
|
||||
if (FAILED(hr = pHostAddress->BuildFromURLA(address.toLocal8Bit().data())))
|
||||
{
|
||||
return printDirectPlayError(hr);
|
||||
}
|
||||
|
||||
// Setup the DPL_CONNECTION_SETTINGS
|
||||
DPL_CONNECTION_SETTINGS *pSettings = new DPL_CONNECTION_SETTINGS;
|
||||
|
||||
// Allocate space for device address pointers
|
||||
// We cannot use QScopedArrayPointer, because memory needs to be valid
|
||||
// for the lifetime of DPL_CONNECTION_SETTINGS.
|
||||
IDirectPlay8Address **apDevAddress = new IDirectPlay8Address*[1];
|
||||
|
||||
// Set the device addresses
|
||||
apDevAddress[0] = pDeviceAddress;
|
||||
|
||||
QString session = BlackMisc::CProject::systemNameAndVersion();
|
||||
QScopedArrayPointer<wchar_t> wstrSessionName(new wchar_t[session.size() + 1]);
|
||||
session.toWCharArray(wstrSessionName.data());
|
||||
wstrSessionName[session.size()] = 0;
|
||||
|
||||
// Fill in the connection settings
|
||||
ZeroMemory(pSettings, sizeof(DPL_CONNECTION_SETTINGS));
|
||||
pSettings->dwSize = sizeof(DPL_CONNECTION_SETTINGS);
|
||||
pSettings->dpnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
|
||||
pSettings->dwFlags = 0;
|
||||
pSettings->dpnAppDesc.guidApplication = *pAppGuid;
|
||||
pSettings->dpnAppDesc.guidInstance = GUID_NULL;
|
||||
pSettings->dpnAppDesc.dwFlags = DPNSESSION_NODPNSVR;
|
||||
pSettings->pdp8HostAddress = pHostAddress;
|
||||
pSettings->ppdp8DeviceAddresses = apDevAddress;
|
||||
pSettings->cNumDeviceAddresses = 1;
|
||||
pSettings->dpnAppDesc.pwszSessionName = new WCHAR[wcslen(wstrSessionName.data()) + 1];
|
||||
wcscpy(pSettings->dpnAppDesc.pwszSessionName, wstrSessionName.data());
|
||||
|
||||
// FIXME: Use players callsign
|
||||
QString playerName("Player");
|
||||
WCHAR wstrPlayerName[m_maxSizePlayerName];
|
||||
playerName.toWCharArray(wstrPlayerName);
|
||||
wstrPlayerName[playerName.size()] = 0;
|
||||
pSettings->pwszPlayerName = new WCHAR[wcslen(wstrPlayerName) + 1];
|
||||
wcscpy(pSettings->pwszPlayerName, wstrPlayerName);
|
||||
|
||||
*ppdplConnectSettings = pSettings;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void CLobbyClient::freeConnectSettings(DPL_CONNECTION_SETTINGS *pSettings)
|
||||
{
|
||||
if (!pSettings) return;
|
||||
|
||||
SafeDeleteArray(pSettings->pwszPlayerName);
|
||||
SafeDeleteArray(pSettings->dpnAppDesc.pwszSessionName);
|
||||
SafeDeleteArray(pSettings->dpnAppDesc.pwszPassword);
|
||||
SafeDeleteArray(pSettings->dpnAppDesc.pvReservedData);
|
||||
SafeDeleteArray(pSettings->dpnAppDesc.pvApplicationReservedData);
|
||||
SafeRelease(pSettings->pdp8HostAddress);
|
||||
SafeRelease(pSettings->ppdp8DeviceAddresses[0]);
|
||||
SafeDeleteArray(pSettings->ppdp8DeviceAddresses);
|
||||
SafeDelete(pSettings);
|
||||
}
|
||||
|
||||
HRESULT CLobbyClient::directPlayMessageHandler(DWORD /* messageId */, void * /* msgBuffer */)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CLobbyClient::directPlayLobbyMessageHandler(DWORD messageId, void *msgBuffer)
|
||||
{
|
||||
switch (messageId)
|
||||
{
|
||||
case DPL_MSGID_DISCONNECT:
|
||||
{
|
||||
PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
|
||||
pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)msgBuffer;
|
||||
|
||||
// We should free any data associated with the
|
||||
// app here, but there is none.
|
||||
break;
|
||||
}
|
||||
|
||||
case DPL_MSGID_RECEIVE:
|
||||
{
|
||||
PDPL_MESSAGE_RECEIVE pReceiveMsg;
|
||||
pReceiveMsg = (PDPL_MESSAGE_RECEIVE)msgBuffer;
|
||||
|
||||
// The lobby app sent us data. This sample doesn't
|
||||
// expected data from the app, but it is useful
|
||||
// for more complex clients.
|
||||
break;
|
||||
}
|
||||
|
||||
case DPL_MSGID_SESSION_STATUS:
|
||||
{
|
||||
PDPL_MESSAGE_SESSION_STATUS pStatusMsg;
|
||||
pStatusMsg = (PDPL_MESSAGE_SESSION_STATUS)msgBuffer;
|
||||
|
||||
QString message;
|
||||
message.append(QString("%1: ").arg(pStatusMsg->hSender, 0, 16));
|
||||
switch (pStatusMsg->dwStatus)
|
||||
{
|
||||
case DPLSESSION_CONNECTED:
|
||||
message.append("Session connected"); break;
|
||||
case DPLSESSION_COULDNOTCONNECT:
|
||||
message.append("Session could not connect"); break;
|
||||
case DPLSESSION_DISCONNECTED:
|
||||
message.append("Session disconnected"); break;
|
||||
case DPLSESSION_TERMINATED:
|
||||
message.append("Session terminated"); break;
|
||||
case DPLSESSION_HOSTMIGRATED:
|
||||
message.append("Host migrated"); break;
|
||||
case DPLSESSION_HOSTMIGRATEDHERE:
|
||||
message.append("Host migrated to this client"); break;
|
||||
default:
|
||||
message.append("%1").arg(pStatusMsg->dwStatus);
|
||||
break;
|
||||
}
|
||||
qDebug() << message;
|
||||
break;
|
||||
}
|
||||
|
||||
case DPL_MSGID_CONNECTION_SETTINGS:
|
||||
{
|
||||
PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
|
||||
pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)msgBuffer;
|
||||
|
||||
// The app has changed the connection settings.
|
||||
// This simple client doesn't handle this, but more complex clients may
|
||||
// want to.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
src/plugins/simulator/fs9/lobby_client.h
Normal file
62
src/plugins/simulator/fs9/lobby_client.h
Normal file
@@ -0,0 +1,62 @@
|
||||
|
||||
#include "callback_wrapper.h"
|
||||
#include <QDebug>
|
||||
#include <dplay8.h>
|
||||
#include <dplobby8.h>
|
||||
|
||||
namespace BlackSimPlugin
|
||||
{
|
||||
namespace Fs9
|
||||
{
|
||||
//! Lobby client launching and connecting FS9
|
||||
class CLobbyClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
//! Constructor
|
||||
CLobbyClient(QObject *parent = nullptr);
|
||||
|
||||
//! Destructor
|
||||
~CLobbyClient();
|
||||
|
||||
//! Initialize DirectPlay
|
||||
HRESULT initDirectPlay();
|
||||
|
||||
//! Connect FS9 simulator to our host
|
||||
HRESULT connectFs9ToHost(const QString address);
|
||||
|
||||
private:
|
||||
|
||||
//! Alloc and fill up a DPL_CONNECTION_SETTINGS. Call FreeConnectSettings later to free it.
|
||||
HRESULT allocAndInitConnectSettings(const QString &address, GUID* pAppGuid, DPL_CONNECTION_SETTINGS** ppdplConnectSettings );
|
||||
|
||||
void freeConnectSettings( DPL_CONNECTION_SETTINGS* pSettings );
|
||||
|
||||
//! DirectPlay message handler
|
||||
HRESULT directPlayMessageHandler(DWORD messageId, void *msgBuffer);
|
||||
|
||||
//! DirectPlay message handler
|
||||
HRESULT directPlayLobbyMessageHandler(DWORD messageId, void *msgBuffer);
|
||||
|
||||
IDirectPlay8Peer *m_directPlayPeer = nullptr; //!< DirectPlay peer address
|
||||
IDirectPlay8Address *m_deviceAddress = nullptr; //!< DirectPlay device address
|
||||
IDirectPlay8Address *m_hostAddress = nullptr; //!< DirectPlay device address
|
||||
IDirectPlay8LobbyClient *m_dpLobbyClient = nullptr;
|
||||
|
||||
QString m_hostname = "localhost";
|
||||
|
||||
DWORD m_dwPort = 0;
|
||||
|
||||
DPNHANDLE m_applicationHandle = 0;
|
||||
|
||||
typedef CallbackWrapper<CLobbyClient, HRESULT, DWORD, void *> TCallbackWrapper; //!< DirectPlay message handler wrapper
|
||||
TCallbackWrapper m_callbackWrapper; //!< Callback wrapper
|
||||
TCallbackWrapper m_lobbyCallbackWrapper; //!< Callback wrapper
|
||||
|
||||
static const size_t m_maxSizePlayerName = 14;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ namespace BlackSimPlugin
|
||||
m_fs9Host(new CFs9Host),
|
||||
m_hostThread(this),
|
||||
m_simulatorInfo(CSimulatorInfo::FS9()),
|
||||
m_lobbyClient(new CLobbyClient(this)),
|
||||
m_fsuipc(new FsCommon::CFsuipc())
|
||||
{
|
||||
// We move the host thread already in the constructor
|
||||
@@ -76,7 +77,11 @@ namespace BlackSimPlugin
|
||||
{
|
||||
m_fsuipc->connect(); // connect FSUIPC too
|
||||
|
||||
// FIXME: This does start hosting only. Add lobby connection here.
|
||||
// If we are already hosting, connect FS0 through lobby connection
|
||||
if (m_isHosting) m_lobbyClient->connectFs9ToHost(m_fs9Host->getHostAddress());
|
||||
// If not, deferre connection until host is setup
|
||||
else m_startedLobbyConnection = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -288,6 +293,11 @@ namespace BlackSimPlugin
|
||||
m_isHosting = true;
|
||||
startTimer(50);
|
||||
emit statusChanged(Connected);
|
||||
if (m_startedLobbyConnection)
|
||||
{
|
||||
m_lobbyClient->connectFs9ToHost(m_fs9Host->getHostAddress());
|
||||
m_startedLobbyConnection = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CFs9Host::Terminated:
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#include "fs9_host.h"
|
||||
#include "fs9_client.h"
|
||||
#include "lobby_client.h"
|
||||
#include "../fscommon/fsuipc.h"
|
||||
#include "blackcore/simulator.h"
|
||||
#include "blackcore/interpolator_linear.h"
|
||||
@@ -153,6 +154,7 @@ namespace BlackSimPlugin
|
||||
CFs9Host *m_fs9Host = nullptr;
|
||||
QThread m_hostThread;
|
||||
bool m_isHosting = false; //!< Is sim connected
|
||||
bool m_startedLobbyConnection = false;
|
||||
bool m_syncTime = false; //!< Time synchronized?
|
||||
int m_syncDeferredCounter = 0; //!< Set when synchronized, used to wait some time
|
||||
bool m_simPaused = false; //!< Simulator paused?
|
||||
@@ -160,6 +162,8 @@ namespace BlackSimPlugin
|
||||
QHash<BlackMisc::Aviation::CCallsign, CFs9Client *> m_hashFs9Clients;
|
||||
QHash<CFs9Client *, QThread *> m_fs9ClientThreads;
|
||||
|
||||
CLobbyClient *m_lobbyClient;
|
||||
|
||||
BlackSim::CSimulatorInfo m_simulatorInfo;
|
||||
BlackMisc::Aviation::CAircraft m_ownAircraft; //!< Object representing our own aircraft from simulator
|
||||
BlackMisc::Aviation::CAirportList m_airportsInRange;
|
||||
|
||||
Reference in New Issue
Block a user