From cf39b8782198ae49593933587e511491598d5ffd Mon Sep 17 00:00:00 2001 From: Roland Winklmeier Date: Sat, 26 Jul 2014 19:01:29 +0200 Subject: [PATCH] refs #241 DirectPlay peer base class --- src/plugins/simulator/fs9/directplay_peer.cpp | 278 ++++++++++++++++++ src/plugins/simulator/fs9/directplay_peer.h | 95 ++++++ 2 files changed, 373 insertions(+) create mode 100644 src/plugins/simulator/fs9/directplay_peer.cpp create mode 100644 src/plugins/simulator/fs9/directplay_peer.h diff --git a/src/plugins/simulator/fs9/directplay_peer.cpp b/src/plugins/simulator/fs9/directplay_peer.cpp new file mode 100644 index 000000000..9c6a36312 --- /dev/null +++ b/src/plugins/simulator/fs9/directplay_peer.cpp @@ -0,0 +1,278 @@ +/* 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. + */ + +#include "directplay_peer.h" +#include "multiplayer_packet_parser.h" +#include +#include +#include +#include +#include +#include + +namespace BlackSimPlugin +{ + namespace Fs9 + { + CDirectPlayPeer::CDirectPlayPeer(const QString &callsign, QObject *parent) + : QObject(parent), + m_callsign(callsign), + m_mutexHostList(QMutex::Recursive), + m_callbackWrapper(this, &CDirectPlayPeer::directPlayMessageHandler) + { + m_playerLocal = 0; + m_playerUser = 0; + } + + CDirectPlayPeer::~CDirectPlayPeer() + { + if (m_directPlayPeer) + { + m_directPlayPeer->Close(DPNCLOSE_IMMEDIATE); + m_directPlayPeer->Release(); + } + + if (m_deviceAddress) m_deviceAddress->Release(); + } + + HRESULT CDirectPlayPeer::directPlayMessageHandler(DWORD messageId, void *msgBuffer) + { + HRESULT hr = S_OK; + + switch (messageId) + { + case DPN_MSGID_CREATE_PLAYER: + { + DPNMSG_CREATE_PLAYER* pCreatePlayerMsg = static_cast(msgBuffer); + + HRESULT hr; + + // Get the peer info and extract its name + DWORD dwSize = 0; + DPN_PLAYER_INFO* pdpPlayerInfo = nullptr; + hr = DPNERR_CONNECTING; + + // GetPeerInfo might return DPNERR_CONNECTING when connecting, + // so just keep calling it if it does + while( hr == DPNERR_CONNECTING ) + hr = m_directPlayPeer->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 ); + + if( hr == DPNERR_BUFFERTOOSMALL ) + { + QScopedArrayPointer memPtr(new unsigned char[dwSize]); + pdpPlayerInfo = reinterpret_cast(memPtr.data()); + if( pdpPlayerInfo == nullptr) + { + break; + } + + ZeroMemory( pdpPlayerInfo, dwSize ); + pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO); + + hr = m_directPlayPeer->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 ); + if( SUCCEEDED(hr)) + { + if (pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL) + m_playerLocal = pCreatePlayerMsg->dpnidPlayer; + else + { + // The first connecting player should be the user + if (m_playerUser == 0) + { + m_playerUser = pCreatePlayerMsg->dpnidPlayer; + + } + } + } + } + + break; + } + + case DPN_MSGID_RECEIVE: + { + PDPNMSG_RECEIVE pReceiveMsg = static_cast(msgBuffer); + + // Proceeed only, if the sender is our local player + if (pReceiveMsg->dpnidSender == m_playerUser) + { + QByteArray messageData = QByteArray((char*)pReceiveMsg->pReceiveData, pReceiveMsg->dwReceiveDataSize); + + emit customPacketReceived(messageData); + } + break; + } + + case DPN_MSGID_ENUM_HOSTS_RESPONSE: + { + PDPNMSG_ENUM_HOSTS_RESPONSE enumHostsResponseMsg = static_cast(msgBuffer); + const DPN_APPLICATION_DESC *applicationDescription = enumHostsResponseMsg->pApplicationDescription; + + QMutexLocker locker(&m_mutexHostList); + + auto iterator = std::find_if(m_hostNodeList.begin(), m_hostNodeList.end(), [&] (const CHostNode &hostNode) + { + return applicationDescription->guidInstance == hostNode.getApplicationDesc().guidInstance; + }); + + if(iterator == m_hostNodeList.end()) + { + + // This host session is not in the list then so insert it. + CHostNode hostNode; + HRESULT hr; + + // Copy the Host Address + if( FAILED( hr = enumHostsResponseMsg->pAddressSender->Duplicate( hostNode.getHostAddressPtr() ) ) ) + { + qWarning() << "Failed to duplicate host address!"; + return hr; + } + + DPN_APPLICATION_DESC appDesc; + + ZeroMemory(&appDesc, sizeof(DPN_APPLICATION_DESC)); + memcpy(&appDesc, applicationDescription, sizeof(DPN_APPLICATION_DESC)); + + // Null out all the pointers we aren't copying + appDesc.pwszSessionName = nullptr; + appDesc.pwszPassword = nullptr; + appDesc.pvReservedData = nullptr; + appDesc.dwReservedDataSize = 0; + appDesc.pvApplicationReservedData = nullptr; + appDesc.dwApplicationReservedDataSize = 0; + //hostNode.setApplicationDesc(appDesc); + hostNode.setApplicationDesc(appDesc); + hostNode.setSessionName(QString::fromWCharArray(applicationDescription->pwszSessionName)); + m_hostNodeList.append(hostNode); + } + break; + } + + } + + return hr; + } + + HRESULT CDirectPlayPeer::initDirectPlay() + { + HRESULT hr = S_OK; + + // Init COM so we can use CoCreateInstance + CoInitializeEx(nullptr, COINIT_MULTITHREADED); + + // Create the IDirectPlay8Peer Object + if( FAILED( hr = CoCreateInstance(CLSID_DirectPlay8Peer, + nullptr, + CLSCTX_INPROC_SERVER, + IID_IDirectPlay8Peer, + reinterpret_cast(&m_directPlayPeer) ) ) ) + { + qWarning() << "Failed to create DirectPlay8Peer object!"; + return hr; + } + + // Init DirectPlay + if( FAILED( hr = m_directPlayPeer->Initialize(&m_callbackWrapper, m_callbackWrapper.messageHandler, 0 ) ) ) + { + qWarning() << "Failed to initialize directplay peer!"; + return hr; + } + + // Ensure that TCP/IP is a valid Service Provider + if( !isServiceProviderValid( &CLSID_DP8SP_TCPIP ) ) + { + hr = E_FAIL; + qWarning() << "Service provider is invalid!"; + return hr; + } + + return hr; + } + + bool CDirectPlayPeer::isServiceProviderValid(const GUID* /*pGuidSP*/) + { + HRESULT hr = S_OK; + DWORD dwItems = 0; + DWORD dwSize = 0; + + // The first call is to retrieve the size of the DPN_SERVICE_PROVIDER_INFO array + hr = m_directPlayPeer->EnumServiceProviders(&CLSID_DP8SP_TCPIP, nullptr, nullptr, &dwSize, &dwItems, 0); + + if( hr != DPNERR_BUFFERTOOSMALL) + { + qWarning() << "Failed to enumerate service providers!"; + return false; + } + + // Allocating an array with new DPN_SERVICE_PROVIDER_INFO[items] does not work, because the struct has + // several pointers in it. Hence EnumServiceProviders tells us how much memory it exactly needs. + QScopedArrayPointer memPtr(new unsigned char[dwSize]); + DPN_SERVICE_PROVIDER_INFO* dpnSPInfo = reinterpret_cast(memPtr.data()); + + if( FAILED( hr = m_directPlayPeer->EnumServiceProviders(&CLSID_DP8SP_TCPIP, nullptr, dpnSPInfo, &dwSize, &dwItems, 0 ) ) ) + { + qWarning() << "Failed to enumerate service providers!"; + return false; + } + + // There are no items returned so the requested SP is not available + if( dwItems == 0) hr = E_FAIL; + + if( SUCCEEDED(hr) ) return true; + else return false; + } + + HRESULT CDirectPlayPeer::createDeviceAddress() + { + HRESULT hr = S_OK; + + // Create our IDirectPlay8Address Device Address + if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Address, nullptr, + CLSCTX_INPROC_SERVER, + IID_IDirectPlay8Address, + reinterpret_cast(&m_deviceAddress) ) ) ) + { + qWarning() << "Failed to create DirectPlay8Address instance!"; + return hr; + } + + // Set the SP for our Device Address + if( FAILED( hr = m_deviceAddress->SetSP( &CLSID_DP8SP_TCPIP ) ) ) + { + qWarning() << "Failed to set SP!"; + return hr; + } + + return hr; + } + + HRESULT CDirectPlayPeer::sendMessage( const QByteArray &message) + { + HRESULT hr = S_OK; + DPN_BUFFER_DESC dpBufferDesc; + + if( ( dpBufferDesc.dwBufferSize = message.size() ) == 0 ) return S_FALSE; + + dpBufferDesc.pBufferData = (BYTE*)message.data(); + + // If m_playerUser is non zero, send it only to him + if( FAILED( hr = m_directPlayPeer->SendTo( m_playerUser, + &dpBufferDesc, + 1, 0, + nullptr, nullptr, + DPNSEND_SYNC | DPNSEND_NOLOOPBACK ) ) ) + { + qWarning() << "Failed to send message!"; + } + + return hr; + } + } +} diff --git a/src/plugins/simulator/fs9/directplay_peer.h b/src/plugins/simulator/fs9/directplay_peer.h new file mode 100644 index 000000000..855702fbb --- /dev/null +++ b/src/plugins/simulator/fs9/directplay_peer.h @@ -0,0 +1,95 @@ +/* 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. + */ + +#ifndef BLACKSIMPLUGIN_FS9_DIRECTPLAY_PEER_H +#define BLACKSIMPLUGIN_FS9_DIRECTPLAY_PEER_H + +#include "fs9.h" +#include "host_node.h" +#include "callback_wrapper.h" +#include +#include +#include +#include +#include +#include +#include + +namespace BlackSimPlugin +{ + namespace Fs9 + { + //! DirectPlay peer implementation + class CDirectPlayPeer : public QObject + { + Q_OBJECT + + public: + + //! Constructor + CDirectPlayPeer(const QString &callsign, QObject *parent = nullptr); + + //! Destructor + virtual ~CDirectPlayPeer(); + + //! Returns users DirectPlay ID + DPNID getPlayerUserId() const { return m_playerUser; } + + //! Sets users DirectPlay ID + void setPlayerUserId(DPNID id) { m_playerUser = id; } + + public slots: + + //! Initialize DirectPlay host + virtual void init() = 0; + + //! Send a custom DirectPlay message + HRESULT sendMessage(const QByteArray &data); + + signals: + + //! Received custom FS9 packet + void customPacketReceived(const QByteArray &data); + + protected: + + //! DirectPlay message handler + HRESULT directPlayMessageHandler(DWORD messageId, void *msgBuffer); + + //! Initialize DirectPlay + HRESULT initDirectPlay(); + + //! Returns true of the service provider is a valid on this machine + bool isServiceProviderValid(const GUID *pGuidSP); + + //! Creates a new DirectPlay device address + HRESULT createDeviceAddress(); + + QString m_callsign; //!< Peer callsign + + IDirectPlay8Peer *m_directPlayPeer = nullptr; //!< DirectPlay peer address + IDirectPlay8Address *m_deviceAddress = nullptr; //!< DirectPlay device address + + QList m_hostNodeList; //!< List of enumerated hosts + quint32 m_packetIndex = 0; //!< Multiplayer packet index + + // DirectPlay Player Id's + std::atomic m_playerLocal; //!< Local player Id + // We need the Id of the users player, because we are sending packets only to him + std::atomic m_playerUser; //!< User player Id + + QMutex m_mutexHostList; //!< Host list mutex + + typedef CallbackWrapper TCallbackWrapper; //!< DirectPlay peer message handler wrapper + TCallbackWrapper m_callbackWrapper; //!< Callback wrapper + }; + } +} + +#endif // guard