/*========================================================================== * * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. * * File: group.c * Content: Methods for managing groups * * History: * Date By Reason * ======= ======= ====== * 2/27/97 myronth Created it * 3/17/97 myronth Create/DestroyGroup, Removed unnecessary Enum functions * 3/20/97 myronth AddPlayerToGroup, DeletePlayerFromGroup * 3/21/97 myronth SetGroupName, Get/SetGroupData * 3/31/97 myronth Removed dead code, Added CreateAndMapNewGroup function * 4/3/97 myronth Changed CALLSP macro to CALL_LP * 5/6/97 kipo GetGroup() now takes a parent ID * 5/8/97 myronth Subgroup support, GroupConnSettings, StartSession, * and drop the lobby lock when calling the LP * 5/12/97 myronth Handle remote groups properly * 5/17/97 myronth Added parent ID to CreateAndMapNewGroup calls, * Added send message code for DestroyGroup and * DeletePlayerFromGroup on the local machine * 5/20/97 myronth Send Delete & DestroyPlayer messages for remote * players when a local player leaves a group (#8586) * Made AddPlayerToGroup & DeletePlayerFromGroup return * DPERR_ACCESSDENIED on remote players (#8679), * Fixed a bunch of other lock bugs, Changed debug levels * 5/21/97 myronth Pass CreateGroup flags through the lobby (#8813) * 5/22/97 myronth Added functions to destroy remote subgroups when * a local player leaves a group (#8810) * 5/23/97 myronth Send messages locally for CreateGroup and * CreateGroupInGroup (#8870) * 6/3/97 myronth Added PRV_DestroySubgroups function (#9134) and * rearranged some of the DestroyGroup code * 6/5/97 myronth Added shortcut checking to PRV_DestroySubgroups by * adding the PRV_AreSubgroupsShortcuts function * 6/6/97 myronth Added PRV_DestroyGroupAndParents and PRV_Destroy- * ShortcutsForExitingPlayer, cleaned up PRV_Delete- * PlayerFromGroup, Fixed StartSession bugs (#9573,#9574) * 6/9/97 myronth Only delete shortcuts (don't destroy the subgoup) * in the PRV_DestroySubgroups function * 6/16/97 myronth Fixed bad deletion of uncle groups & some subgroups * during DeletePlayerFromGroup (#9655) * 6/20/97 myronth Send AddGroupToGroup message locally to avoid * sending duplicate messages. Also added code to * send local DeleteGroupFromGroup messages (#10139) * 6/24/97 myronth Send AddPlayerToGroup message locally to avoid * sending duplicate messages (#10287) * 8/22/97 myronth Force guidInstance to NULL in SetGroupConnectionSettings * 9/29/97 myronth Send local SetGroupName/Data msgs after call to * lobby server succeeds (#12554) * 10/23/97 myronth Added hidden group support (#12688), fixed crashing * bug on DeletePlayerFromGroup (#12885) * 10/29/97 myronth Added support for group owners, including * DPL_SetGroupOwner and DPL_GetGroupOwner * 11/5/97 myronth Expose lobby ID's as DPID's in lobby sessions ***************************************************************************/ #include "dplobpr.h" //-------------------------------------------------------------------------- // // Functions // //-------------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "PRV_AddGroupToGroup" HRESULT DPLAPI PRV_AddGroupToGroup(LPDPLOBBYI_DPLOBJECT this, DWORD dwParentID, DWORD dwGroupID) { SPDATA_ADDGROUPTOGROUP ad; MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_GROUP lpGroupTo = NULL; HRESULT hr = DP_OK; DPF(7, "Entering DPL_AddGroupToGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", this, dwParentID, dwGroupID); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLAY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLAY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&ad, 0, sizeof(SPDATA_ADDGROUPTOGROUP)); ad.dwSize = sizeof(SPDATA_ADDGROUPTOGROUP); ad.dwParentID = dwParentID; ad.dwGroupID = dwGroupID; // Call the AddGroupToGroup method in the SP if(CALLBACK_EXISTS(AddGroupToGroup)) { ad.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, AddGroupToGroup, &ad); ENTER_DPLOBBY(); } else { // AddGroupToGroup is required DPF_ERR("The Lobby Provider callback for AddGroupToGroup doesn't exist -- it's required"); ASSERT(FALSE); hr = DPERR_UNAVAILABLE; goto EXIT_ADDGROUPTOGROUP; } // If it succeeded, send the AddGroupToGroup message to our local players if(SUCCEEDED(hr)) { // Take the dplay lock ENTER_DPLAY(); // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { LEAVE_DPLAY(); DPF_ERR("Unable to find group in nametable"); hr = DPERR_INVALIDGROUP; goto EXIT_ADDGROUPTOGROUP; } // Now build the system message (at least the parts we need) memset(&msg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_ADDSHORTCUTTOGROUP); msg.dwPlayerID = dwGroupID; msg.dwGroupID = dwParentID; // Call dplay's DistributeGroupMessage function to put the message // in the queues of all the appropriate players hr = DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)&msg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding AddGroupToGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } // Drop the dplay lock LEAVE_DPLAY(); } else { DPF_ERRVAL("Failed calling AddGroupToGroup in the Lobby Provider, hr = 0x%08x", hr); } EXIT_ADDGROUPTOGROUP: LEAVE_DPLOBBY(); return hr; } // PRV_AddGroupToGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_AddPlayerToGroup" HRESULT DPLAPI PRV_AddPlayerToGroup(LPDPLOBBYI_DPLOBJECT this, DWORD dwGroupID, DWORD dwPlayerID) { SPDATA_ADDPLAYERTOGROUP ad; LPDPLAYI_PLAYER lpPlayer = NULL; MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_GROUP lpGroupTo = NULL; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr = DP_OK; DPF(7, "Entering DPL_AddPlayerToGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", this, dwGroupID, dwPlayerID); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLAY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLAY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Take the dplay lock since we'll be looking at a dplay internal struct ENTER_DPLAY(); // Make sure the player is a local player, otherwise return AccessDenied lpPlayer = PlayerFromID(this->lpDPlayObject, dwPlayerID); if(!lpPlayer) { LEAVE_DPLAY(); DPF_ERR("Unable to find player in nametable"); hr = DPERR_INVALIDPLAYER; goto EXIT_ADDPLAYERTOGROUP; } if(!(lpPlayer->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { LEAVE_DPLAY(); DPF_ERR("Cannot add a remote player to a group"); hr = DPERR_ACCESSDENIED; goto EXIT_ADDPLAYERTOGROUP; } // Drop the dplay lock since we're done LEAVE_DPLAY(); // Setup our SPDATA struct memset(&ad, 0, sizeof(SPDATA_ADDPLAYERTOGROUP)); ad.dwSize = sizeof(SPDATA_ADDPLAYERTOGROUP); ad.dwGroupID = dwGroupID; ad.dwPlayerID = dwPlayerID; // Call the AddPlayerToGroup method in the SP if(CALLBACK_EXISTS(AddPlayerToGroup)) { ad.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, AddPlayerToGroup, &ad); ENTER_DPLOBBY(); } else { // AddPlayerToGroup is required DPF_ERR("The Lobby Provider callback for AddPlayerToGroup doesn't exist -- it's required"); ASSERT(FALSE); hr = DPERR_UNAVAILABLE; goto EXIT_ADDPLAYERTOGROUP; } // If it succeeded, send the AddPlayerToGroup message to our local players if(SUCCEEDED(hr)) { // Take the dplay lock ENTER_DPLAY(); // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { LEAVE_DPLAY(); DPF_ERR("Unable to find group in nametable"); hr = DPERR_INVALIDGROUP; goto EXIT_ADDPLAYERTOGROUP; } // Now build the system message (at least the parts we need) memset(&msg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_ADDPLAYERTOGROUP); msg.dwPlayerID = dwPlayerID; msg.dwGroupID = dwGroupID; // Call dplay's DistributeGroupMessage function to put the message // in the queues of all the appropriate players hr = DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)&msg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding AddPlayerToGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } else { // We need to see if this player is the group owner. If it is, // we need to send a SetGroupOwner message as well. lpGroup = GroupFromID(this->lpDPlayObject, dwGroupID); if(lpGroup && (dwPlayerID == lpGroup->dwOwnerID)) { // Now send the message PRV_SendGroupOwnerMessageLocally(this, dwGroupID, dwPlayerID, 0); } } // Drop the dplay lock LEAVE_DPLAY(); } else { DPF_ERRVAL("Failed calling AddPlayerToGroup in the Lobby Provider, hr = 0x%08x", hr); } EXIT_ADDPLAYERTOGROUP: LEAVE_DPLOBBY(); return hr; } // PRV_AddPlayerToGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_CreateGroup" HRESULT DPLAPI PRV_CreateGroup(LPDPLOBBYI_DPLOBJECT this, LPDPID lpidGroup, LPDPNAME lpName, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags, DWORD dwOwnerID) { SPDATA_CREATEGROUP cg; MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_GROUP lpGroupTo = NULL; DWORD dwInternalFlags; HRESULT hr = DP_OK; DPF(7, "Entering PRV_CreateGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu, 0x%08x, 0x%08x", this, lpidGroup, lpName, lpData, dwDataSize, dwFlags, dwOwnerID); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&cg, 0, sizeof(SPDATA_CREATEGROUP)); cg.dwSize = sizeof(SPDATA_CREATEGROUP); cg.lpName = lpName; cg.lpData = lpData; cg.dwDataSize = dwDataSize; cg.dwFlags = dwFlags; cg.dwGroupOwnerID = dwOwnerID; // Call the CreateGroup method in the SP if(CALLBACK_EXISTS(CreateGroup)) { cg.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, CreateGroup, &cg); ENTER_DPLOBBY(); } else { // CreateGroup is required DPF_ERR("The Lobby Provider callback for CreateGroup doesn't exist -- it's required"); ASSERT(FALSE); LEAVE_DPLOBBY(); return DPERR_UNAVAILABLE; } if(FAILED(hr)) { DPF_ERRVAL("Failed calling CreateGroup in the Lobby Provider, hr = 0x%08x", hr); goto EXIT_CREATEGROUP; } // Setup the flags to pass to GetGroup dwInternalFlags = DPLAYI_PLAYER_PLAYERLOCAL; if(dwFlags & DPGROUP_STAGINGAREA) dwInternalFlags |= DPLAYI_GROUP_STAGINGAREA; if(dwFlags & DPGROUP_HIDDEN) dwInternalFlags |= DPLAYI_GROUP_HIDDEN; // Add the player to dplay's nametable and put it in our map table hr = PRV_CreateAndMapNewGroup(this, lpidGroup, lpName, lpData, dwDataSize, dwInternalFlags, cg.dwGroupID, 0, dwOwnerID); if(FAILED(hr)) { DPF_ERRVAL("Failed creating a new local group, hr = 0x%08x", hr); // REVIEW!!!! -- We need to send a message back to the server saying // we couldn't complete the deal on our end. goto EXIT_CREATEGROUP; } // Now build the system message (at least the parts we need) memset(&msg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_CREATEGROUP); msg.dwPlayerID = *lpidGroup; // Take the lock ENTER_DPLAY(); // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { LEAVE_DPLAY(); DPF_ERRVAL("Unable to find system group in nametable, hr = 0x%08x", hr); goto EXIT_CREATEGROUP; } // Call dplay's DistributeGroupMessage function to put the message in // the queues of all the appropriate players hr = DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)&msg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF(2, "Failed adding CreateGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } LEAVE_DPLAY(); EXIT_CREATEGROUP: LEAVE_DPLOBBY(); return hr; } // PRV_CreateGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_CreateGroupInGroup" HRESULT DPLAPI PRV_CreateGroupInGroup(LPDPLOBBYI_DPLOBJECT this, DWORD dwParentID, LPDPID lpidGroup, LPDPNAME lpName, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags, DWORD dwOwnerID) { SPDATA_CREATEGROUPINGROUP cgig; MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_GROUPNODE lpGroupnode = NULL; LPDPLAYI_GROUP lpGroupTo = NULL; HRESULT hr = DP_OK; DWORD dwInternalFlags; DPF(7, "Entering PRV_CreateGroupInGroup"); DPF(9, "Parameters: 0x%08x, %lu, 0x%08x, 0x%08x, 0x%08x, %lu, 0x%08x, 0x%08x", this, dwParentID, lpidGroup, lpName, lpData, dwDataSize, dwFlags, dwOwnerID); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&cgig, 0, sizeof(SPDATA_CREATEGROUPINGROUP)); cgig.dwSize = sizeof(SPDATA_CREATEGROUPINGROUP); cgig.dwParentID = dwParentID; cgig.lpName = lpName; cgig.lpData = lpData; cgig.dwDataSize = dwDataSize; cgig.dwFlags = dwFlags; cgig.dwGroupOwnerID = dwOwnerID; // Call the CreateGroupInGroup method in the SP if(CALLBACK_EXISTS(CreateGroupInGroup)) { cgig.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, CreateGroupInGroup, &cgig); ENTER_DPLOBBY(); } else { // CreateGroupInGroup is required DPF_ERR("The Lobby Provider callback for CreateGroupInGroup doesn't exist -- it's required"); ASSERT(FALSE); LEAVE_DPLOBBY(); return DPERR_UNAVAILABLE; } if(FAILED(hr)) { DPF_ERRVAL("Failed calling CreateGroupInGroup in the Lobby Provider, hr = 0x%08x", hr); LEAVE_DPLOBBY(); return hr; } // Setup the flags to pass to GetGroup dwInternalFlags = DPLAYI_PLAYER_PLAYERLOCAL; if(dwFlags & DPGROUP_STAGINGAREA) dwInternalFlags |= DPLAYI_GROUP_STAGINGAREA; if(dwFlags & DPGROUP_HIDDEN) dwInternalFlags |= DPLAYI_GROUP_HIDDEN; // Add the group to dplay's nametable and put it in our map table hr = PRV_CreateAndMapNewGroup(this, lpidGroup, lpName, lpData, dwDataSize, dwInternalFlags, cgig.dwGroupID, dwParentID, dwOwnerID); if(FAILED(hr)) { DPF_ERRVAL("Failed creating a new local group, hr = 0x%08x", hr); // REVIEW!!!! -- We need to send a message back to the server saying // we couldn't complete the deal on our end. goto EXIT_CREATEGROUPINGROUP; } // Take the dplay lock ENTER_DPLAY(); // Now build the system message (at least the parts we need) memset(&msg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_CREATEGROUP); msg.dwPlayerID = *lpidGroup; // Find dplay's internal group struct for the To group // Since this is local, send it to all players lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { LEAVE_DPLAY(); DPF_ERRVAL("Unable to find parent group in nametable, hr = 0x%08x", hr); goto EXIT_CREATEGROUPINGROUP; } // Call dplay's DistributeGroupMessage function to put the message // in the queues of all the appropriate players hr = DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)&msg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF(2, "Failed adding CreateGroupInGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } // Drop the dplay lock LEAVE_DPLAY(); EXIT_CREATEGROUPINGROUP: LEAVE_DPLOBBY(); return hr; } // PRV_CreateGroupInGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_DeleteGroupFromGroup" HRESULT DPLAPI PRV_DeleteGroupFromGroup(LPDPLOBBYI_DPLOBJECT this, DWORD dwParentID, DWORD dwGroupID) { SPDATA_DELETEGROUPFROMGROUP dgd; MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_GROUP lpGroupTo = NULL; HRESULT hr = DP_OK; DPF(7, "Entering DPL_DeleteGroupFromGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", this, dwParentID, dwGroupID); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&dgd, 0, sizeof(SPDATA_DELETEGROUPFROMGROUP)); dgd.dwSize = sizeof(SPDATA_DELETEGROUPFROMGROUP); dgd.dwParentID = dwParentID; dgd.dwGroupID = dwGroupID; // Call the DeleteGroupFromGroup method in the SP if(CALLBACK_EXISTS(DeleteGroupFromGroup)) { dgd.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, DeleteGroupFromGroup, &dgd); ENTER_DPLOBBY(); } else { // DeleteGroupFromGroup is required DPF_ERR("The Lobby Provider callback for DeleteGroupFromGroup doesn't exist -- it's required"); ASSERT(FALSE); LEAVE_DPLOBBY(); return DPERR_UNAVAILABLE; } // If it succeeded, send the DeleteGroupFromGroup message to all local players if(SUCCEEDED(hr)) { // Take the dplay lock ENTER_DPLAY(); // Get a pointer to dplay's system group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { LEAVE_LOBBY_ALL(); DPF_ERR("Unable to find system group in nametable, not sending DeleteGroupFromGroup message"); return DPERR_INVALIDGROUP; } // Now build the system message (at least the parts we need) memset(&msg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_DELETEGROUPFROMGROUP); msg.dwPlayerID = dwGroupID; msg.dwGroupID = dwParentID; // Call dplay's DistributeGroupMessage function to put the message // in the queues of all the appropriate players DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)&msg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding DeleteGroupFromGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } // Drop the dplay lock LEAVE_DPLAY(); } else { DPF_ERRVAL("Failed calling DeleteGroupFromGroup in the Lobby Provider, hr = 0x%08x", hr); } // The dplay InternalDeletePlayerFromGroup code will take care of the rest of // the internal cleanup (nametable, players, etc.), so we can just return // from here. LEAVE_DPLOBBY(); return hr; } // PRV_DeleteGroupFromGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_DoSubgroupsContainLocalPlayers" BOOL PRV_DoSubgroupsContainLocalPlayers(LPDPLAYI_GROUP lpGroup, BOOL bIncludeGroup) { LPDPLAYI_GROUPNODE lpGroupnode = NULL; LPDPLAYI_SUBGROUP lpSubgroup = NULL; DPF(7, "Entering PRV_DoSubgroupsContainLocalPlayers"); DPF(9, "Parameters: 0x%08x, %lu", lpGroup, bIncludeGroup); ASSERT(lpGroup); // Figure out how many local players are in this group. If it's // nonzero, just return true from this function. If the bIncludeGroup // parameter is set to FALSE, then don't look at the group passed in lpGroupnode = FindPlayerInGroupList(lpGroup->pSysPlayerGroupnodes, lpGroup->lpDP->pSysPlayer->dwID); if(lpGroupnode && (lpGroupnode->nPlayers > 0) && bIncludeGroup) return TRUE; // Walk the list of subgroups lpSubgroup = lpGroup->pSubgroups; while(lpSubgroup) { // We're going recursive here to do the entire heirarchy // Check out any of it's subgroups if((!(lpSubgroup->dwFlags & DPGROUP_SHORTCUT)) && (PRV_DoSubgroupsContainLocalPlayers(lpSubgroup->pGroup, TRUE))) return TRUE; else lpSubgroup = lpSubgroup->pNextSubgroup; } // while subgroups return FALSE; } // PRV_DoSubgroupsContainLocalPlayers #undef DPF_MODNAME #define DPF_MODNAME "PRV_AreSubgroupsShortcuts" BOOL PRV_AreSubgroupsShortcuts(LPDPLAYI_GROUP lpGroup) { LPDPLAYI_SUBGROUP lpSubgroup = NULL; DPF(7, "Entering PRV_AreSubgroupsShortcuts"); DPF(9, "Parameters: 0x%08x", lpGroup); ASSERT(lpGroup); // If the group is one of the following, then we want to return TRUE so // it doesn't get nuked: // 1) Root group, nGroups > 0 // 2) Root group, hidden, nGroups = 0 // 2) Non-root group, nGroups > 1 // Otherwise, we can check it's subgroups and return FALSE as appropriate if(((lpGroup->dwIDParent == 0) && ((lpGroup->nGroups > 0) || (!(lpGroup->dwFlags & DPLAYI_GROUP_HIDDEN)) && (lpGroup->nGroups == 0))) || ((lpGroup->dwIDParent != 0) && (lpGroup->nGroups > 1))) return TRUE; // Walk the list of subgroups lpSubgroup = lpGroup->pSubgroups; while(lpSubgroup) { // We're going recursive here to do the entire heirarchy // Check out any of it's subgroups if(PRV_AreSubgroupsShortcuts(lpSubgroup->pGroup)) return TRUE; else lpSubgroup = lpSubgroup->pNextSubgroup; } // while subgroups return FALSE; } // PRV_AreSubgroupsShortcuts #undef DPF_MODNAME #define DPF_MODNAME "PRV_DestroyGroupAndParents" void PRV_DestroyGroupAndParents(LPDPLOBBYI_DPLOBJECT this, LPDPLAYI_GROUP lpGroup, LPDPLAYI_GROUP lpStopParent) { LPDPLAYI_GROUPNODE lpGroupnode = NULL; LPDPLAYI_GROUP lpParentGroup = NULL; SPDATA_DESTROYREMOTEGROUP dg; DPID dpidParent; HRESULT hr; DPF(7, "Entering PRV_DestroyGroupAndParents"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", this, lpGroup, lpStopParent); ASSERT(lpGroup); // Now we need to decide if this is the last group this group was in. If // it is, then we need to destroy the group as well, and remove them from // our map table. Of course, only destroy the group if it is a remote group. // ALSO, we need to walk the heirarchy backward (up the tree) to the root // node and delete all groups that were only created to get to our shortcut. if(!(lpGroup->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { // Walk our parental heirarchy until we reach a a root group. dpidParent = lpGroup->dwIDParent; while(dpidParent) { // Get dplay's internal group structures lpParentGroup = GroupFromID(this->lpDPlayObject, dpidParent); if(!lpParentGroup) { ASSERT(FALSE); DPF_ERRVAL("Unable to find group in nametable, dpidGroup = %lu", dpidParent); return; } // If there are any local players in the parent group, we don't want to // destroy it or any of it's subgroups (since players in the group will // be able to see subgroups) lpGroupnode = FindPlayerInGroupList(lpParentGroup->pSysPlayerGroupnodes, this->lpDPlayObject->pSysPlayer->dwID); if((lpGroupnode) && (lpGroupnode->nPlayers > 0)) return; // Make sure we haven't reached our stop parent group if the caller // passed one in. This will keep us from recursively destroying // a subgroup's parent, which we might be spinning on, deleting all // of it's subgroups. if(lpStopParent && (lpStopParent == lpParentGroup)) return; // Destroy the subgroups PRV_DestroySubgroups(this, lpParentGroup, TRUE); // Get the next parent dpidParent = lpParentGroup->dwIDParent; } // See if we processed any parents, or if we already have a root // group. If lpParentGroup is NULL, we have a root group, so just // stuff our group pointer in the parent group pointer if(!lpParentGroup) lpParentGroup = lpGroup; // Now see if our root group is hidden, and if it doesn't contain any // references, then we want to destroy it as well. if((!PRV_DoSubgroupsContainLocalPlayers(lpParentGroup, TRUE)) && (!PRV_AreSubgroupsShortcuts(lpParentGroup)) && (lpParentGroup->dwFlags & DPLAYI_GROUP_HIDDEN)) { // Setup the SPDATA struct for DestroyRemoteGroup memset(&dg, 0, sizeof(SPDATA_DESTROYREMOTEGROUP)); dg.dwSize = sizeof(SPDATA_DESTROYREMOTEGROUP); dg.dwGroupID = lpParentGroup->dwID; // Call our internal remote create hr = DPLP_DestroyGroup((LPDPLOBBYSP)this->lpInterfaces, &dg); if(FAILED(hr)) { DPF_ERRVAL("Failed destroying remote root group, hr = 0x%08x", hr); } } } } // PRV_DestroyGroupAndParents #undef DPF_MODNAME #define DPF_MODNAME "PRV_DestroyRemoteShortcutsForExitingPlayer" void PRV_DestroyRemoteShortcutsForExitingPlayer(LPDPLOBBYI_DPLOBJECT this, LPDPLAYI_GROUP lpGroup, DWORD dwGroupID) { SPDATA_DELETEREMOTEGROUPFROMGROUP drgd; LPDPLAYI_SUBGROUP lpSubgroup = NULL; LPDPLAYI_SUBGROUP lpNextSubgroup = NULL; HRESULT hr; DPF(7, "Entering PRV_DestroyRemoteShortcutsForExitingPlayer"); DPF(9, "Parameters: 0x%08x, 0x%08x, %lu", this, lpGroup, dwGroupID); ASSERT(lpGroup); // Setup the SPDATA_DELETEREMOTEPLAYERFROMGROUP data struct memset(&drgd, 0, sizeof(SPDATA_DELETEREMOTEGROUPFROMGROUP)); drgd.dwSize = sizeof(SPDATA_DELETEREMOTEGROUPFROMGROUP); drgd.dwParentID = dwGroupID; // Walk the list of subgroups, destroying all remote shortcuts lpSubgroup = lpGroup->pSubgroups; while(lpSubgroup) { // Save the next subgroup lpNextSubgroup = lpSubgroup->pNextSubgroup; // Make sure the group is remote and that this is really // a shortcut and not a child if(((lpSubgroup->dwFlags & DPGROUP_SHORTCUT)) && (!(lpSubgroup->pGroup->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL))) { // Get the subgroup's lobby ID drgd.dwGroupID = lpSubgroup->pGroup->dwID; // Call our internal DeleteGroupFromGroup routine to delete // the shortcut and send the appropriate messages // NOTE: It is imperative that we pass in a pointer to the // group who's shortcuts we are removing as the stop parent. // If we do not, we run the risk of deleting it or one of // it's children that we haven't yet looped through, which // will result in a crash as we continue to walk the // subgroup list. hr = PRV_DeleteRemoteGroupFromGroup(this, &drgd, TRUE, lpGroup); if(FAILED(hr)) { DPF_ERRVAL("Failed deleting remote group from group, hr = 0x%08x", hr); } } // Go to the next one lpSubgroup = lpNextSubgroup; } } // PRV_DestroyRemoteShortcutsForExitingPlayer #undef DPF_MODNAME #define DPF_MODNAME "PRV_DestroyRemotePlayersForExitingPlayer" void PRV_DestroyRemotePlayersForExitingPlayer(LPDPLOBBYI_DPLOBJECT this, LPDPLAYI_GROUP lpGroup, DWORD dwGroupID) { SPDATA_DELETEREMOTEPLAYERFROMGROUP drpd; LPDPLAYI_GROUPNODE lpGroupnode = NULL; LPDPLAYI_GROUPNODE lpNextGroupnode = NULL; HRESULT hr; DPF(7, "Entering PRV_DestroyRemotePlayersForExitingPlayer"); DPF(9, "Parameters: 0x%08x, 0x%08x", this, lpGroup); ASSERT(lpGroup); // Setup the SPDATA_DELETEREMOTEPLAYERFROMGROUP data struct memset(&drpd, 0, sizeof(SPDATA_DELETEREMOTEPLAYERFROMGROUP)); drpd.dwSize = sizeof(SPDATA_DELETEREMOTEPLAYERFROMGROUP); drpd.dwGroupID = dwGroupID; // Walk the list of groupnodes, deleting remote players that are not in // any other groups lpGroupnode = lpGroup->pGroupnodes; while(lpGroupnode) { // Save our next groupnode pointer since our current groupnode // will be gone when we come back from the delete lpNextGroupnode = lpGroupnode->pNextGroupnode; // Delete the player from the group if it's remote and then // destroy the player if he is in no other groups if (!(lpGroupnode->pPlayer->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { // Get the remote player's ID drpd.dwPlayerID = lpGroupnode->pPlayer->dwID; // Delete the player from the group hr = PRV_DeleteRemotePlayerFromGroup(this, &drpd, TRUE); if(FAILED(hr)) { DPF_ERRVAL("Failed deleting remote player from group, hr = 0x%08x", hr); } } lpGroupnode = lpNextGroupnode; } // while } // PRV_DestroyRemotePlayersForExitingPlayer #undef DPF_MODNAME #define DPF_MODNAME "PRV_DestroySubgroups" void PRV_DestroySubgroups(LPDPLOBBYI_DPLOBJECT this, LPDPLAYI_GROUP lpGroup, BOOL bRemoteOnly) { LPDPLAYI_SUBGROUP lpSubgroup = NULL; LPDPLAYI_SUBGROUP lpNextSubgroup = NULL; SPDATA_DESTROYREMOTEGROUP dgd; SPDATA_DELETEREMOTEGROUPFROMGROUP drg; HRESULT hr; DPF(7, "Entering PRV_DestroySubgroups"); DPF(9, "Parameters: 0x%08x, 0x%08x, %lu", this, lpGroup, bRemoteOnly); ASSERT(lpGroup); // Setup the static part of the SPDATA structures memset(&dgd, 0, sizeof(SPDATA_DESTROYREMOTEGROUP)); dgd.dwSize = sizeof(SPDATA_DESTROYREMOTEGROUP); memset(&drg, 0, sizeof(SPDATA_DELETEREMOTEGROUPFROMGROUP)); drg.dwSize = sizeof(SPDATA_DELETEREMOTEGROUPFROMGROUP); drg.dwParentID = lpGroup->dwID; // Walk the list of subgroups lpSubgroup = lpGroup->pSubgroups; while(lpSubgroup) { // Save the next subgroup lpNextSubgroup = lpSubgroup->pNextSubgroup; // Make sure it's a remote group if the flag is set if((bRemoteOnly) && (lpSubgroup->pGroup->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { lpSubgroup = lpNextSubgroup; continue; } // If the subgroup doesn't contain any local players, // nor do any of it's subgroups, then destroy it if((!bRemoteOnly) || ((!PRV_DoSubgroupsContainLocalPlayers(lpSubgroup->pGroup, TRUE)) && (!PRV_AreSubgroupsShortcuts(lpSubgroup->pGroup)))) { // If the group is a shortcut, just delete the link. If it's a child, // destroy the subgroup. if(lpSubgroup->dwFlags & DPGROUP_SHORTCUT) { // Finish setting up the SPDATA structure drg.dwGroupID = lpSubgroup->pGroup->dwID; // Destroy the subgroup hr = DPLP_DeleteGroupFromGroup((LPDPLOBBYSP)this->lpInterfaces, &drg); if(FAILED(hr)) { DPF_ERRVAL("Failed deleting remote group from group, hr = 0x%08x", hr); } } else { // Finish setting up the SPDATA structure dgd.dwGroupID = lpSubgroup->pGroup->dwID; // Destroy the subgroup hr = DPLP_DestroyGroup((LPDPLOBBYSP)this->lpInterfaces, &dgd); if(FAILED(hr)) { DPF_ERRVAL("Failed destroying remote group, hr = 0x%08x", hr); } } } lpSubgroup = lpNextSubgroup; } // while lpSubgroups } // PRV_DestroySubgroups #undef DPF_MODNAME #define DPF_MODNAME "PRV_DeletePlayerFromGroup" HRESULT DPLAPI PRV_DeletePlayerFromGroup(LPDPLOBBYI_DPLOBJECT this, DWORD dwGroupID, DWORD dwPlayerID) { SPDATA_DELETEPLAYERFROMGROUP dpd; MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_PLAYER lpPlayer = NULL; LPDPLAYI_GROUP lpGroup =NULL; LPDPLAYI_GROUPNODE lpGroupnode = NULL; HRESULT hr = DP_OK; DPF(7, "Entering PRV_DeletePlayerFromGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", this, dwGroupID, dwPlayerID); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Take the dplay lock since we'll be looking at a dplay internal struct ENTER_DPLAY(); // Make sure the player is a local player, otherwise return AccessDenied lpPlayer = PlayerFromID(this->lpDPlayObject, dwPlayerID); if(!lpPlayer) { DPF_ERR("Unable to find player in nametable"); hr = DPERR_INVALIDGROUP; goto EXIT_DELETEPLAYERFROMGROUP; } if(!(lpPlayer->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { DPF_ERR("Cannot delete a remote player from a group"); hr = DPERR_INVALIDPLAYER; goto EXIT_DELETEPLAYERFROMGROUP; } // Drop the dplay lock since we're done LEAVE_DPLAY(); // Setup our SPDATA struct memset(&dpd, 0, sizeof(SPDATA_DELETEPLAYERFROMGROUP)); dpd.dwSize = sizeof(SPDATA_DELETEPLAYERFROMGROUP); dpd.dwGroupID = dwGroupID; dpd.dwPlayerID = dwPlayerID; // Call the DeletePlayerFromGroup method in the SP if(CALLBACK_EXISTS(DeletePlayerFromGroup)) { dpd.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, DeletePlayerFromGroup, &dpd); ENTER_DPLOBBY(); } else { // DeletePlayerFromGroup is required DPF_ERR("The Lobby Provider callback for DeletePlayerFromGroup doesn't exist -- it's required"); ASSERT(FALSE); LEAVE_DPLOBBY(); return DPERR_UNAVAILABLE; } if(FAILED(hr)) { DPF_ERRVAL("Failed calling DeletePlayerFromGroup in the Lobby Provider, hr = 0x%08x", hr); LEAVE_DPLOBBY(); return hr; } // Take the dplay lock ENTER_DPLAY(); // We need to remove all other players in the group and send the appropriate // message to the player we are about to delete because he won't see the // system messages for them once he leaves the group. However, if any other // local players are in the group, we don't want to remove the remote players // from the nametable because the other local players need to see them. // Get a pointer to dplay's internal group structure lpGroup = GroupFromID(this->lpDPlayObject, dwGroupID); if(!lpGroup) { DPF_ERRVAL("Unable to find group in nametable, idGroup = %lu", dwGroupID); hr = DPERR_INVALIDGROUP; goto EXIT_DELETEPLAYERFROMGROUP; } // Get a pointer to dplay's internal player structure lpPlayer = PlayerFromID(this->lpDPlayObject, dwPlayerID); if(!lpPlayer) { DPF_ERRVAL("Unable to find player in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDPLAYER; goto EXIT_DELETEPLAYERFROMGROUP; } // We need to send a DeletePlayerFromGroup message to the player who // was deleted since he won't get the group message once he's gone // Now build the system message (at least the parts we need) memset(&msg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_DELETEPLAYERFROMGROUP); msg.dwPlayerID = dwPlayerID; msg.dwGroupID = dwGroupID; // Call dplay's handleplayermessage function to put the message in the queue hr = HandlePlayerMessage(lpPlayer, (LPBYTE)&msg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF_ERRVAL("Failed adding message to player's receive queue from lobby, hr = 0x%08x", hr); // Set the hresult back to DP_OK since only the message failed hr = DP_OK; } // Figure out how many local players are in this group. If it's only 1, // then delete all the remote players. lpGroupnode = FindPlayerInGroupList(lpGroup->pSysPlayerGroupnodes, this->lpDPlayObject->pSysPlayer->dwID); if((!lpGroupnode) || (lpGroupnode->nPlayers == 0)) { // Destroy all remote players that are only in this group PRV_DestroyRemotePlayersForExitingPlayer(this, lpGroup, dwGroupID); // Destroy all the remote shortcut groups, making sure we are // not in them, and removing their entire parental heirarchy PRV_DestroyRemoteShortcutsForExitingPlayer(this, lpGroup, dwGroupID); // Destroy all remote subgroups of this group, making sure we're // not in them for some reason PRV_DestroySubgroups(this, lpGroup, TRUE); // Destroy the group we're leaving if it is remote as well as it's // parental chain. PRV_DestroyGroupAndParents(this, lpGroup, NULL); } EXIT_DELETEPLAYERFROMGROUP: // The dplay InternalDeletePlayerFromGroup code will take care of the rest of // the internal cleanup (nametable, players, etc.), so we can just return // from here. LEAVE_LOBBY_ALL(); return hr; } // PRV_DeletePlayerFromGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_DestroyGroup" HRESULT DPLAPI PRV_DestroyGroup(LPDPLOBBYI_DPLOBJECT this, DWORD dwLobbyID) { SPDATA_DESTROYGROUP dg; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr = DP_OK; DPF(7, "Entering PRV_DestroyGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", this, dwLobbyID); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&dg, 0, sizeof(SPDATA_DESTROYGROUP)); dg.dwSize = sizeof(SPDATA_DESTROYGROUP); dg.dwGroupID = dwLobbyID; // Call the DestroyGroup method in the SP if(CALLBACK_EXISTS(DestroyGroup)) { dg.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, DestroyGroup, &dg); ENTER_DPLOBBY(); } else { // DestroyGroup is required DPF_ERR("The Lobby Provider callback for DestroyGroup doesn't exist -- it's required"); ASSERT(FALSE); LEAVE_DPLOBBY(); return DPERR_UNAVAILABLE; } if(FAILED(hr)) { DPF_ERRVAL("Failed calling DestroyGroup in the Lobby Provider, hr = 0x%08x", hr); LEAVE_DPLOBBY(); return hr; } // Take the lock ENTER_DPLAY(); // So, get dplay's internal group structure lpGroup = GroupFromID(this->lpDPlayObject, dwLobbyID); if(!lpGroup) { // This shouldn't ever happen. If the groups isn't in the nametable, // we should never get this far. ASSERT(FALSE); LEAVE_LOBBY_ALL(); DPF_ERRVAL("Unable to find group in nametable, dpidGroup = %lu", dwLobbyID); return DPERR_INVALIDGROUP; } // Send messages to remove shortcuts to this group (since dplay won't // do it for us) PRV_SendDeleteShortcutMessageForExitingGroup(this, lpGroup); // Destroy all the subgroups and remote players PRV_RemoveSubgroupsAndPlayersFromGroup(this, lpGroup, dwLobbyID, FALSE); // Drop the dplay lock since we're done mucking around with it's structures LEAVE_DPLAY(); // Broadcast the DestroyGroup message hr = PRV_BroadcastDestroyGroupMessage(this, dwLobbyID); if(FAILED(hr)) { DPF_ERRVAL("Failed to send DestroyGroup message to local players, hr = 0x%08x", hr); } // The dplay InternalDestroyGroup code will take care of the rest of // the internal cleanup (nametable, players, etc.), so we can just return // from here. LEAVE_DPLOBBY(); return hr; } // PRV_DestroyGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_GetGroupConnectionSettings" HRESULT PRV_GetGroupConnectionSettings(LPDIRECTPLAY lpDP, DWORD dwFlags, DWORD dwGroupID, LPVOID lpData, LPDWORD lpdwSize) { SPDATA_GETGROUPCONNECTIONSETTINGS gcs; LPDPLOBBYI_DPLOBJECT this = NULL; LPDPLAYI_DPLAY lpDPObject = NULL; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr = DP_OK; DPF(7, "Entering PRV_GetGroupConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDP, dwFlags, dwGroupID, lpData, lpdwSize); TRY { lpDPObject = DPLAY_FROM_INT(lpDP); hr = VALID_DPLAY_PTR( lpDPObject ); if (FAILED(hr)) { DPF_ERRVAL("Bad DPlay interface pointer - hr = 0x%08lx\n",hr); return hr; } if(!IS_LOBBY_OWNED(lpDPObject)) { DPF_ERR("GetGroupConnectionSettings is only supported for lobby connections"); return DPERR_UNSUPPORTED; } this = lpDPObject->lpLobbyObject; if(!VALID_DPLOBBY_PTR(this)) { DPF_ERR("Bad DPLobby object"); return DPERR_INVALIDOBJECT; } lpGroup = GroupFromID(lpDPObject, dwGroupID); if (!VALID_DPLAY_GROUP(lpGroup)) { DPF_ERR("Invalid group id"); return DPERR_INVALIDGROUP; } if( !VALID_DWORD_PTR( lpdwSize ) ) { DPF_ERR("lpdwSize was not a valid dword pointer!"); return DPERR_INVALIDPARAMS; } if(lpData) { if( !VALID_WRITE_PTR(lpData, *lpdwSize) ) { DPF_ERR("lpData is not a valid output buffer of the size specified in *lpdwSize"); return DPERR_INVALIDPARAMS; } } // We haven't defined any flags for this release if( (dwFlags) ) { return DPERR_INVALIDFLAGS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&gcs, 0, sizeof(SPDATA_GETGROUPCONNECTIONSETTINGS)); gcs.dwSize = sizeof(SPDATA_GETGROUPCONNECTIONSETTINGS); gcs.dwFlags = dwFlags; gcs.dwGroupID = dwGroupID; gcs.lpdwBufferSize = lpdwSize; gcs.lpBuffer = lpData; // Call the GetGroupConnectionSettings method in the SP if(CALLBACK_EXISTS(GetGroupConnectionSettings)) { gcs.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the dplay lock since we are going to send a guaranteed message LEAVE_LOBBY_ALL(); hr = CALL_LP(this, GetGroupConnectionSettings, &gcs); // Take the lock back ENTER_LOBBY_ALL(); } else { // GetGroupConnectionSettings is required DPF_ERR("The Lobby Provider callback for GetGroupConnectionSettings doesn't exist -- it's required"); ASSERT(FALSE); return DPERR_UNAVAILABLE; } if(FAILED(hr) && (hr != DPERR_BUFFERTOOSMALL)) { DPF_ERRVAL("Failed calling GetGroupConnectionSettings in the Lobby Provider, hr = 0x%08x", hr); } return hr; } // PRV_GetGroupConnectionSettings #undef DPF_MODNAME #define DPF_MODNAME "DPL_GetGroupConnectionSettings" HRESULT DPLAPI DPL_GetGroupConnectionSettings(LPDIRECTPLAY lpDP, DWORD dwFlags, DPID idGroup, LPVOID lpData, LPDWORD lpdwSize) { HRESULT hr; DPF(7, "Entering DPL_GetGroupConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDP, dwFlags, idGroup, lpData, lpdwSize); ENTER_LOBBY_ALL(); // Set the ANSI flag to TRUE and call the internal function hr = PRV_GetGroupConnectionSettings(lpDP, dwFlags, idGroup, lpData, lpdwSize); LEAVE_LOBBY_ALL(); return hr; } // DPL_GetGroupConnectionSettings #undef DPF_MODNAME #define DPF_MODNAME "PRV_GetGroupData" HRESULT DPLAPI PRV_GetGroupData(LPDPLOBBYI_DPLOBJECT this, DWORD dwGroupID, LPVOID lpData, LPDWORD lpdwDataSize) { SPDATA_GETGROUPDATA ggd; HRESULT hr = DP_OK; DPF(7, "Entering DPL_GetGroupData"); DPF(9, "Parameters: 0x%08x, %lu, 0x%08x, 0x%08x", this, dwGroupID, lpData, lpdwDataSize); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&ggd, 0, sizeof(SPDATA_GETGROUPDATA)); ggd.dwSize = sizeof(SPDATA_GETGROUPDATA); ggd.dwGroupID = dwGroupID; ggd.lpdwDataSize = lpdwDataSize; ggd.lpData = lpData; // Call the GetGroupData method in the SP if(CALLBACK_EXISTS(GetGroupData)) { ggd.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, GetGroupData, &ggd); ENTER_DPLOBBY(); } else { // GetGroupData is required DPF_ERR("The Lobby Provider callback for GetGroupData doesn't exist -- it's required"); ASSERT(FALSE); LEAVE_DPLOBBY(); return DPERR_UNAVAILABLE; } if(FAILED(hr)) { DPF_ERRVAL("Failed calling GetGroupData in the Lobby Provider, hr = 0x%08x", hr); } LEAVE_DPLOBBY(); return hr; } // PRV_GetGroupData #undef DPF_MODNAME #define DPF_MODNAME "DPL_GetGroupOwner" HRESULT DPAPI DPL_GetGroupOwner(LPDIRECTPLAY lpDP, DWORD dwGroupID, LPDPID lpidOwner) { LPDPLAYI_DPLAY this; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr; DPF(7, "Entering DPL_GetGroupOwner"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", lpDP, dwGroupID, lpidOwner); ENTER_DPLAY(); TRY { this = DPLAY_FROM_INT(lpDP); hr = VALID_DPLAY_PTR( this ); if (FAILED(hr)) { LEAVE_DPLAY(); DPF_ERRVAL("bad dplay ptr - hr = 0x%08lx\n",hr); return hr; } lpGroup = GroupFromID(this, dwGroupID); if ((!VALID_DPLAY_GROUP(lpGroup)) || (DPID_ALLPLAYERS == dwGroupID)) { LEAVE_DPLAY(); DPF_ERR("Invalid group id"); return DPERR_INVALIDGROUP; } if (!VALID_DWORD_PTR(lpidOwner)) { LEAVE_DPLAY(); DPF_ERR("Invalid owner id pointer"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLAY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // This method is only valid in lobby session if(IS_LOBBY_OWNED(this)) { *lpidOwner = lpGroup->dwOwnerID; } else { DPF_ERR("GetGroupOwner is only supported for lobby sessions"); hr = DPERR_UNSUPPORTED; } LEAVE_DPLAY(); return hr; } // DPL_GetGroupOwner #undef DPF_MODNAME #define DPF_MODNAME "PRV_SetGroupConnectionSettings" HRESULT PRV_SetGroupConnectionSettings(LPDIRECTPLAY lpDP, DWORD dwFlags, DWORD dwGroupID, LPDPLCONNECTION lpConn, BOOL bAnsi) { SPDATA_SETGROUPCONNECTIONSETTINGS scs; LPDPLOBBYI_DPLOBJECT this = NULL; LPDPLAYI_DPLAY lpDPObject = NULL; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr = DP_OK; DPF(7, "Entering PRV_SetGroupConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x, %lu", lpDP, dwFlags, dwGroupID, lpConn, bAnsi); TRY { lpDPObject = DPLAY_FROM_INT(lpDP); hr = VALID_DPLAY_PTR( lpDPObject ); if (FAILED(hr)) { DPF_ERRVAL("Bad DPlay interface pointer - hr = 0x%08lx\n",hr); return hr; } if(!IS_LOBBY_OWNED(lpDPObject)) { DPF_ERR("SetGroupConnectionSettings is only supported for lobby connections"); return DPERR_UNSUPPORTED; } this = lpDPObject->lpLobbyObject; if(!VALID_DPLOBBY_PTR(this)) { DPF_ERR("Bad DPLobby object"); return DPERR_INVALIDOBJECT; } lpGroup = GroupFromID(lpDPObject, dwGroupID); if (!VALID_DPLAY_GROUP(lpGroup)) { DPF_ERR("Invalid group id"); return DPERR_INVALIDGROUP; } hr = PRV_ValidateDPLCONNECTION(lpConn, FALSE); if(FAILED(hr)) { DPF_ERR("Invalid DPLCONNECTION structure"); return hr; } // We haven't defined any flags for this release if( (dwFlags) ) { return DPERR_INVALIDFLAGS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&scs, 0, sizeof(SPDATA_SETGROUPCONNECTIONSETTINGS)); scs.dwSize = sizeof(SPDATA_SETGROUPCONNECTIONSETTINGS); scs.dwFlags = dwFlags; scs.dwGroupID = dwGroupID; scs.lpConn = lpConn; // Ensure that the guidInstance in the DPLCONNECTION structure is NULL lpConn->lpSessionDesc->guidInstance = GUID_NULL; // Call the SetGroupConnectionSettings method in the SP if(CALLBACK_EXISTS(SetGroupConnectionSettings)) { scs.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the dplay lock since we're sending a guaranteed message LEAVE_LOBBY_ALL(); hr = CALL_LP(this, SetGroupConnectionSettings, &scs); // Take the lock back ENTER_LOBBY_ALL(); } else { // SetGroupConnectionSettings is required DPF_ERR("The Lobby Provider callback for SetGroupConnectionSettings doesn't exist -- it's required"); ASSERT(FALSE); return DPERR_UNAVAILABLE; } if(FAILED(hr)) { DPF_ERRVAL("Failed calling SetGroupConnectionSettings in the Lobby Provider, hr = 0x%08x", hr); } return hr; } // PRV_SetGroupConnectionSettings #undef DPF_MODNAME #define DPF_MODNAME "DPL_SetGroupConnectionSettings" HRESULT DPLAPI DPL_SetGroupConnectionSettings(LPDIRECTPLAY lpDP, DWORD dwFlags, DPID idGroup, LPDPLCONNECTION lpConn) { HRESULT hr; DPF(7, "Entering DPL_SetGroupConnectionSettings"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x", lpDP, dwFlags, idGroup, lpConn); ENTER_LOBBY_ALL(); // Set the ANSI flag to TRUE and call the internal function hr = PRV_SetGroupConnectionSettings(lpDP, dwFlags, idGroup, lpConn, FALSE); LEAVE_LOBBY_ALL(); return hr; } // DPL_SetGroupConnectionSettings #undef DPF_MODNAME #define DPF_MODNAME "PRV_SetGroupData" HRESULT DPLAPI PRV_SetGroupData(LPDPLOBBYI_DPLOBJECT this, DWORD dwGroupID, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags) { SPDATA_SETGROUPDATA sgd; HRESULT hr = DP_OK; DPF(7, "Entering DPL_SetGroupData"); DPF(9, "Parameters: 0x%08x, %lu, 0x%08x, %lu, 0x%08x", this, dwGroupID, lpData, dwDataSize, dwFlags); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLAY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLAY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&sgd, 0, sizeof(SPDATA_SETGROUPDATA)); sgd.dwSize = sizeof(SPDATA_SETGROUPDATA); sgd.dwGroupID = dwGroupID; sgd.dwDataSize = dwDataSize; sgd.lpData = lpData; sgd.dwFlags = dwFlags; // Call the SetGroupData method in the SP if(CALLBACK_EXISTS(SetGroupData)) { sgd.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, SetGroupData, &sgd); ENTER_DPLOBBY(); } else { // SetGroupData is required DPF_ERR("The Lobby Provider callback for SetGroupData doesn't exist -- it's required"); ASSERT(FALSE); hr = DPERR_UNAVAILABLE; goto EXIT_SETGROUPDATA; } // If it succeeded, send the SetGroupData message to our local players if(SUCCEEDED(hr)) { hr = PRV_SendDataChangedMessageLocally(this, dwGroupID, lpData, dwDataSize); } else { DPF_ERRVAL("Failed calling SetGroupData in the Lobby Provider, hr = 0x%08x", hr); } EXIT_SETGROUPDATA: LEAVE_DPLOBBY(); return hr; } // PRV_SetGroupData #undef DPF_MODNAME #define DPF_MODNAME "PRV_SetGroupName" HRESULT DPLAPI PRV_SetGroupName(LPDPLOBBYI_DPLOBJECT this, DWORD dwGroupID, LPDPNAME lpName, DWORD dwFlags) { SPDATA_SETGROUPNAME sgn; HRESULT hr = DP_OK; DPF(7, "Entering DPL_SetGroupName"); DPF(9, "Parameters: 0x%08x, %lu, 0x%08x, 0x%08x", this, dwGroupID, lpName, dwFlags); ENTER_DPLOBBY(); TRY { if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLAY(); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLAY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Setup our SPDATA struct memset(&sgn, 0, sizeof(SPDATA_SETGROUPNAME)); sgn.dwSize = sizeof(SPDATA_SETGROUPNAME); sgn.dwGroupID = dwGroupID; sgn.lpName = lpName; sgn.dwFlags = dwFlags; // Call the SetGroupName method in the SP if(CALLBACK_EXISTS(SetGroupName)) { sgn.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // CreatePlayer response (which always happens) LEAVE_DPLOBBY(); hr = CALL_LP(this, SetGroupName, &sgn); ENTER_DPLOBBY(); } else { // SetGroupName is required DPF_ERR("The Lobby Provider callback for SetGroupName doesn't exist -- it's required"); ASSERT(FALSE); hr = DPERR_UNAVAILABLE; goto EXIT_SETGROUPNAME; } // If it succeeded, send the SetGroupName message to our local players if(SUCCEEDED(hr)) { hr = PRV_SendNameChangedMessageLocally(this, dwGroupID, lpName, FALSE); } else { DPF_ERRVAL("Failed calling SetGroupName in the Lobby Provider, hr = 0x%08x", hr); } EXIT_SETGROUPNAME: LEAVE_DPLOBBY(); return hr; } // PRV_SetGroupName #undef DPF_MODNAME #define DPF_MODNAME "DPL_SetGroupOwner" HRESULT DPLAPI DPL_SetGroupOwner(LPDIRECTPLAY lpDP, DWORD dwGroupID, DWORD dwOwnerID) { LPDPLOBBYI_DPLOBJECT this; LPDPLAYI_DPLAY lpDPlayObject; SPDATA_SETGROUPOWNER sgo; LPDPLAYI_PLAYER lpNewOwner = NULL; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr = DP_OK; DWORD dwOldOwnerID; DPF(7, "Entering DPL_SetGroupOwner"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", lpDP, dwGroupID, dwOwnerID); ENTER_LOBBY_ALL(); TRY { lpDPlayObject = DPLAY_FROM_INT(lpDP); hr = VALID_DPLAY_PTR( lpDPlayObject ); if (FAILED(hr)) { LEAVE_LOBBY_ALL(); DPF_ERRVAL("bad dplay ptr - hr = 0x%08lx\n",hr); return hr; } this = lpDPlayObject->lpLobbyObject; if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_LOBBY_ALL(); return DPERR_INVALIDOBJECT; } lpGroup = GroupFromID(lpDPlayObject, dwGroupID); if ((!VALID_DPLAY_GROUP(lpGroup)) || (DPID_ALLPLAYERS == dwGroupID)) { LEAVE_LOBBY_ALL(); DPF_ERR("Invalid group id"); return DPERR_INVALIDGROUP; } // DPID_SERVERPLAYER is valid here if(dwOwnerID != DPID_SERVERPLAYER) { lpNewOwner = PlayerFromID(lpDPlayObject, dwOwnerID); if (!VALID_DPLAY_PLAYER(lpNewOwner)) { LEAVE_LOBBY_ALL(); DPF_ERR("Invalid new owner player id"); return DPERR_INVALIDPLAYER; } } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_LOBBY_ALL(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // do the send if(!IS_LOBBY_OWNED(lpDPlayObject)) { DPF_ERR("SetGroupOwner is only supported for lobby sessions"); hr = DPERR_UNSUPPORTED; goto EXIT_SETGROUPOWNER; } // Setup our SPDATA struct memset(&sgo, 0, sizeof(SPDATA_SETGROUPOWNER)); sgo.dwSize = sizeof(SPDATA_SETGROUPOWNER); sgo.dwGroupID = dwGroupID; sgo.dwOwnerID = dwOwnerID; // Call the SetGroupOwner method in the SP if(CALLBACK_EXISTS(SetGroupOwner)) { sgo.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the lock so the lobby provider's receive thread can get back // in with other messages if they show up in the queue before our // response (which always happens) LEAVE_LOBBY_ALL(); hr = CALL_LP(this, SetGroupOwner, &sgo); ENTER_LOBBY_ALL(); } else { // SetGroupOwner is required DPF_ERR("The Lobby Provider callback for SetGroupOwner doesn't exist"); hr = DPERR_UNAVAILABLE; goto EXIT_SETGROUPOWNER; } // If it succeeded, send the SetGroupOwner message to our local players if(SUCCEEDED(hr)) { // Get a pointer to our internal data struct for the group, just in // case it changed for some reason while we had dropped the locks lpGroup = GroupFromID(this->lpDPlayObject, dwGroupID); if(!lpGroup) { DPF_ERR("Unable to find group in nametable -- local nametable will be incorrect"); goto EXIT_SETGROUPOWNER; } // Save the old owner so we can put it in the message dwOldOwnerID = lpGroup->dwOwnerID; // Change the owner lpGroup->dwOwnerID = dwOwnerID; // Send a SetGroupOwner message locally PRV_SendGroupOwnerMessageLocally(this, dwGroupID, dwOwnerID, dwOldOwnerID); } else { DPF_ERRVAL("Failed calling SetGroupOwner in the Lobby Provider, hr = 0x%08x", hr); } EXIT_SETGROUPOWNER: LEAVE_LOBBY_ALL(); return hr; } // DPL_SetGroupOwner #undef DPF_MODNAME #define DPF_MODNAME "DPL_StartSession" HRESULT DPLAPI DPL_StartSession(LPDIRECTPLAY lpDP, DWORD dwFlags, DWORD dwGroupID) { SPDATA_STARTSESSION ss; LPDPLOBBYI_DPLOBJECT this = NULL; LPDPLAYI_DPLAY lpDPObject = NULL; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr = DP_OK; DPF(7, "Entering DPL_StartSession"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x", lpDP, dwFlags, dwGroupID); ENTER_LOBBY_ALL(); TRY { lpDPObject = DPLAY_FROM_INT(lpDP); hr = VALID_DPLAY_PTR( lpDPObject ); if (FAILED(hr)) { DPF_ERRVAL("Bad DPlay interface pointer - hr = 0x%08lx\n",hr); goto ERROR_STARTSESSION; } if(!IS_LOBBY_OWNED(lpDPObject)) { DPF_ERR("SetGroupConnectionSettings is only supported for lobby connections"); hr = DPERR_UNSUPPORTED; goto ERROR_STARTSESSION; } this = lpDPObject->lpLobbyObject; if(!VALID_DPLOBBY_PTR(this)) { DPF_ERR("Bad DPLobby object"); hr = DPERR_INVALIDOBJECT; goto ERROR_STARTSESSION; } if(dwFlags) { DPF_ERR("Invalid flags"); hr = DPERR_INVALIDFLAGS; goto ERROR_STARTSESSION; } lpGroup = GroupFromID(lpDPObject, dwGroupID); if (!VALID_DPLAY_GROUP(lpGroup)) { DPF_ERR("Invalid group id"); hr = DPERR_INVALIDGROUP; goto ERROR_STARTSESSION; } // Make sure the group is a staging area if(!(lpGroup->dwFlags & DPLAYI_GROUP_STAGINGAREA)) { DPF_ERR("StartSession can only be called on a Staging Area"); hr = DPERR_INVALIDGROUP; goto ERROR_STARTSESSION; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); hr = DPERR_INVALIDPARAMS; goto ERROR_STARTSESSION; } // Setup our SPDATA struct memset(&ss, 0, sizeof(SPDATA_STARTSESSION)); ss.dwSize = sizeof(SPDATA_STARTSESSION); ss.dwGroupID = dwGroupID; ss.dwFlags = dwFlags; // Call the StartSession method in the SP if(CALLBACK_EXISTS(StartSession)) { ss.lpISP = PRV_GetDPLobbySPInterface(this); // Drop the dplay lock so we can send a guarateed message LEAVE_LOBBY_ALL(); hr = CALL_LP(this, StartSession, &ss); // Take the lock back ENTER_LOBBY_ALL(); } else { // StartSession is required DPF_ERR("The Lobby Provider callback for StartSession doesn't exist -- it's required"); ASSERT(FALSE); hr = DPERR_UNAVAILABLE; goto ERROR_STARTSESSION; } if(FAILED(hr)) { DPF_ERRVAL("Failed calling StartSession in the Lobby Provider, hr = 0x%08x", hr); } ERROR_STARTSESSION: LEAVE_LOBBY_ALL(); return hr; } // DPL_StartSession #undef DPF_MODNAME #define DPF_MODNAME "PRV_CreateAndMapNewGroup" HRESULT PRV_CreateAndMapNewGroup(LPDPLOBBYI_DPLOBJECT this, DPID * lpdpid, LPDPNAME lpName, LPVOID lpData, DWORD dwDataSize, DWORD dwFlags, DWORD dwLobbyID, DPID dpidParent, DWORD dwOwnerID) { LPDPLAYI_GROUP lpGroup = NULL, lpSysGroup = NULL; HRESULT hr; DPID dpidGroup, dpidSysPlayer; // Take the dplay lock ENTER_DPLAY(); // Make sure the lobby ID is valid if(!IsValidLobbyID(dwLobbyID)) { DPF_ERRVAL("ID %lu is reserved, cannot create new player", dwLobbyID); hr = DPERR_INVALIDPLAYER; goto EXIT_CREATEANDMAPNEWGROUP; } // If this is a remote player, we need allocate a new nametable entry // for them and set the correct system player ID if(!(dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { // Allocate a new ID for the player hr = NS_AllocNameTableEntry(this->lpDPlayObject, &dpidGroup); if(FAILED(hr)) { DPF_ERRVAL("Unable to allocate new nametable id, hr = 0x%08x", hr); goto EXIT_CREATEANDMAPNEWGROUP; } // Make sure we have a lobby system player (for all remote players // & groups). If we don't then allocate a new one. if(!(this->dpidSysPlayer)) { hr = PRV_CreateAndMapNewPlayer(this, &dpidSysPlayer, NULL, NULL, NULL, 0, DPLAYI_PLAYER_SYSPLAYER, DPID_LOBBYREMOTESYSTEMPLAYER, TRUE); if(FAILED(hr)) { DPF_ERRVAL("Unable to create lobby system player, hr = 0x%08x", hr); ASSERT(FALSE); goto EXIT_CREATEANDMAPNEWGROUP; } // Set the lobby system player ID pointer to the new ID this->dpidSysPlayer = dpidSysPlayer; } } // Get a group struct for the group (if it's local, this will add it // to the nametable. If it's remote, we need to add it below) hr = GetGroup(this->lpDPlayObject, &lpGroup, lpName, lpData, dwDataSize, dwFlags, dpidParent, dwLobbyID); if(FAILED(hr)) { DPF_ERRVAL("Failed trying to add group to the nametable, hr = 0x%08x", hr); goto EXIT_CREATEANDMAPNEWGROUP; } // Fixup the group's owner lpGroup->dwOwnerID = dwOwnerID; // If the group is remote, set the group's ID to the new one we // allocated and then set the system group ID to the lobby system group if(!(dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { // lpGroup->dwIDSysPlayer = this->dpidSysPlayer; // Add the group to the nametable hr = AddItemToNameTable(this->lpDPlayObject, (DWORD_PTR)lpGroup, &dpidGroup, TRUE, dwLobbyID); if (FAILED(hr)) { DPF_ERRVAL("Unable to add new group to the nametable, hr = 0x%08x", hr); ASSERT(FALSE); goto EXIT_CREATEANDMAPNEWGROUP; } // Set the group's ID lpGroup->dwID = dpidGroup; } // Set the output dpid pointer *lpdpid = lpGroup->dwID; EXIT_CREATEANDMAPNEWGROUP: LEAVE_DPLAY(); return hr; } // PRV_CreateAndMapNewGroup