From 6df47cee60643c0737b061b83e1835a0f64acf61 Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Sat, 10 Nov 2018 21:34:31 +0000 Subject: [PATCH] Handle group cleanups for session end etc --- src/DirectPlay8Peer.cpp | 132 +++++++++++++++++++++++++++++++++++++- src/DirectPlay8Peer.hpp | 3 + tests/DirectPlay8Peer.cpp | 102 +++++++++++++++++++++++++++++ 3 files changed, 235 insertions(+), 2 deletions(-) diff --git a/src/DirectPlay8Peer.cpp b/src/DirectPlay8Peer.cpp index 6afbffa..466271b 100644 --- a/src/DirectPlay8Peer.cpp +++ b/src/DirectPlay8Peer.cpp @@ -2478,6 +2478,8 @@ HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags) dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_NORMAL); } + group_destroy_all(l, DPNDESTROYGROUPREASON_NORMAL); + /* Wait for outstanding EnumHosts() calls. */ host_enum_completed.wait(l, [this]() { return async_host_enums.empty() && sync_host_enums.empty(); }); @@ -2945,6 +2947,8 @@ HRESULT DirectPlay8Peer::TerminateSession(void* CONST pvTerminateData, CONST DWO dispatch_destroy_player(l, cp->first, cp->second, DPNDESTROYPLAYERREASON_SESSIONTERMINATED); } + group_destroy_all(l, DPNDESTROYGROUPREASON_NORMAL); + /* Destroy any peers which weren't fully connected. */ for(auto dp = destroy_peers.begin(); dp != destroy_peers.end();) @@ -3788,6 +3792,8 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock &l, unsigned int auto peer_id = peers.begin()->first; peer_destroy(l, peer_id, outstanding_op_result, destroy_player_reason); } + + group_destroy_all(l, DPNDESTROYGROUPREASON_NORMAL); } RENEW_PEER_OR_RETURN(); @@ -3882,6 +3888,23 @@ void DirectPlay8Peer::peer_shutdown_all(std::unique_lock &l, HRESULT } } +void DirectPlay8Peer::group_destroy_all(std::unique_lock &l, DWORD dwReason) +{ + for(std::map::iterator g; (g = groups.begin()) != groups.end();) + { + DPNID group_id = g->first; + Group *group = &(g->second); + + if(destroyed_groups.find(group_id) == destroyed_groups.end()) + { + destroyed_groups.insert(group_id); + dispatch_destroy_group(l, group_id, group->ctx, dwReason); + } + + groups.erase(group_id); + } +} + void DirectPlay8Peer::close_main_sockets() { if(discovery_socket != -1) @@ -4581,6 +4604,8 @@ void DirectPlay8Peer::handle_connect_peer(std::unique_lock &l, unsig /* Send DPLITE_MSGID_GROUP_CREATE for each group. */ + std::set member_group_ids; + for(auto gi = groups.begin(); gi != groups.end(); ++gi) { DPNID group_id = gi->first; @@ -4599,6 +4624,11 @@ void DirectPlay8Peer::handle_connect_peer(std::unique_lock &l, unsig peer->sq.send(SendQueue::SEND_PRI_HIGH, group_create, NULL, [](std::unique_lock &l, HRESULT result) {}); + + if(group->player_ids.find(local_player_id) != group->player_ids.end()) + { + member_group_ids.insert(group_id); + } } /* Send DPLITE_MSGID_CONNECT_PEER_OK. */ @@ -4607,7 +4637,12 @@ void DirectPlay8Peer::handle_connect_peer(std::unique_lock &l, unsig connect_peer_ok.append_wstring(local_player_name); connect_peer_ok.append_data(local_player_data.data(), local_player_data.size()); - /* TODO: Include our group memberships. */ + connect_peer_ok.append_dword(member_group_ids.size()); + + for(auto i = member_group_ids.begin(); i != member_group_ids.end(); ++i) + { + connect_peer_ok.append_dword(*i); + } peer->sq.send(SendQueue::SEND_PRI_HIGH, connect_peer_ok, @@ -4655,6 +4690,14 @@ void DirectPlay8Peer::handle_connect_peer_ok(std::unique_lock &l, un (const unsigned char*)(player_data.first), (const unsigned char*)(player_data.first) + player_data.second); + DWORD peer_group_count = pd.get_dword(2); + std::set peer_groups; + + for(DWORD i = 0; i < peer_group_count; ++i) + { + peer_groups.insert(pd.get_dword(3 + i)); + } + peer->state = Peer::PS_CONNECTED; /* player_id initialised in handling of DPLITE_MSGID_CONNECT_HOST_OK. */ @@ -4677,7 +4720,46 @@ void DirectPlay8Peer::handle_connect_peer_ok(std::unique_lock &l, un peer->player_ctx = cp.pvPlayerContext; } - /* TODO: Add peer to specified groups. */ + for(auto g = peer_groups.begin(); g != peer_groups.end(); ++g) + { + DPNID group_id = *g; + + if(destroyed_groups.find(group_id) != destroyed_groups.end()) + { + /* Group is already in the process of being destroyed. */ + continue; + } + + Group *group = get_group_by_id(group_id); + if(group == NULL) + { + /* Unknown group ID... very rarely normal. */ + continue; + } + + if(group->player_ids.find(peer->player_id) != group->player_ids.end()) + { + /* Already in group... somehow?! */ + continue; + } + + group->player_ids.insert(peer->player_id); + + DPNMSG_ADD_PLAYER_TO_GROUP ap; + memset(&ap, 0, sizeof(ap)); + + ap.dwSize = sizeof(ap); + ap.dpnidGroup = group_id; + ap.pvGroupContext = group->ctx; + ap.dpnidPlayer = peer->player_id; + ap.pvPlayerContext = peer->player_ctx; + + l.unlock(); + message_handler(message_handler_ctx, DPN_MSGID_ADD_PLAYER_TO_GROUP, &ap); + l.lock(); + + RENEW_PEER_OR_RETURN(); + } connect_check(l); } @@ -4966,6 +5048,8 @@ void DirectPlay8Peer::handle_destroy_peer(std::unique_lock &l, unsig } peer_shutdown_all(l, DPNERR_HOSTTERMINATEDSESSION, DPNDESTROYPLAYERREASON_SESSIONTERMINATED); + + group_destroy_all(l, DPNDESTROYGROUPREASON_SESSIONTERMINATED); } else{ /* The host called DestroyPeer() on another peer in the session. @@ -5032,6 +5116,8 @@ void DirectPlay8Peer::handle_terminate_session(std::unique_lock &l, dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_SESSIONTERMINATED); peer_shutdown_all(l, DPNERR_HOSTTERMINATEDSESSION, DPNDESTROYPLAYERREASON_SESSIONTERMINATED); + + group_destroy_all(l, DPNDESTROYGROUPREASON_SESSIONTERMINATED); } catch(const PacketDeserialiser::Error &e) { @@ -5627,6 +5713,36 @@ HRESULT DirectPlay8Peer::dispatch_create_player(std::unique_lock &l, HRESULT DirectPlay8Peer::dispatch_destroy_player(std::unique_lock &l, DPNID dpnidPlayer, void *pvPlayerContext, DWORD dwReason) { + /* HACK: Remove the player ID from any groups it is still in. */ + for(auto g = groups.begin(); g != groups.end();) + { + DPNID group_id = g->first; + Group *group = &(g->second); + + if(group->player_ids.find(dpnidPlayer) != group->player_ids.end()) + { + group->player_ids.erase(dpnidPlayer); + + DPNMSG_REMOVE_PLAYER_FROM_GROUP rp; + memset(&rp, 0, sizeof(rp)); + + rp.dwSize = sizeof(rp); + rp.dpnidGroup = group_id; + rp.pvGroupContext = group->ctx; + rp.dpnidPlayer = dpnidPlayer; + rp.pvPlayerContext = pvPlayerContext; + + l.unlock(); + message_handler(message_handler_ctx, DPN_MSGID_REMOVE_PLAYER_FROM_GROUP, &rp); + l.lock(); + + g = groups.begin(); + } + else{ + ++g; + } + } + DPNMSG_DESTROY_PLAYER dp; memset(&dp, 0, sizeof(dp)); @@ -5638,6 +5754,18 @@ HRESULT DirectPlay8Peer::dispatch_destroy_player(std::unique_lock &l return dispatch_message(l, DPN_MSGID_DESTROY_PLAYER, &dp); } +HRESULT DirectPlay8Peer::dispatch_destroy_group(std::unique_lock &l, DPNID dpnidGroup, void *pvGroupContext, DWORD dwReason) +{ + DPNMSG_DESTROY_GROUP dg; + + dg.dwSize = sizeof(dg); + dg.dpnidGroup = dpnidGroup; + dg.pvGroupContext = pvGroupContext; + dg.dwReason = dwReason; + + return dispatch_message(l, DPN_MSGID_DESTROY_GROUP, &dg); +} + 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), send_open(true), next_ack_id(1) {} diff --git a/src/DirectPlay8Peer.hpp b/src/DirectPlay8Peer.hpp index c50e8b1..a9fb7af 100644 --- a/src/DirectPlay8Peer.hpp +++ b/src/DirectPlay8Peer.hpp @@ -218,6 +218,8 @@ class DirectPlay8Peer: public IDirectPlay8Peer void peer_shutdown(std::unique_lock &l, unsigned int peer_id, HRESULT outstanding_op_result, DWORD destroy_player_reason); void peer_shutdown_all(std::unique_lock &l, HRESULT outstanding_op_result, DWORD destroy_player_reason); + void group_destroy_all(std::unique_lock &l, DWORD dwReason); + void close_main_sockets(); void handle_host_enum_request(std::unique_lock &l, const PacketDeserialiser &pd, const struct sockaddr_in *from_addr); @@ -246,6 +248,7 @@ class DirectPlay8Peer: public IDirectPlay8Peer HRESULT dispatch_message(std::unique_lock &l, DWORD dwMessageType, PVOID pvMessage); HRESULT dispatch_create_player(std::unique_lock &l, DPNID dpnidPlayer, void **ppvPlayerContext); HRESULT dispatch_destroy_player(std::unique_lock &l, DPNID dpnidPlayer, void *pvPlayerContext, DWORD dwReason); + HRESULT dispatch_destroy_group(std::unique_lock &l, DPNID dpnidGroup, void *pvGroupContext, DWORD dwReason); public: DirectPlay8Peer(std::atomic *global_refcount); diff --git a/tests/DirectPlay8Peer.cpp b/tests/DirectPlay8Peer.cpp index 9b474f8..f27e98d 100644 --- a/tests/DirectPlay8Peer.cpp +++ b/tests/DirectPlay8Peer.cpp @@ -9477,6 +9477,108 @@ TEST(DirectPlay8Peer, DestroyGroupByTerminateSession) peer1.expect_end(); } +TEST(DirectPlay8Peer, DestroyGroupByDestroyPeer) +{ + 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); + + Sleep(100); + + DPNID p1_cg_dpnidGroup; + + peer1.expect_begin(); + peer1.expect_push([&host, &p1_cg_dpnidGroup](DWORD dwMessageType, PVOID pMessage) + { + EXPECT_EQ(dwMessageType, DPN_MSGID_CREATE_GROUP); + if(dwMessageType == DPN_MSGID_CREATE_GROUP) + { + DPNMSG_CREATE_GROUP *cg = (DPNMSG_CREATE_GROUP*)(pMessage); + cg->pvGroupContext = (void*)(0xBCDE); + + p1_cg_dpnidGroup = cg->dpnidGroup; + } + + return S_OK; + }); + + DPN_GROUP_INFO group_info; + memset(&group_info, 0, sizeof(group_info)); + + group_info.dwSize = sizeof(group_info); + group_info.dwInfoFlags = DPNINFO_NAME | DPNINFO_DATA; + group_info.pwszName = L"Test Group"; + group_info.pvData = NULL; + group_info.dwDataSize = 0; + group_info.dwGroupFlags = 0; + + ASSERT_EQ(host->CreateGroup( + &group_info, /* pdpnGroupInfo */ + NULL, /* pvGroupContext */ + NULL, /* pvAsyncContext */ + NULL, /* phAsyncHandle */ + DPNCREATEGROUP_SYNC), /* dwFlags */ + S_OK); + + Sleep(250); + + peer1.expect_end(); + + peer1.expect_begin(); + peer1.expect_push([](DWORD dwMessageType, PVOID pMessage) + { + EXPECT_TRUE(dwMessageType == DPN_MSGID_DESTROY_PLAYER + || dwMessageType == DPN_MSGID_TERMINATE_SESSION); + return S_OK; + }, 3); + peer1.expect_push([&host, &p1_cg_dpnidGroup](DWORD dwMessageType, PVOID pMessage) + { + EXPECT_EQ(dwMessageType, DPN_MSGID_DESTROY_GROUP); + if(dwMessageType == DPN_MSGID_DESTROY_GROUP) + { + DPNMSG_DESTROY_GROUP *dg = (DPNMSG_DESTROY_GROUP*)(pMessage); + + EXPECT_EQ(dg->dwSize, sizeof(DPNMSG_DESTROY_GROUP)); + EXPECT_EQ(dg->dpnidGroup, p1_cg_dpnidGroup); + EXPECT_EQ(dg->pvGroupContext, (void*)(0xBCDE)); + EXPECT_EQ(dg->dwReason, DPNDESTROYGROUPREASON_SESSIONTERMINATED); + } + + return S_OK; + }); + + ASSERT_EQ(host->DestroyPeer(peer1.first_cc_dpnidLocal, NULL, 0, 0), S_OK); + + Sleep(250); + + peer1.expect_end(); +} + TEST(DirectPlay8Peer, AddPlayerToGroupSync) { const unsigned char GROUP_DATA[] = { 0x01, 0x00, 0x02, 0x03, 0x04, 0x05, 0x06 };