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:
parent
61e8d23da8
commit
e7f5d0f68d
@ -55,7 +55,7 @@ DirectPlay8Peer::~DirectPlay8Peer()
|
|||||||
{
|
{
|
||||||
if(state != STATE_NEW)
|
if(state != STATE_NEW)
|
||||||
{
|
{
|
||||||
Close(0);
|
Close(DPNCLOSE_IMMEDIATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +108,13 @@ HRESULT DirectPlay8Peer::Initialize(PVOID CONST pvUserContext, CONST PFNDPNMESSA
|
|||||||
return DPNERR_ALREADYINITIALIZED;
|
return DPNERR_ALREADYINITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WSADATA wd;
|
||||||
|
if(WSAStartup(MAKEWORD(2,2), &wd) != 0)
|
||||||
|
{
|
||||||
|
log_printf("WSAStartup() failed");
|
||||||
|
return DPNERR_GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
message_handler = pfn;
|
message_handler = pfn;
|
||||||
message_handler_ctx = pvUserContext;
|
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(udp_socket_event, [this]() { handle_udp_socket_event(); });
|
||||||
worker_pool->add_handle(other_socket_event, [this]() { handle_other_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;
|
state = STATE_INITIALISED;
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@ -1368,30 +1368,28 @@ HRESULT DirectPlay8Peer::GetLocalHostAddresses(IDirectPlay8Address** CONST prgpA
|
|||||||
}
|
}
|
||||||
|
|
||||||
HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
|
HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
|
||||||
{
|
|
||||||
if(state == STATE_NEW)
|
|
||||||
{
|
|
||||||
return DPNERR_UNINITIALIZED;
|
|
||||||
}
|
|
||||||
|
|
||||||
CancelAsyncOperation(0, DPNCANCEL_ALL_OPERATIONS);
|
|
||||||
|
|
||||||
/* TODO: Wait properly. */
|
|
||||||
|
|
||||||
while(1)
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(lock);
|
std::unique_lock<std::mutex> l(lock);
|
||||||
if(host_enums.empty())
|
|
||||||
|
bool was_connected = false;
|
||||||
|
|
||||||
|
switch(state)
|
||||||
{
|
{
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sleep(50);
|
/* Signal all EnumHosts() calls to complete. */
|
||||||
|
for(auto ei = host_enums.begin(); ei != host_enums.end(); ++ei)
|
||||||
|
{
|
||||||
|
ei->second.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete worker_pool;
|
|
||||||
worker_pool = NULL;
|
|
||||||
|
|
||||||
if(discovery_socket != -1)
|
if(discovery_socket != -1)
|
||||||
{
|
{
|
||||||
closesocket(discovery_socket);
|
closesocket(discovery_socket);
|
||||||
@ -1410,6 +1408,88 @@ HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
|
|||||||
udp_socket = -1;
|
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();
|
WSACleanup();
|
||||||
|
|
||||||
state = STATE_NEW;
|
state = STATE_NEW;
|
||||||
@ -1427,6 +1507,8 @@ HRESULT DirectPlay8Peer::EnumHosts(PDPN_APPLICATION_DESC CONST pApplicationDesc,
|
|||||||
try {
|
try {
|
||||||
if(dwFlags & DPNENUMHOSTS_SYNC)
|
if(dwFlags & DPNENUMHOSTS_SYNC)
|
||||||
{
|
{
|
||||||
|
/* TODO: Instantiate synchronous instances in host_enums. */
|
||||||
|
|
||||||
HRESULT result;
|
HRESULT result;
|
||||||
|
|
||||||
HostEnumerator he(
|
HostEnumerator he(
|
||||||
@ -1474,6 +1556,8 @@ HRESULT DirectPlay8Peer::EnumHosts(PDPN_APPLICATION_DESC CONST pApplicationDesc,
|
|||||||
|
|
||||||
std::unique_lock<std::mutex> l(lock);
|
std::unique_lock<std::mutex> l(lock);
|
||||||
host_enums.erase(handle);
|
host_enums.erase(handle);
|
||||||
|
|
||||||
|
host_enum_completed.notify_all();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return DPNSUCCESS_PENDING;
|
return DPNSUCCESS_PENDING;
|
||||||
@ -1719,6 +1803,11 @@ void DirectPlay8Peer::handle_udp_socket_event()
|
|||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> l(lock);
|
std::unique_lock<std::mutex> l(lock);
|
||||||
|
|
||||||
|
if(udp_socket == -1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct sockaddr_in from_addr;
|
struct sockaddr_in from_addr;
|
||||||
int fa_len = sizeof(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.pvPlayerContext = peer->player_ctx;
|
||||||
dp.dwReason = destroy_player_reason;
|
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;
|
peer->state = Peer::PS_CLOSING;
|
||||||
|
|
||||||
l.unlock();
|
l.unlock();
|
||||||
@ -2446,6 +2540,8 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock<std::mutex> &l, unsigned int
|
|||||||
|
|
||||||
peers.erase(peer_id);
|
peers.erase(peer_id);
|
||||||
delete peer;
|
delete peer;
|
||||||
|
|
||||||
|
peer_destroyed.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Immediately close all sockets and erase all peers. */
|
/* 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):
|
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)
|
bool DirectPlay8Peer::Peer::enable_events(long events)
|
||||||
|
@ -34,11 +34,13 @@ class DirectPlay8Peer: public IDirectPlay8Peer
|
|||||||
STATE_CONNECTING,
|
STATE_CONNECTING,
|
||||||
STATE_CONNECT_FAILED,
|
STATE_CONNECT_FAILED,
|
||||||
STATE_CONNECTED,
|
STATE_CONNECTED,
|
||||||
|
STATE_CLOSING,
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
AsyncHandleAllocator handle_alloc;
|
AsyncHandleAllocator handle_alloc;
|
||||||
|
|
||||||
std::map<DPNHANDLE, HostEnumerator> host_enums;
|
std::map<DPNHANDLE, HostEnumerator> host_enums;
|
||||||
|
std::condition_variable host_enum_completed;
|
||||||
|
|
||||||
GUID instance_guid;
|
GUID instance_guid;
|
||||||
GUID application_guid;
|
GUID application_guid;
|
||||||
@ -53,7 +55,7 @@ class DirectPlay8Peer: public IDirectPlay8Peer
|
|||||||
uint32_t local_ip;
|
uint32_t local_ip;
|
||||||
uint16_t local_port;
|
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 listener_socket; /* TCP listener socket. */
|
||||||
int discovery_socket; /* Discovery UDP sockets, RECIEVES broadcasts only. */
|
int discovery_socket; /* Discovery UDP sockets, RECIEVES broadcasts only. */
|
||||||
|
|
||||||
@ -154,14 +156,16 @@ class DirectPlay8Peer: public IDirectPlay8Peer
|
|||||||
|
|
||||||
unsigned int next_peer_id;
|
unsigned int next_peer_id;
|
||||||
std::map<unsigned int, Peer*> peers;
|
std::map<unsigned int, Peer*> peers;
|
||||||
|
std::condition_variable peer_destroyed;
|
||||||
|
|
||||||
std::map<DPNID, unsigned int> player_to_peer_id;
|
std::map<DPNID, unsigned int> player_to_peer_id;
|
||||||
|
|
||||||
/* Serialises access to:
|
/* Serialises access to everything.
|
||||||
*
|
*
|
||||||
* host_enums
|
* All methods and event handlers hold this lock while executing. They will
|
||||||
* pending_peers
|
* temporarily release it when executing the application message handler, after
|
||||||
* peers
|
* 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;
|
std::mutex lock;
|
||||||
|
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <list>
|
||||||
|
#include <mutex>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "../src/DirectPlay8Address.hpp"
|
#include "../src/DirectPlay8Address.hpp"
|
||||||
#include "../src/DirectPlay8Peer.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()
|
~IDP8AddressInstance()
|
||||||
{
|
{
|
||||||
#ifdef INSTANTIATE_FROM_COM
|
#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
|
struct FoundSession
|
||||||
{
|
{
|
||||||
GUID application_guid;
|
GUID application_guid;
|
||||||
@ -2300,6 +2437,346 @@ TEST(DirectPlay8Peer, ConnectTwoPeersToHost)
|
|||||||
EXPECT_EQ(p2_cp2_dpnidPlayer, p1_player_id);
|
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)
|
TEST(DirectPlay8Peer, GetApplicationDesc)
|
||||||
{
|
{
|
||||||
const unsigned char APP_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
|
const unsigned char APP_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x04 };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user