1
0
mirror of https://github.com/solemnwarning/directplay-lite synced 2024-12-30 16:45:37 +01:00

Proper implementation of IDirectPlay8Peer::Close()

This commit is contained in:
Daniel Collins 2018-10-11 12:26:20 +01:00
parent 61e8d23da8
commit e7f5d0f68d
3 changed files with 608 additions and 31 deletions

View File

@ -55,7 +55,7 @@ DirectPlay8Peer::~DirectPlay8Peer()
{
if(state != STATE_NEW)
{
Close(0);
Close(DPNCLOSE_IMMEDIATE);
}
}
@ -108,6 +108,13 @@ HRESULT DirectPlay8Peer::Initialize(PVOID CONST pvUserContext, CONST PFNDPNMESSA
return DPNERR_ALREADYINITIALIZED;
}
WSADATA wd;
if(WSAStartup(MAKEWORD(2,2), &wd) != 0)
{
log_printf("WSAStartup() failed");
return DPNERR_GENERIC;
}
message_handler = pfn;
message_handler_ctx = pvUserContext;
@ -116,13 +123,6 @@ HRESULT DirectPlay8Peer::Initialize(PVOID CONST pvUserContext, CONST PFNDPNMESSA
worker_pool->add_handle(udp_socket_event, [this]() { handle_udp_socket_event(); });
worker_pool->add_handle(other_socket_event, [this]() { handle_other_socket_event(); });
WSADATA wd;
if(WSAStartup(MAKEWORD(2,2), &wd) != 0)
{
log_printf("WSAStartup() failed");
return DPNERR_GENERIC;
}
state = STATE_INITIALISED;
return S_OK;
@ -1369,29 +1369,27 @@ HRESULT DirectPlay8Peer::GetLocalHostAddresses(IDirectPlay8Address** CONST prgpA
HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
{
if(state == STATE_NEW)
std::unique_lock<std::mutex> l(lock);
bool was_connected = false;
switch(state)
{
return DPNERR_UNINITIALIZED;
case STATE_NEW: return DPNERR_UNINITIALIZED;
case STATE_INITIALISED: break;
case STATE_HOSTING: was_connected = true; break;
case STATE_CONNECTING: break;
case STATE_CONNECT_FAILED: break;
case STATE_CONNECTED: was_connected = true; break;
case STATE_CLOSING: return DPNERR_ALREADYCLOSING;
}
CancelAsyncOperation(0, DPNCANCEL_ALL_OPERATIONS);
/* TODO: Wait properly. */
while(1)
/* Signal all EnumHosts() calls to complete. */
for(auto ei = host_enums.begin(); ei != host_enums.end(); ++ei)
{
std::unique_lock<std::mutex> l(lock);
if(host_enums.empty())
{
break;
}
Sleep(50);
ei->second.cancel();
}
delete worker_pool;
worker_pool = NULL;
if(discovery_socket != -1)
{
closesocket(discovery_socket);
@ -1410,6 +1408,88 @@ HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
udp_socket = -1;
}
state = STATE_CLOSING;
if(dwFlags & DPNCLOSE_IMMEDIATE)
{
while(!peers.empty())
{
unsigned int peer_id = peers.begin()->first;
peer_destroy(l, peer_id, DPNERR_USERCANCEL, DPNDESTROYPLAYERREASON_NORMAL);
}
}
else{
for(auto pi = peers.begin(); pi != peers.end();)
{
unsigned int peer_id = pi->first;
Peer *peer = pi->second;
if(peer->state == Peer::PS_CONNECTED)
{
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(dp);
dp.dpnidPlayer = peer->player_id;
dp.pvPlayerContext = peer->player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_NORMAL;
peer->state = Peer::PS_CLOSING;
/* Wake up a worker to deal with closing the connection. */
SetEvent(peer->event);
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
pi = peers.begin();
}
else if(peer->state == Peer::PS_CLOSING)
{
/* Do nothing. We're waiting for this peer to go away. */
++pi;
}
else{
peer_destroy(l, peer_id, DPNERR_USERCANCEL, DPNDESTROYPLAYERREASON_NORMAL);
pi = peers.begin();
}
}
/* Wait for remaining peers to finish disconnecting. */
peer_destroyed.wait(l, [this]() { return peers.empty(); });
}
if(was_connected)
{
/* Raise a DPNMSG_DESTROY_PLAYER for ourself. */
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = local_player_id;
dp.pvPlayerContext = local_player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_NORMAL;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
}
/* Wait for outstanding EnumHosts() calls. */
host_enum_completed.wait(l, [this]() { return host_enums.empty(); });
/* We need to release the lock while the worker_pool destructor runs so that any worker
* threads waiting for it can finish. No other thread should mess with it while we are in
* STATE_CLOSING and we have no open sockets.
*/
l.unlock();
delete worker_pool;
l.lock();
worker_pool = NULL;
WSACleanup();
state = STATE_NEW;
@ -1427,6 +1507,8 @@ HRESULT DirectPlay8Peer::EnumHosts(PDPN_APPLICATION_DESC CONST pApplicationDesc,
try {
if(dwFlags & DPNENUMHOSTS_SYNC)
{
/* TODO: Instantiate synchronous instances in host_enums. */
HRESULT result;
HostEnumerator he(
@ -1474,6 +1556,8 @@ HRESULT DirectPlay8Peer::EnumHosts(PDPN_APPLICATION_DESC CONST pApplicationDesc,
std::unique_lock<std::mutex> l(lock);
host_enums.erase(handle);
host_enum_completed.notify_all();
}));
return DPNSUCCESS_PENDING;
@ -1719,6 +1803,11 @@ void DirectPlay8Peer::handle_udp_socket_event()
{
std::unique_lock<std::mutex> l(lock);
if(udp_socket == -1)
{
return;
}
struct sockaddr_in from_addr;
int fa_len = sizeof(from_addr);
@ -2419,6 +2508,11 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock<std::mutex> &l, unsigned int
dp.pvPlayerContext = peer->player_ctx;
dp.dwReason = destroy_player_reason;
/* Bodge to prevent io_peer_send() initiating a graceful shutdown
* while the application is handling the DPNMSG_DESTROY_PLAYER.
*/
peer->send_open = false;
peer->state = Peer::PS_CLOSING;
l.unlock();
@ -2446,6 +2540,8 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock<std::mutex> &l, unsigned int
peers.erase(peer_id);
delete peer;
peer_destroyed.notify_all();
}
/* Immediately close all sockets and erase all peers. */
@ -3434,7 +3530,7 @@ void DirectPlay8Peer::connect_fail(std::unique_lock<std::mutex> &l, HRESULT hRes
}
DirectPlay8Peer::Peer::Peer(enum PeerState state, int sock, uint32_t ip, uint16_t port):
state(state), sock(sock), ip(ip), port(port), recv_busy(false), recv_buf_cur(0), events(0), sq(event), next_ack_id(1)
state(state), sock(sock), ip(ip), port(port), recv_busy(false), recv_buf_cur(0), events(0), sq(event), send_open(true), next_ack_id(1)
{}
bool DirectPlay8Peer::Peer::enable_events(long events)

View File

@ -34,11 +34,13 @@ class DirectPlay8Peer: public IDirectPlay8Peer
STATE_CONNECTING,
STATE_CONNECT_FAILED,
STATE_CONNECTED,
STATE_CLOSING,
} state;
AsyncHandleAllocator handle_alloc;
std::map<DPNHANDLE, HostEnumerator> host_enums;
std::condition_variable host_enum_completed;
GUID instance_guid;
GUID application_guid;
@ -53,7 +55,7 @@ class DirectPlay8Peer: public IDirectPlay8Peer
uint32_t local_ip;
uint16_t local_port;
int udp_socket; /* UDP socket, used for general send/recv operations. */
int udp_socket; /* UDP socket, used for non-guaranteed send/recv operations. */
int listener_socket; /* TCP listener socket. */
int discovery_socket; /* Discovery UDP sockets, RECIEVES broadcasts only. */
@ -154,14 +156,16 @@ class DirectPlay8Peer: public IDirectPlay8Peer
unsigned int next_peer_id;
std::map<unsigned int, Peer*> peers;
std::condition_variable peer_destroyed;
std::map<DPNID, unsigned int> player_to_peer_id;
/* Serialises access to:
/* Serialises access to everything.
*
* host_enums
* pending_peers
* peers
* All methods and event handlers hold this lock while executing. They will
* temporarily release it when executing the application message handler, after
* which they must reclaim it and check for any changes to the state which may
* affect them, such as a peer being destroyed.
*/
std::mutex lock;

View File

@ -2,7 +2,10 @@
#include <array>
#include <functional>
#include <gtest/gtest.h>
#include <list>
#include <mutex>
#include <stdexcept>
#include <stdint.h>
#include "../src/DirectPlay8Address.hpp"
#include "../src/DirectPlay8Peer.hpp"
@ -91,6 +94,15 @@ class IDP8AddressInstance
}
}
IDP8AddressInstance(GUID service_provider, DWORD port): IDP8AddressInstance()
{
if(instance->SetSP(&service_provider) != S_OK
|| instance->AddComponent(DPNA_KEY_PORT, &port, sizeof(DWORD), DPNA_DATATYPE_DWORD) != S_OK)
{
throw std::runtime_error("Address setup failed");
}
}
~IDP8AddressInstance()
{
#ifdef INSTANTIATE_FROM_COM
@ -217,6 +229,131 @@ struct SessionHost
}
};
class TestPeer
{
public:
DPNID first_cp_dpnidPlayer;
DPNID first_cc_dpnidLocal;
private:
const char *ident;
bool expecting;
std::list< std::function<HRESULT(DWORD,PVOID)> > callbacks;
std::mutex lock;
IDirectPlay8Peer *instance;
static HRESULT CALLBACK callback(PVOID pvUserContext, DWORD dwMessageType, PVOID pMessage)
{
TestPeer *t = (TestPeer*)(pvUserContext);
std::unique_lock<std::mutex> l(t->lock);
if(t->expecting)
{
if(!t->callbacks.empty())
{
auto callback = t->callbacks.front();
t->callbacks.pop_front();
l.unlock();
return callback(dwMessageType, pMessage);
}
else{
ADD_FAILURE() << "[" << t->ident << "] Unexpected message with type " << dwMessageType;
return DPN_OK;
}
}
if(dwMessageType == DPN_MSGID_CREATE_PLAYER)
{
DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage);
if(t->first_cp_dpnidPlayer == -1)
{
t->first_cp_dpnidPlayer = cp->dpnidPlayer;
}
/* Invert the player ID to serve as a default context pointer. */
cp->pvPlayerContext = (void*)~(uintptr_t)(cp->dpnidPlayer);
}
else if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE)
{
DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage);
if(t->first_cc_dpnidLocal == -1)
{
t->first_cc_dpnidLocal = cc->dpnidLocal;
}
}
return DPN_OK;
}
public:
TestPeer(const char *ident):
first_cp_dpnidPlayer(-1),
first_cc_dpnidLocal(-1),
ident(ident),
expecting(false)
{
#ifdef INSTANTIATE_FROM_COM
CoInitialize(NULL);
CoCreateInstance(CLSID_DirectPlay8Peer, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay8Peer, (void**)(&instance));
#else
instance = new DirectPlay8Peer(NULL);
#endif
if(instance->Initialize(this, &callback, 0) != S_OK)
{
throw std::runtime_error("IDirectPlay8Peer->Initialize() failed");
}
}
~TestPeer()
{
#ifdef INSTANTIATE_FROM_COM
instance->Release();
CoUninitialize();
#else
instance->Release();
#endif
}
IDirectPlay8Peer &operator*()
{
return *instance;
}
IDirectPlay8Peer *operator->()
{
return instance;
}
void expect_begin()
{
std::unique_lock<std::mutex> l(lock);
expecting = true;
}
void expect_push(const std::function<HRESULT(DWORD,PVOID)> &callback)
{
std::unique_lock<std::mutex> l(lock);
callbacks.push_back(callback);
}
void expect_end()
{
std::unique_lock<std::mutex> l(lock);
expecting = false;
EXPECT_TRUE(callbacks.empty());
callbacks.clear();
}
};
struct FoundSession
{
GUID application_guid;
@ -2300,6 +2437,346 @@ TEST(DirectPlay8Peer, ConnectTwoPeersToHost)
EXPECT_EQ(p2_cp2_dpnidPlayer, p1_player_id);
}
TEST(DirectPlay8Peer, NonHostPeerSoftClose)
{
DPN_APPLICATION_DESC app_desc;
memset(&app_desc, 0, sizeof(app_desc));
app_desc.dwSize = sizeof(app_desc);
app_desc.guidApplication = APP_GUID_1;
app_desc.pwszSessionName = L"Session 1";
IDP8AddressInstance host_addr(CLSID_DP8SP_TCPIP, PORT);
TestPeer host("host");
ASSERT_EQ(host->Host(&app_desc, &(host_addr.instance), 1, NULL, NULL, 0, 0), S_OK);
IDP8AddressInstance connect_addr(CLSID_DP8SP_TCPIP, L"127.0.0.1", PORT);
TestPeer peer1("peer1");
ASSERT_EQ(peer1->Connect(
&app_desc, /* pdnAppDesc */
connect_addr, /* pHostAddr */
NULL, /* pDeviceInfo */
NULL, /* pdnSecurity */
NULL, /* pdnCredentials */
NULL, /* pvUserConnectData */
0, /* dwUserConnectDataSize */
0, /* pvPlayerContext */
NULL, /* pvAsyncContext */
NULL, /* phAsyncHandle */
DPNCONNECT_SYNC /* dwFlags */
), S_OK);
TestPeer peer2("peer2");
ASSERT_EQ(peer2->Connect(
&app_desc, /* pdnAppDesc */
connect_addr, /* pHostAddr */
NULL, /* pDeviceInfo */
NULL, /* pdnSecurity */
NULL, /* pdnCredentials */
NULL, /* pvUserConnectData */
0, /* dwUserConnectDataSize */
0, /* pvPlayerContext */
NULL, /* pvAsyncContext */
NULL, /* phAsyncHandle */
DPNCONNECT_SYNC /* dwFlags */
), S_OK);
Sleep(100);
host.expect_begin();
host.expect_push([&peer2](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_EQ(dp->dpnidPlayer, peer2.first_cc_dpnidLocal);
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_NORMAL);
}
return DPN_OK;
});
peer1.expect_begin();
peer1.expect_push([&peer2](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_EQ(dp->dpnidPlayer, peer2.first_cc_dpnidLocal);
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_NORMAL);
}
return DPN_OK;
});
DPNID p2_dp_dpnidPlayer1;
DPNID p2_dp_dpnidPlayer2;
peer2.expect_begin();
peer2.expect_push([&host, &peer1, &peer2, &p2_dp_dpnidPlayer1](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_TRUE((dp->dpnidPlayer == host.first_cp_dpnidPlayer || dp->dpnidPlayer == peer1.first_cc_dpnidLocal))
<< "(dpnidPlayer = " << dp->dpnidPlayer
<< ", host = " << host.first_cp_dpnidPlayer
<< ", peer1 = " << peer1.first_cc_dpnidLocal
<< ", peer2 = " << peer2.first_cc_dpnidLocal << ")";
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_NORMAL);
p2_dp_dpnidPlayer1 = dp->dpnidPlayer;
}
return DPN_OK;
});
peer2.expect_push([&host, &peer1, &peer2, &p2_dp_dpnidPlayer2](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_TRUE((dp->dpnidPlayer == host.first_cp_dpnidPlayer || dp->dpnidPlayer == peer1.first_cc_dpnidLocal))
<< "(dpnidPlayer = " << dp->dpnidPlayer
<< ", host = " << host.first_cp_dpnidPlayer
<< ", peer1 = " << peer1.first_cc_dpnidLocal
<< ", peer2 = " << peer2.first_cc_dpnidLocal << ")";
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_NORMAL);
p2_dp_dpnidPlayer2 = dp->dpnidPlayer;
}
return DPN_OK;
});
peer2.expect_push([&host, &peer1, &peer2](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_TRUE((dp->dpnidPlayer == peer2.first_cc_dpnidLocal))
<< "(dpnidPlayer = " << dp->dpnidPlayer
<< ", host = " << host.first_cp_dpnidPlayer
<< ", peer1 = " << peer1.first_cc_dpnidLocal
<< ", peer2 = " << peer2.first_cc_dpnidLocal << ")";
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_NORMAL);
}
return DPN_OK;
});
peer2->Close(0);
Sleep(100);
peer2.expect_end();
peer1.expect_end();
host.expect_end();
EXPECT_NE(p2_dp_dpnidPlayer1, p2_dp_dpnidPlayer2);
}
TEST(DirectPlay8Peer, NonHostPeerHardClose)
{
DPN_APPLICATION_DESC app_desc;
memset(&app_desc, 0, sizeof(app_desc));
app_desc.dwSize = sizeof(app_desc);
app_desc.guidApplication = APP_GUID_1;
app_desc.pwszSessionName = L"Session 1";
IDP8AddressInstance host_addr(CLSID_DP8SP_TCPIP, PORT);
TestPeer host("host");
ASSERT_EQ(host->Host(&app_desc, &(host_addr.instance), 1, NULL, NULL, 0, 0), S_OK);
IDP8AddressInstance connect_addr(CLSID_DP8SP_TCPIP, L"127.0.0.1", PORT);
TestPeer peer1("peer1");
ASSERT_EQ(peer1->Connect(
&app_desc, /* pdnAppDesc */
connect_addr, /* pHostAddr */
NULL, /* pDeviceInfo */
NULL, /* pdnSecurity */
NULL, /* pdnCredentials */
NULL, /* pvUserConnectData */
0, /* dwUserConnectDataSize */
0, /* pvPlayerContext */
NULL, /* pvAsyncContext */
NULL, /* phAsyncHandle */
DPNCONNECT_SYNC /* dwFlags */
), S_OK);
TestPeer peer2("peer2");
ASSERT_EQ(peer2->Connect(
&app_desc, /* pdnAppDesc */
connect_addr, /* pHostAddr */
NULL, /* pDeviceInfo */
NULL, /* pdnSecurity */
NULL, /* pdnCredentials */
NULL, /* pvUserConnectData */
0, /* dwUserConnectDataSize */
0, /* pvPlayerContext */
NULL, /* pvAsyncContext */
NULL, /* phAsyncHandle */
DPNCONNECT_SYNC /* dwFlags */
), S_OK);
Sleep(100);
host.expect_begin();
host.expect_push([&peer2](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_EQ(dp->dpnidPlayer, peer2.first_cc_dpnidLocal);
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_CONNECTIONLOST);
}
return DPN_OK;
});
peer1.expect_begin();
peer1.expect_push([&peer2](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_EQ(dp->dpnidPlayer, peer2.first_cc_dpnidLocal);
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_CONNECTIONLOST);
}
return DPN_OK;
});
DPNID p2_dp_dpnidPlayer1;
DPNID p2_dp_dpnidPlayer2;
peer2.expect_begin();
peer2.expect_push([&host, &peer1, &peer2, &p2_dp_dpnidPlayer1](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_TRUE((dp->dpnidPlayer == host.first_cp_dpnidPlayer || dp->dpnidPlayer == peer1.first_cc_dpnidLocal))
<< "(dpnidPlayer = " << dp->dpnidPlayer
<< ", host = " << host.first_cp_dpnidPlayer
<< ", peer1 = " << peer1.first_cc_dpnidLocal
<< ", peer2 = " << peer2.first_cc_dpnidLocal << ")";
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_NORMAL);
p2_dp_dpnidPlayer1 = dp->dpnidPlayer;
}
return DPN_OK;
});
peer2.expect_push([&host, &peer1, &peer2, &p2_dp_dpnidPlayer2](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_TRUE((dp->dpnidPlayer == host.first_cp_dpnidPlayer || dp->dpnidPlayer == peer1.first_cc_dpnidLocal))
<< "(dpnidPlayer = " << dp->dpnidPlayer
<< ", host = " << host.first_cp_dpnidPlayer
<< ", peer1 = " << peer1.first_cc_dpnidLocal
<< ", peer2 = " << peer2.first_cc_dpnidLocal << ")";
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_NORMAL);
p2_dp_dpnidPlayer2 = dp->dpnidPlayer;
}
return DPN_OK;
});
peer2.expect_push([&host, &peer1, &peer2](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_PLAYER);
if(dwMessageType == DPN_MSGID_DESTROY_PLAYER)
{
DPNMSG_DESTROY_PLAYER *dp = (DPNMSG_DESTROY_PLAYER*)(pMessage);
EXPECT_EQ(dp->dwSize, sizeof(DPNMSG_DESTROY_PLAYER));
EXPECT_TRUE((dp->dpnidPlayer == peer2.first_cc_dpnidLocal))
<< "(dpnidPlayer = " << dp->dpnidPlayer
<< ", host = " << host.first_cp_dpnidPlayer
<< ", peer1 = " << peer1.first_cc_dpnidLocal
<< ", peer2 = " << peer2.first_cc_dpnidLocal << ")";
EXPECT_EQ(dp->pvPlayerContext, (void*)~(uintptr_t)(dp->dpnidPlayer));
EXPECT_EQ(dp->dwReason, DPNDESTROYPLAYERREASON_NORMAL);
}
return DPN_OK;
});
peer2->Close(DPNCLOSE_IMMEDIATE);
Sleep(100);
peer2.expect_end();
peer1.expect_end();
host.expect_end();
EXPECT_NE(p2_dp_dpnidPlayer1, p2_dp_dpnidPlayer2);
}
TEST(DirectPlay8Peer, GetApplicationDesc)
{
const unsigned char APP_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };