diff --git a/src/DirectPlay8Peer.cpp b/src/DirectPlay8Peer.cpp index c46d591..d956909 100644 --- a/src/DirectPlay8Peer.cpp +++ b/src/DirectPlay8Peer.cpp @@ -940,30 +940,156 @@ HRESULT DirectPlay8Peer::SetPeerInfo(CONST DPN_PLAYER_INFO* CONST pdpnPlayerInfo } } - /* TODO: Send update to peers. */ - - if(dwFlags & DPNSETPEERINFO_SYNC) + /* If called before creating or joining a session, SetPeerInfo() returns S_OK and + * doesn't fire any local messages. + */ + if(state != STATE_HOSTING && state != STATE_CONNECTED) { return S_OK; } + + std::function&, HRESULT)> op_finished_cb; + + unsigned int sync_pending = 0; + unsigned int *pending = &sync_pending; + + std::condition_variable sync_cv; + HRESULT sync_result = S_OK; + + DPNHANDLE async_handle = handle_alloc.new_pinfo(); + HRESULT *async_result = NULL; + + if(dwFlags & DPNSETPEERINFO_SYNC) + { + op_finished_cb = [this, pending, &sync_result, &sync_cv](std::unique_lock &l, HRESULT result) + { + if(result != S_OK && sync_result == S_OK) + { + /* Error code from the first failure wins. */ + sync_result = result; + } + + if(--(*pending) == 0) + { + sync_cv.notify_one(); + } + }; + } else{ - DPNHANDLE handle = handle_alloc.new_pinfo(); - if(phAsyncHandle != NULL) { - *phAsyncHandle = handle; + *phAsyncHandle = async_handle; } - DPNMSG_ASYNC_OP_COMPLETE oc; - memset(&oc, 0, sizeof(oc)); + pending = new unsigned int(0); + async_result = new HRESULT(S_OK); - oc.dwSize = sizeof(oc); - oc.hAsyncOp = handle; - oc.pvUserContext = pvAsyncContext; - oc.hResultCode = S_OK; + op_finished_cb = [this, pending, async_result, async_handle, pvAsyncContext](std::unique_lock &l, HRESULT result) + { + if(result != S_OK && *async_result == S_OK) + { + /* Error code from the first failure wins. */ + *async_result = result; + } + + if(--(*pending) == 0) + { + DPNMSG_ASYNC_OP_COMPLETE oc; + memset(&oc, 0, sizeof(oc)); + + oc.dwSize = sizeof(oc); + oc.hAsyncOp = async_handle; + oc.pvUserContext = pvAsyncContext; + oc.hResultCode = *async_result; + + delete async_result; + delete pending; + + l.unlock(); + message_handler(message_handler_ctx, DPN_MSGID_ASYNC_OP_COMPLETE, &oc); + l.lock(); + } + }; + } + + for(auto pi = peers.begin(); pi != peers.end(); ++pi) + { + unsigned int peer_id = pi->first; + Peer *peer = pi->second; + + if(peer->state != Peer::PS_CONNECTED) + { + continue; + } + + DWORD ack_id = peer->alloc_ack_id(); + + PacketSerialiser playerinfo(DPLITE_MSGID_PLAYERINFO); + + playerinfo.append_dword(local_player_id); + playerinfo.append_wstring(local_player_name); + playerinfo.append_data(local_player_data.data(), local_player_data.size()); + playerinfo.append_dword(ack_id); + + pi->second->sq.send(SendQueue::SEND_PRI_MEDIUM, playerinfo, NULL, + [this, op_finished_cb, peer_id, ack_id] + (std::unique_lock &l, HRESULT result) + { + if(result == S_OK) + { + /* DPLITE_MSGID_PLAYERINFO has been sent, register the op + * to handle the response. + */ + Peer *peer = get_peer_by_peer_id(peer_id); + assert(peer != NULL); + + peer->register_ack(ack_id, [op_finished_cb](std::unique_lock &l, HRESULT result) + { + op_finished_cb(l, result); + }); + } + else{ + op_finished_cb(l, result); + } + }); + + ++(*pending); + } + + /* Bodge to dispatch a DPNMSG_ASYNC_OP_COMPLETE if we didn't have any + * peers to notify. None of the send callbacks could have touched + * pending by this point since we haven't released the lock. + */ + bool no_peers_to_notify = (*pending == 0); + + { + /* Notify the local instance it just changed its own peer info... which the + * official DirectX does for some reason. + */ + + DPNMSG_PEER_INFO pi; + memset(&pi, 0, sizeof(pi)); + + pi.dwSize = sizeof(DPNMSG_PEER_INFO); + pi.dpnidPeer = local_player_id; + pi.pvPlayerContext = local_player_ctx; l.unlock(); - message_handler(message_handler_ctx, DPN_MSGID_ASYNC_OP_COMPLETE, &oc); + message_handler(message_handler_ctx, DPN_MSGID_PEER_INFO, &pi); + l.lock(); + } + + if(dwFlags & DPNSETPEERINFO_SYNC) + { + sync_cv.wait(l, [pending]() { return (*pending == 0); }); + return sync_result; + } + else{ + if(no_peers_to_notify) + { + ++(*pending); + op_finished_cb(l, S_OK); + } return DPNSUCCESS_PENDING; } @@ -988,8 +1114,8 @@ HRESULT DirectPlay8Peer::GetPeerInfo(CONST DPNID dpnid, DPN_PLAYER_INFO* CONST p return DPNERR_INVALIDPLAYER; } - UNIMPLEMENTED("DirectPlay8Peer::GetPeerInfo"); - return DPNERR_GENERIC; + name = &(peer->player_name); + data = &(peer->player_data); } size_t needed_buf_size = sizeof(DPN_PLAYER_INFO) + data->size() + ((name->length() + !name->empty()) * sizeof(wchar_t)); @@ -1480,6 +1606,9 @@ void DirectPlay8Peer::io_peer_connected(std::unique_lock &l, unsigne connect_host.append_null(); } + connect_host.append_wstring(local_player_name); + connect_host.append_data(local_player_data.data(), local_player_data.size()); + peer->sq.send(SendQueue::SEND_PRI_MEDIUM, connect_host, NULL, @@ -1656,6 +1785,18 @@ void DirectPlay8Peer::io_peer_recv(std::unique_lock &l, unsigned int break; } + case DPLITE_MSGID_PLAYERINFO: + { + handle_playerinfo(l, peer_id, *pd); + break; + } + + case DPLITE_MSGID_ACK: + { + handle_ack(l, peer_id, *pd); + break; + } + default: /* TODO: Log "unrecognised packet type" */ break; @@ -1774,6 +1915,8 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock &l, unsigned int while((peer = get_peer_by_peer_id(peer_id)) != NULL) { + /* Cancel any outstanding sends and notify the callbacks. */ + SendQueue::SendOp *sqop; if((sqop = peer->sq.get_pending()) != NULL) { @@ -1789,6 +1932,23 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock &l, unsigned int continue; } + /* Fail any outstanding acks and notify the callbacks. */ + + if(!peer->pending_acks.empty()) + { + auto ai = peer->pending_acks.begin(); + + std::function&, HRESULT)> callback = ai->second; + peer->pending_acks.erase(ai); + + callback(l, outstanding_op_result); + + /* Return to the start in case the peer has gone away while the lock was + * released to run the callback. + */ + continue; + } + if(peer->state == Peer::PS_CONNECTED) { /* TODO: DPN_MSGID_DESTROY_PLAYER? */ @@ -2007,6 +2167,17 @@ void DirectPlay8Peer::handle_host_connect_request(std::unique_lock & return; } + peer->player_name = pd.get_wstring(4); + + peer->player_data.clear(); + + std::pair player_data = pd.get_data(5); + + peer->player_data.reserve(player_data.second); + peer->player_data.insert(peer->player_data.end(), + (const unsigned char*)(player_data.first), + (const unsigned char*)(player_data.first) + player_data.second); + DPNMSG_INDICATE_CONNECT ic; memset(&ic, 0, sizeof(ic)); @@ -2080,6 +2251,9 @@ void DirectPlay8Peer::handle_host_connect_request(std::unique_lock & connect_host_ok.append_null(); } + connect_host_ok.append_wstring(local_player_name); + connect_host_ok.append_data(local_player_data.data(), local_player_data.size()); + peer->sq.send(SendQueue::SEND_PRI_MEDIUM, connect_host_ok, NULL, @@ -2155,11 +2329,13 @@ void DirectPlay8Peer::handle_host_connect_ok(std::unique_lock &l, un abort(); } + int after_peers_base = 4 + (n_other_peers * 3); + connect_reply_data.clear(); - if(!pd.is_null(4 + (n_other_peers * 3))) + if(!pd.is_null(after_peers_base + 0)) { - std::pair d = pd.get_data(4 + (n_other_peers * 3)); + std::pair d = pd.get_data(after_peers_base + 0); if(d.second > 0) { @@ -2170,6 +2346,17 @@ void DirectPlay8Peer::handle_host_connect_ok(std::unique_lock &l, un } } + peer->player_name = pd.get_wstring(after_peers_base + 1); + + peer->player_data.clear(); + + std::pair player_data = pd.get_data(after_peers_base + 2); + + peer->player_data.reserve(player_data.second); + peer->player_data.insert(peer->player_data.end(), + (const unsigned char*)(player_data.first), + (const unsigned char*)(player_data.first) + player_data.second); + peer->state = Peer::PS_CONNECTED; { @@ -2291,6 +2478,85 @@ void DirectPlay8Peer::handle_message(std::unique_lock &l, const Pack } } +void DirectPlay8Peer::handle_playerinfo(std::unique_lock &l, unsigned int peer_id, const PacketDeserialiser &pd) +{ + Peer *peer = get_peer_by_peer_id(peer_id); + assert(peer != NULL); + + try { + DPNID player_id = pd.get_dword(0); + std::wstring name = pd.get_wstring(1); + std::pair data = pd.get_data(2); + DWORD ack_id = pd.get_dword(3); + + if(peer->state != Peer::PS_CONNECTED || player_id != peer->player_id) + { + /* TODO: LOG ME */ + return; + } + + peer->player_name = name; + + peer->player_data.clear(); + peer->player_data.insert(peer->player_data.end(), + (const unsigned char*)(data.first), + (const unsigned char*)(data.first) + data.second); + + /* TODO: Should we send DPLITE_MSGID_ACK before or after the callback + * completes? + */ + + PacketSerialiser ack(DPLITE_MSGID_ACK); + ack.append_dword(ack_id); + ack.append_dword(S_OK); + + peer->sq.send(SendQueue::SEND_PRI_HIGH, ack, NULL, + [](std::unique_lock &l, HRESULT s_result) {}); + + DPNMSG_PEER_INFO pi; + memset(&pi, 0, sizeof(pi)); + + pi.dwSize = sizeof(pi); + pi.dpnidPeer = peer->player_id; + pi.pvPlayerContext = peer->player_ctx; + + l.unlock(); + message_handler(message_handler_ctx, DPN_MSGID_PEER_INFO, &pi); + l.lock(); + } + catch(const PacketDeserialiser::Error &e) + { + /* TODO: LOG ME */ + } +} + +void DirectPlay8Peer::handle_ack(std::unique_lock &l, unsigned int peer_id, const PacketDeserialiser &pd) +{ + Peer *peer = get_peer_by_peer_id(peer_id); + assert(peer != NULL); + + try { + DWORD ack_id = pd.get_dword(0); + HRESULT result = pd.get_dword(1); + + auto ai = peer->pending_acks.find(ack_id); + if(ai == peer->pending_acks.end()) + { + /* TODO: LOG ME */ + return; + } + + std::function&, HRESULT)> callback = ai->second; + peer->pending_acks.erase(ai); + + callback(l, result); + } + catch(const PacketDeserialiser::Error &e) + { + /* TODO: LOG ME */ + } +} + /* Check if we have finished connecting and should enter STATE_CONNECTED. * * This is called after processing either of: @@ -2388,5 +2654,22 @@ void DirectPlay8Peer::connect_fail(std::unique_lock &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), sq(event) + state(state), sock(sock), ip(ip), port(port), recv_busy(false), recv_buf_cur(0), sq(event), next_ack_id(1) {} + +DWORD DirectPlay8Peer::Peer::alloc_ack_id() +{ + DWORD id = next_ack_id++; + if(next_ack_id == 0) + { + ++next_ack_id; + } + + return id; +} + +void DirectPlay8Peer::Peer::register_ack(DWORD id, const std::function&, HRESULT)> &callback) +{ + assert(pending_acks.find(id) == pending_acks.end()); + pending_acks.emplace(id, callback); +} diff --git a/src/DirectPlay8Peer.hpp b/src/DirectPlay8Peer.hpp index fc86e85..1a28b52 100644 --- a/src/DirectPlay8Peer.hpp +++ b/src/DirectPlay8Peer.hpp @@ -111,6 +111,9 @@ class DirectPlay8Peer: public IDirectPlay8Peer DPNID player_id; /* Player ID, not initialised before state PS_CONNECTED. */ void *player_ctx; /* Player context, not initialised before state PS_CONNECTED. */ + std::wstring player_name; + std::vector player_data; + bool recv_busy; unsigned char recv_buf[MAX_PACKET_SIZE]; size_t recv_buf_cur; @@ -119,7 +122,17 @@ class DirectPlay8Peer: public IDirectPlay8Peer SendQueue sq; + /* Some messages require confirmation of success/failure from the other + * peer. Each of these is assigned a rolling (per peer) ID, the callback + * associated to which is called when we get a DPLITE_MSGID_ACK. + */ + DWORD next_ack_id; + std::map< DWORD, std::function&, HRESULT)> > pending_acks; + Peer(enum PeerState state, int sock, uint32_t ip, uint16_t port); + + DWORD alloc_ack_id(); + void register_ack(DWORD id, const std::function&, HRESULT)> &callback); }; DPNID local_player_id; @@ -177,6 +190,8 @@ class DirectPlay8Peer: public IDirectPlay8Peer void handle_host_connect_ok(std::unique_lock &l, unsigned int peer_id, const PacketDeserialiser &pd); void handle_host_connect_fail(std::unique_lock &l, unsigned int peer_id, const PacketDeserialiser &pd); void handle_message(std::unique_lock &l, const PacketDeserialiser &pd); + void handle_playerinfo(std::unique_lock &l, unsigned int peer_id, const PacketDeserialiser &pd); + void handle_ack(std::unique_lock &l, unsigned int peer_id, const PacketDeserialiser &pd); void connect_check(std::unique_lock &l); void connect_fail(std::unique_lock &l, HRESULT hResultCode, const void *pvApplicationReplyData, DWORD dwApplicationReplyDataSize); diff --git a/src/Messages.hpp b/src/Messages.hpp index b382924..ca0810d 100644 --- a/src/Messages.hpp +++ b/src/Messages.hpp @@ -34,6 +34,8 @@ * GUID - Application GUID * WSTRING | NULL - Password * DATA | NULL - Request data + * WSTRING - Player name (empty = none) + * DATA - Player data (empty = none) */ #define DPLITE_MSGID_CONNECT_HOST_OK 4 @@ -51,6 +53,8 @@ * DWORD - Port (host byte order) * * DATA | NULL - Response data + * WSTRING - Host player name (empty = none) + * DATA - Host player data (empty = none) */ #define DPLITE_MSGID_CONNECT_HOST_FAIL 5 @@ -71,4 +75,22 @@ * DWORD - Flags (DPNSEND_GUARANTEED, DPNSEND_COALESCE, DPNSEND_COMPLETEONPROCESS) */ +#define DPLITE_MSGID_PLAYERINFO 7 + +/* Player info has been updated by the peer using the SetPeerInfo() method. + * + * DWORD - Player ID, will always be that of the sending peer. + * WSTRING - Player name (empty = none) + * DATA - Player data (empty = none) + * DWORD - Operation ID to return in DPLITE_MSGID_OP_COMPLETE +*/ + +#define DPLITE_MSGID_ACK 8 + +/* The peer has completed processing a message. + * + * DWORD - Ack ID, should be unique to the peer, for a time. + * DWORD - Result code (normally S_OK) +*/ + #endif /* !DPLITE_MESSAGES_HPP */ diff --git a/tests/DirectPlay8Peer.cpp b/tests/DirectPlay8Peer.cpp index 33510b7..27bff79 100644 --- a/tests/DirectPlay8Peer.cpp +++ b/tests/DirectPlay8Peer.cpp @@ -261,6 +261,30 @@ static void EXPECT_SESSIONS(std::map got, const } } +static void EXPECT_PEERINFO(IDirectPlay8Peer *instance, DPNID player_id, const wchar_t *name, const void *data, size_t data_size, DWORD flags) +{ + DWORD buf_size = 0; + + ASSERT_EQ(instance->GetPeerInfo(player_id, NULL, &buf_size, 0), DPNERR_BUFFERTOOSMALL); + EXPECT_EQ(buf_size, sizeof(DPN_PLAYER_INFO) + ((wcslen(name) + 1) * sizeof(wchar_t)) + data_size); + + std::vector buffer(buf_size); + DPN_PLAYER_INFO *info = (DPN_PLAYER_INFO*)(buffer.data()); + + info->dwSize = sizeof(DPN_PLAYER_INFO); + + ASSERT_EQ(instance->GetPeerInfo(player_id, info, &buf_size, 0), S_OK); + + EXPECT_EQ(info->dwSize, sizeof(DPN_PLAYER_INFO)); + EXPECT_EQ(info->dwInfoFlags, (DPNINFO_NAME | DPNINFO_DATA)); + EXPECT_EQ(info->dwPlayerFlags, (flags)); + + EXPECT_EQ(std::wstring(info->pwszName), std::wstring(name)); + + EXPECT_EQ(std::string((const char*)(info->pvData), info->dwDataSize), + std::string((const char*)(data), data_size)); +} + TEST(DirectPlay8Peer, EnumHostsSync) { SessionHost a1s1(APP_GUID_1, L"Application 1 Session 1"); @@ -3114,3 +3138,1037 @@ TEST(DirectPlay8Peer, SyncSendToHostToNone) testing = false; } + +TEST(DirectPlay8Peer, SetPeerInfoSyncBeforeHost) +{ + std::atomic testing(false); + + std::atomic host_seq(0), p1_seq(0); + DPNID host_player_id = -1, p1_player_id = -1; + + std::function host_cb = + [&testing, &host_seq, &host_player_id] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CREATE_PLAYER && host_player_id == -1) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + host_player_id = cp->dpnidPlayer; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++host_seq; + + switch(seq) + { + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance host; + + ASSERT_EQ(host->Initialize(&host_cb, &callback_shim, 0), S_OK); + + const wchar_t *HOST_NAME = L"Da Host"; + const unsigned char HOST_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x00, 0xAA, 0xBB, 0xCC }; + + { + DPN_PLAYER_INFO info; + memset(&info, 0, sizeof(info)); + + info.dwSize = sizeof(info); + info.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA; + info.pwszName = (wchar_t*)(HOST_NAME); + info.pvData = (void*)(HOST_DATA); + info.dwDataSize = sizeof(HOST_DATA); + + ASSERT_EQ(host->SetPeerInfo(&info, NULL, NULL, DPNSETPEERINFO_SYNC), S_OK); + } + + { + 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 = (wchar_t*)(L"Session 1"); + + IDP8AddressInstance addr; + + DWORD port = PORT; + + ASSERT_EQ(addr->SetSP(&CLSID_DP8SP_TCPIP), S_OK); + ASSERT_EQ(addr->AddComponent(DPNA_KEY_PORT, &port, sizeof(DWORD), DPNA_DATATYPE_DWORD), S_OK); + + ASSERT_EQ(host->Host(&app_desc, &(addr.instance), 1, NULL, NULL, (void*)(0xB00), 0), S_OK); + } + + std::function p1_cb = + [&testing, &p1_seq, &p1_player_id] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE) + { + DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage); + p1_player_id = cc->dpnidLocal; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++p1_seq; + + switch(seq) + { + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance p1; + + ASSERT_EQ(p1->Initialize(&p1_cb, &callback_shim, 0), S_OK); + + DPN_APPLICATION_DESC connect_to_app; + memset(&connect_to_app, 0, sizeof(connect_to_app)); + + connect_to_app.dwSize = sizeof(connect_to_app); + connect_to_app.guidApplication = APP_GUID_1; + + IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT); + + ASSERT_EQ(p1->Connect( + &connect_to_app, /* pdnAppDesc */ + connect_to_addr, /* pHostAddr */ + NULL, /* pDeviceInfo */ + NULL, /* pdnSecurity */ + NULL, /* pdnCredentials */ + NULL, /* pvUserConnectData */ + 0, /* dwUserConnectDataSize */ + NULL, /* pvPlayerContext */ + NULL, /* pvAsyncContext */ + NULL, /* phAsyncHandle */ + DPNCONNECT_SYNC /* dwFlags */ + ), S_OK); + + /* Give everything a moment to settle. */ + Sleep(250); + + /* Check the host has its own player data. */ + EXPECT_PEERINFO(host.instance, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), (DPNPLAYER_LOCAL | DPNPLAYER_HOST)); + + /* Check the peer has the host's data. */ + EXPECT_PEERINFO(p1.instance, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), DPNPLAYER_HOST); +} + +TEST(DirectPlay8Peer, SetPeerInfoSyncAfterPeerConnects) +{ + const wchar_t *HOST_NAME = L"Da Host"; + const unsigned char HOST_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x00, 0xAA, 0xBB, 0xCC }; + + std::atomic testing(false); + + std::atomic host_seq(0), p1_seq(0); + DPNID host_player_id = -1, p1_player_id = -1; + + IDirectPlay8Peer *hostp; + + std::function host_cb = + [&testing, &host_seq, &host_player_id, &hostp, &HOST_NAME, &HOST_DATA] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CREATE_PLAYER && host_player_id == -1) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + host_player_id = cp->dpnidPlayer; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++host_seq; + + switch(seq) + { + case 1: + { + EXPECT_EQ(dwMessageType, DPN_MSGID_PEER_INFO); + + if(dwMessageType == DPN_MSGID_PEER_INFO) + { + DPNMSG_PEER_INFO *pi = (DPNMSG_PEER_INFO*)(pMessage); + + EXPECT_EQ(pi->dwSize, sizeof(DPNMSG_PEER_INFO)); + EXPECT_EQ(pi->dpnidPeer, host_player_id); + EXPECT_EQ(pi->pvPlayerContext, (void*)(0xB00)); + + EXPECT_PEERINFO(hostp, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), (DPNPLAYER_LOCAL | DPNPLAYER_HOST)); + } + + break; + } + + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance host; + hostp = host.instance; + + ASSERT_EQ(host->Initialize(&host_cb, &callback_shim, 0), S_OK); + + { + 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 = (wchar_t*)(L"Session 1"); + + IDP8AddressInstance addr; + + DWORD port = PORT; + + ASSERT_EQ(addr->SetSP(&CLSID_DP8SP_TCPIP), S_OK); + ASSERT_EQ(addr->AddComponent(DPNA_KEY_PORT, &port, sizeof(DWORD), DPNA_DATATYPE_DWORD), S_OK); + + ASSERT_EQ(host->Host(&app_desc, &(addr.instance), 1, NULL, NULL, (void*)(0xB00), 0), S_OK); + } + + IDirectPlay8Peer *p1p; + + std::function p1_cb = + [&testing, &p1_seq, &p1_player_id, &host_player_id, &p1p, &HOST_NAME, &HOST_DATA] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE) + { + DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage); + p1_player_id = cc->dpnidLocal; + } + else if(dwMessageType == DPN_MSGID_CREATE_PLAYER) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + + if(cp->dpnidPlayer == host_player_id) + { + cp->pvPlayerContext = (void*)(0x1234); + } + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++p1_seq; + + switch(seq) + { + case 1: + { + EXPECT_EQ(dwMessageType, DPN_MSGID_PEER_INFO); + + if(dwMessageType == DPN_MSGID_PEER_INFO) + { + DPNMSG_PEER_INFO *pi = (DPNMSG_PEER_INFO*)(pMessage); + + EXPECT_EQ(pi->dwSize, sizeof(DPNMSG_PEER_INFO)); + EXPECT_EQ(pi->dpnidPeer, host_player_id); + EXPECT_EQ(pi->pvPlayerContext, (void*)(0x1234)); + + EXPECT_PEERINFO(p1p, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), DPNPLAYER_HOST); + } + + break; + } + + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance p1; + p1p = p1.instance; + + ASSERT_EQ(p1->Initialize(&p1_cb, &callback_shim, 0), S_OK); + + DPN_APPLICATION_DESC connect_to_app; + memset(&connect_to_app, 0, sizeof(connect_to_app)); + + connect_to_app.dwSize = sizeof(connect_to_app); + connect_to_app.guidApplication = APP_GUID_1; + + IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT); + + ASSERT_EQ(p1->Connect( + &connect_to_app, /* pdnAppDesc */ + connect_to_addr, /* pHostAddr */ + NULL, /* pDeviceInfo */ + NULL, /* pdnSecurity */ + NULL, /* pdnCredentials */ + NULL, /* pvUserConnectData */ + 0, /* dwUserConnectDataSize */ + NULL, /* pvPlayerContext */ + NULL, /* pvAsyncContext */ + NULL, /* phAsyncHandle */ + DPNCONNECT_SYNC /* dwFlags */ + ), S_OK); + + /* Give everything a moment to settle. */ + Sleep(250); + + testing = true; + + { + + DPN_PLAYER_INFO info; + memset(&info, 0, sizeof(info)); + + info.dwSize = sizeof(info); + info.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA; + info.pwszName = (wchar_t*)(HOST_NAME); + info.pvData = (void*)(HOST_DATA); + info.dwDataSize = sizeof(HOST_DATA); + + ASSERT_EQ(host->SetPeerInfo(&info, NULL, NULL, DPNSETPEERINFO_SYNC), S_OK); + } + + Sleep(250); + + /* Check the host has its own player data. */ + EXPECT_PEERINFO(host.instance, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), (DPNPLAYER_LOCAL | DPNPLAYER_HOST)); + + /* Check the peer has the host's data. */ + EXPECT_PEERINFO(p1.instance, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), DPNPLAYER_HOST); + + testing = false; + + EXPECT_EQ(host_seq, 1); + EXPECT_EQ(p1_seq, 1); +} + +TEST(DirectPlay8Peer, SetPeerInfoSyncBeforeConnect) +{ + std::atomic testing(false); + + std::atomic host_seq(0), p1_seq(0); + DPNID host_player_id = -1, p1_player_id = -1; + + std::function host_cb = + [&testing, &host_seq, &host_player_id] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CREATE_PLAYER && host_player_id == -1) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + host_player_id = cp->dpnidPlayer; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++host_seq; + + switch(seq) + { + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance host; + + ASSERT_EQ(host->Initialize(&host_cb, &callback_shim, 0), S_OK); + + { + 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 = (wchar_t*)(L"Session 1"); + + IDP8AddressInstance addr; + + DWORD port = PORT; + + ASSERT_EQ(addr->SetSP(&CLSID_DP8SP_TCPIP), S_OK); + ASSERT_EQ(addr->AddComponent(DPNA_KEY_PORT, &port, sizeof(DWORD), DPNA_DATATYPE_DWORD), S_OK); + + ASSERT_EQ(host->Host(&app_desc, &(addr.instance), 1, NULL, NULL, (void*)(0xB00), 0), S_OK); + } + + std::function p1_cb = + [&testing, &p1_seq, &p1_player_id] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE) + { + DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage); + p1_player_id = cc->dpnidLocal; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++p1_seq; + + switch(seq) + { + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance p1; + + ASSERT_EQ(p1->Initialize(&p1_cb, &callback_shim, 0), S_OK); + + const wchar_t *P1_NAME = L"Not Da Host"; + const unsigned char P1_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x00, 0xAA, 0xBB, 0xCC }; + + { + DPN_PLAYER_INFO info; + memset(&info, 0, sizeof(info)); + + info.dwSize = sizeof(info); + info.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA; + info.pwszName = (wchar_t*)(P1_NAME); + info.pvData = (void*)(P1_DATA); + info.dwDataSize = sizeof(P1_DATA); + + ASSERT_EQ(p1->SetPeerInfo(&info, NULL, NULL, DPNSETPEERINFO_SYNC), S_OK); + } + + DPN_APPLICATION_DESC connect_to_app; + memset(&connect_to_app, 0, sizeof(connect_to_app)); + + connect_to_app.dwSize = sizeof(connect_to_app); + connect_to_app.guidApplication = APP_GUID_1; + + IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT); + + ASSERT_EQ(p1->Connect( + &connect_to_app, /* pdnAppDesc */ + connect_to_addr, /* pHostAddr */ + NULL, /* pDeviceInfo */ + NULL, /* pdnSecurity */ + NULL, /* pdnCredentials */ + NULL, /* pvUserConnectData */ + 0, /* dwUserConnectDataSize */ + NULL, /* pvPlayerContext */ + NULL, /* pvAsyncContext */ + NULL, /* phAsyncHandle */ + DPNCONNECT_SYNC /* dwFlags */ + ), S_OK); + + /* Give everything a moment to settle. */ + Sleep(250); + + /* Check the host has the peer's data. */ + EXPECT_PEERINFO(host.instance, p1_player_id, + P1_NAME, P1_DATA, sizeof(P1_DATA), 0); + + /* Check the peer has its own data. */ + EXPECT_PEERINFO(p1.instance, p1_player_id, + P1_NAME, P1_DATA, sizeof(P1_DATA), DPNPLAYER_LOCAL); +} + +TEST(DirectPlay8Peer, SetPeerInfoSyncAfterConnect) +{ + const wchar_t *P1_NAME = L"Not Da Host"; + const unsigned char P1_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x00, 0xAA, 0xBB, 0xCC }; + + std::atomic testing(false); + + std::atomic host_seq(0), p1_seq(0); + DPNID host_player_id = -1, p1_player_id = -1; + + IDirectPlay8Peer *hostp; + + std::function host_cb = + [&testing, &host_seq, &host_player_id, &p1_player_id, &hostp, &P1_NAME, &P1_DATA] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CREATE_PLAYER && host_player_id == -1) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + host_player_id = cp->dpnidPlayer; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++host_seq; + + switch(seq) + { + case 1: + { + EXPECT_EQ(dwMessageType, DPN_MSGID_PEER_INFO); + + if(dwMessageType == DPN_MSGID_PEER_INFO) + { + DPNMSG_PEER_INFO *pi = (DPNMSG_PEER_INFO*)(pMessage); + + EXPECT_EQ(pi->dwSize, sizeof(DPNMSG_PEER_INFO)); + EXPECT_EQ(pi->dpnidPeer, p1_player_id); + EXPECT_EQ(pi->pvPlayerContext, (void*)(0x0000)); + + EXPECT_PEERINFO(hostp, p1_player_id, + P1_NAME, P1_DATA, sizeof(P1_DATA), 0); + } + + break; + } + + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance host; + hostp = host.instance; + + ASSERT_EQ(host->Initialize(&host_cb, &callback_shim, 0), S_OK); + + { + 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 = (wchar_t*)(L"Session 1"); + + IDP8AddressInstance addr; + + DWORD port = PORT; + + ASSERT_EQ(addr->SetSP(&CLSID_DP8SP_TCPIP), S_OK); + ASSERT_EQ(addr->AddComponent(DPNA_KEY_PORT, &port, sizeof(DWORD), DPNA_DATATYPE_DWORD), S_OK); + + ASSERT_EQ(host->Host(&app_desc, &(addr.instance), 1, NULL, NULL, (void*)(0xB00), 0), S_OK); + } + + IDirectPlay8Peer *p1p; + + std::function p1_cb = + [&testing, &p1_seq, &p1_player_id, &p1p, &P1_NAME, &P1_DATA] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE) + { + DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage); + p1_player_id = cc->dpnidLocal; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++p1_seq; + + switch(seq) + { + case 1: + { + EXPECT_EQ(dwMessageType, DPN_MSGID_PEER_INFO); + + if(dwMessageType == DPN_MSGID_PEER_INFO) + { + DPNMSG_PEER_INFO *pi = (DPNMSG_PEER_INFO*)(pMessage); + + EXPECT_EQ(pi->dwSize, sizeof(DPNMSG_PEER_INFO)); + EXPECT_EQ(pi->dpnidPeer, p1_player_id); + EXPECT_EQ(pi->pvPlayerContext, (void*)(0x0000)); + + EXPECT_PEERINFO(p1p, p1_player_id, + P1_NAME, P1_DATA, sizeof(P1_DATA), DPNPLAYER_LOCAL); + } + + break; + } + + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance p1; + p1p = p1.instance; + + ASSERT_EQ(p1->Initialize(&p1_cb, &callback_shim, 0), S_OK); + + DPN_APPLICATION_DESC connect_to_app; + memset(&connect_to_app, 0, sizeof(connect_to_app)); + + connect_to_app.dwSize = sizeof(connect_to_app); + connect_to_app.guidApplication = APP_GUID_1; + + IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT); + + ASSERT_EQ(p1->Connect( + &connect_to_app, /* pdnAppDesc */ + connect_to_addr, /* pHostAddr */ + NULL, /* pDeviceInfo */ + NULL, /* pdnSecurity */ + NULL, /* pdnCredentials */ + NULL, /* pvUserConnectData */ + 0, /* dwUserConnectDataSize */ + NULL, /* pvPlayerContext */ + NULL, /* pvAsyncContext */ + NULL, /* phAsyncHandle */ + DPNCONNECT_SYNC /* dwFlags */ + ), S_OK); + + /* Give everything a moment to settle. */ + Sleep(250); + + testing = true; + + { + + DPN_PLAYER_INFO info; + memset(&info, 0, sizeof(info)); + + info.dwSize = sizeof(info); + info.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA; + info.pwszName = (wchar_t*)(P1_NAME); + info.pvData = (void*)(P1_DATA); + info.dwDataSize = sizeof(P1_DATA); + + ASSERT_EQ(p1->SetPeerInfo(&info, NULL, NULL, DPNSETPEERINFO_SYNC), S_OK); + } + + Sleep(250); + + /* Check the host has the peer's data. */ + EXPECT_PEERINFO(host.instance, p1_player_id, + P1_NAME, P1_DATA, sizeof(P1_DATA), 0); + + /* Check the peer has its own data. */ + EXPECT_PEERINFO(p1.instance, p1_player_id, + P1_NAME, P1_DATA, sizeof(P1_DATA), DPNPLAYER_LOCAL); + + testing = false; + + EXPECT_EQ(host_seq, 1); + EXPECT_EQ(p1_seq, 1); +} + +TEST(DirectPlay8Peer, SetPeerInfoAsyncBeforeHost) +{ + std::atomic testing(false); + + std::atomic host_seq(0), p1_seq(0); + DPNID host_player_id = -1, p1_player_id = -1; + + std::function host_cb = + [&testing, &host_seq, &host_player_id] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CREATE_PLAYER && host_player_id == -1) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + host_player_id = cp->dpnidPlayer; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++host_seq; + + switch(seq) + { + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance host; + + ASSERT_EQ(host->Initialize(&host_cb, &callback_shim, 0), S_OK); + + const wchar_t *HOST_NAME = L"Da Host"; + const unsigned char HOST_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x00, 0xAA, 0xBB, 0xCC }; + + { + testing = true; + + DPN_PLAYER_INFO info; + memset(&info, 0, sizeof(info)); + + info.dwSize = sizeof(info); + info.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA; + info.pwszName = (wchar_t*)(HOST_NAME); + info.pvData = (void*)(HOST_DATA); + info.dwDataSize = sizeof(HOST_DATA); + + /* SetPeerInfo() behaves as if called synchronously if called asynchronously + * before joining or creating a session. + */ + + DPNHANDLE setinfo_handle; + ASSERT_EQ(host->SetPeerInfo(&info, NULL, &setinfo_handle, 0), S_OK); + + Sleep(250); + + testing = false; + } + + { + 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 = (wchar_t*)(L"Session 1"); + + IDP8AddressInstance addr; + + DWORD port = PORT; + + ASSERT_EQ(addr->SetSP(&CLSID_DP8SP_TCPIP), S_OK); + ASSERT_EQ(addr->AddComponent(DPNA_KEY_PORT, &port, sizeof(DWORD), DPNA_DATATYPE_DWORD), S_OK); + + ASSERT_EQ(host->Host(&app_desc, &(addr.instance), 1, NULL, NULL, (void*)(0xB00), 0), S_OK); + } + + std::function p1_cb = + [&testing, &p1_seq, &p1_player_id] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE) + { + DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage); + p1_player_id = cc->dpnidLocal; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++p1_seq; + + switch(seq) + { + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance p1; + + ASSERT_EQ(p1->Initialize(&p1_cb, &callback_shim, 0), S_OK); + + DPN_APPLICATION_DESC connect_to_app; + memset(&connect_to_app, 0, sizeof(connect_to_app)); + + connect_to_app.dwSize = sizeof(connect_to_app); + connect_to_app.guidApplication = APP_GUID_1; + + IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT); + + ASSERT_EQ(p1->Connect( + &connect_to_app, /* pdnAppDesc */ + connect_to_addr, /* pHostAddr */ + NULL, /* pDeviceInfo */ + NULL, /* pdnSecurity */ + NULL, /* pdnCredentials */ + NULL, /* pvUserConnectData */ + 0, /* dwUserConnectDataSize */ + NULL, /* pvPlayerContext */ + NULL, /* pvAsyncContext */ + NULL, /* phAsyncHandle */ + DPNCONNECT_SYNC /* dwFlags */ + ), S_OK); + + /* Give everything a moment to settle. */ + Sleep(250); + + /* Check the host has its own player data. */ + EXPECT_PEERINFO(host.instance, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), (DPNPLAYER_LOCAL | DPNPLAYER_HOST)); + + /* Check the peer has the host's data. */ + EXPECT_PEERINFO(p1.instance, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), DPNPLAYER_HOST); + + EXPECT_EQ(host_seq, 0); + EXPECT_EQ(p1_seq, 0); +} + +TEST(DirectPlay8Peer, SetPeerInfoAsyncAfterPeerConnects) +{ + const wchar_t *HOST_NAME = L"Da Host"; + const unsigned char HOST_DATA[] = { 0x00, 0x01, 0x02, 0x03, 0x00, 0xAA, 0xBB, 0xCC }; + + std::atomic testing(false); + + std::atomic host_seq(0), p1_seq(0); + DPNID host_player_id = -1, p1_player_id = -1; + + IDirectPlay8Peer *hostp; + DPNHANDLE setinfo_handle; + + std::function host_cb = + [&testing, &host_seq, &host_player_id, &hostp, &HOST_NAME, &HOST_DATA, &setinfo_handle] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CREATE_PLAYER && host_player_id == -1) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + host_player_id = cp->dpnidPlayer; + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++host_seq; + + switch(seq) + { + case 1: + { + EXPECT_EQ(dwMessageType, DPN_MSGID_PEER_INFO); + + if(dwMessageType == DPN_MSGID_PEER_INFO) + { + DPNMSG_PEER_INFO *pi = (DPNMSG_PEER_INFO*)(pMessage); + + EXPECT_EQ(pi->dwSize, sizeof(DPNMSG_PEER_INFO)); + EXPECT_EQ(pi->dpnidPeer, host_player_id); + EXPECT_EQ(pi->pvPlayerContext, (void*)(0xB00)); + + EXPECT_PEERINFO(hostp, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), (DPNPLAYER_LOCAL | DPNPLAYER_HOST)); + } + + break; + } + + case 2: + { + EXPECT_EQ(dwMessageType, DPN_MSGID_ASYNC_OP_COMPLETE); + + if(dwMessageType == DPN_MSGID_ASYNC_OP_COMPLETE) + { + DPNMSG_ASYNC_OP_COMPLETE *oc = (DPNMSG_ASYNC_OP_COMPLETE*)(pMessage); + + EXPECT_EQ(oc->dwSize, sizeof(DPNMSG_ASYNC_OP_COMPLETE)); + EXPECT_EQ(oc->hAsyncOp, setinfo_handle); + EXPECT_EQ(oc->pvUserContext, (void*)(0x9999)); + EXPECT_EQ(oc->hResultCode, S_OK); + } + + break; + } + + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance host; + hostp = host.instance; + + ASSERT_EQ(host->Initialize(&host_cb, &callback_shim, 0), S_OK); + + { + 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 = (wchar_t*)(L"Session 1"); + + IDP8AddressInstance addr; + + DWORD port = PORT; + + ASSERT_EQ(addr->SetSP(&CLSID_DP8SP_TCPIP), S_OK); + ASSERT_EQ(addr->AddComponent(DPNA_KEY_PORT, &port, sizeof(DWORD), DPNA_DATATYPE_DWORD), S_OK); + + ASSERT_EQ(host->Host(&app_desc, &(addr.instance), 1, NULL, NULL, (void*)(0xB00), 0), S_OK); + } + + IDirectPlay8Peer *p1p; + + std::function p1_cb = + [&testing, &p1_seq, &p1_player_id, &host_player_id, &p1p, &HOST_NAME, &HOST_DATA] + (DWORD dwMessageType, PVOID pMessage) + { + if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE) + { + DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage); + p1_player_id = cc->dpnidLocal; + } + else if(dwMessageType == DPN_MSGID_CREATE_PLAYER) + { + DPNMSG_CREATE_PLAYER *cp = (DPNMSG_CREATE_PLAYER*)(pMessage); + + if(cp->dpnidPlayer == host_player_id) + { + cp->pvPlayerContext = (void*)(0x1234); + } + } + + if(!testing) + { + return DPN_OK; + } + + int seq = ++p1_seq; + + switch(seq) + { + case 1: + { + EXPECT_EQ(dwMessageType, DPN_MSGID_PEER_INFO); + + if(dwMessageType == DPN_MSGID_PEER_INFO) + { + DPNMSG_PEER_INFO *pi = (DPNMSG_PEER_INFO*)(pMessage); + + EXPECT_EQ(pi->dwSize, sizeof(DPNMSG_PEER_INFO)); + EXPECT_EQ(pi->dpnidPeer, host_player_id); + EXPECT_EQ(pi->pvPlayerContext, (void*)(0x1234)); + + EXPECT_PEERINFO(p1p, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), DPNPLAYER_HOST); + } + + break; + } + + default: + ADD_FAILURE() << "Unexpected message of type " << dwMessageType <<", sequence " << seq; + break; + } + + return DPN_OK; + }; + + IDP8PeerInstance p1; + p1p = p1.instance; + + ASSERT_EQ(p1->Initialize(&p1_cb, &callback_shim, 0), S_OK); + + DPN_APPLICATION_DESC connect_to_app; + memset(&connect_to_app, 0, sizeof(connect_to_app)); + + connect_to_app.dwSize = sizeof(connect_to_app); + connect_to_app.guidApplication = APP_GUID_1; + + IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT); + + ASSERT_EQ(p1->Connect( + &connect_to_app, /* pdnAppDesc */ + connect_to_addr, /* pHostAddr */ + NULL, /* pDeviceInfo */ + NULL, /* pdnSecurity */ + NULL, /* pdnCredentials */ + NULL, /* pvUserConnectData */ + 0, /* dwUserConnectDataSize */ + NULL, /* pvPlayerContext */ + NULL, /* pvAsyncContext */ + NULL, /* phAsyncHandle */ + DPNCONNECT_SYNC /* dwFlags */ + ), S_OK); + + /* Give everything a moment to settle. */ + Sleep(250); + + testing = true; + + { + + DPN_PLAYER_INFO info; + memset(&info, 0, sizeof(info)); + + info.dwSize = sizeof(info); + info.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA; + info.pwszName = (wchar_t*)(HOST_NAME); + info.pvData = (void*)(HOST_DATA); + info.dwDataSize = sizeof(HOST_DATA); + + ASSERT_EQ(host->SetPeerInfo(&info, (void*)(0x9999), &setinfo_handle, 0), DPNSUCCESS_PENDING); + } + + Sleep(250); + + /* Check the host has its own player data. */ + EXPECT_PEERINFO(host.instance, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), (DPNPLAYER_LOCAL | DPNPLAYER_HOST)); + + /* Check the peer has the host's data. */ + EXPECT_PEERINFO(p1.instance, host_player_id, + HOST_NAME, HOST_DATA, sizeof(HOST_DATA), DPNPLAYER_HOST); + + testing = false; + + EXPECT_EQ(host_seq, 2); + EXPECT_EQ(p1_seq, 1); +}