refs #308 lobby client

This commit is contained in:
Roland Winklmeier
2014-09-02 20:38:53 +02:00
parent b00d67e90c
commit 2ab1a0f847
5 changed files with 395 additions and 1 deletions

View File

@@ -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);
}
}

View 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;
}
}
}

View 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;
};
}
}

View File

@@ -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:

View File

@@ -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;