/*========================================================================== * * Copyright (C) 1996-1997 Microsoft Corporation. All Rights Reserved. * * File: dplsp.c * Content: DirectPlayLobby Service Provider interface code * * History: * Date By Reason * ======= ======= ====== * 10/23/96 myronth Created it * 11/20/96 myronth Added DPLAPI to function declarations * 2/12/97 myronth Mass DX5 changes * 2/18/97 myronth Implemented GetObjectCaps * 2/26/97 myronth #ifdef'd out DPASYNCDATA stuff (removed dependency) * 3/31/97 myronth Implemented all IDPLobbySP methods without putting * player management message in the message queue. * Removed dead code * 4/4/97 myronth Changed IDPLobbySP methods' structure names and * implemented system messages for all of them * 4/27/97 sohailm Updated calls to HandlePlayerMessage to reflect new params * 5/8/97 myronth All remote subgroup functions for IDPLobbySP * including StartSession, Purged dead code * 5/12/97 myronth Extra semi-colon bug fixes, Fixed group player count * decrement that was in the wrong place * 5/14/97 myronth Allow CreateGroup message to pass even if the * Group ID is in the map table (bug #8354). * 5/17/97 myronth Fixed HandleMessage, Added SendChatMessage * 5/17/97 myronth Filtered some message to certain groups, Fixed calls * to CreateAndMapNewGroup which needed a parent ID * 5/20/97 myronth Changed DPLP_DeleteRemotePlayerFromGroup to use * InternalDeletePlayerFromGroup instead of * RemovedPlayerFromGroup (now includes system player) * (Bug #8586) * 5/21/97 myronth Fixed the player name for players joining a session * (#8798), Changed to new DPMSG_CHAT format (#8642) * 5/22/97 myronth Fixed flag propagation in DPLP_CreateGroup (#8813) * 5/23/97 myronth Send messages locally for CreateGroup and * CreateGroupInGroup (#8870) * 6/3/97 myronth Added support for player flags in AddPlayerToGroup * (#9091) and added PRV_RemoveSubgroupsAndPlayers- * FromGroup function (#9134) * 6/5/97 myronth Fixed AddGroupToGroup & DeleteGroupFromGroup by * adding heirarchy creating & deletion. (#8731) * 6/6/97 myronth Moved code from PRV_DeleteRemoteGroupFromGroup to * PRV_DestroyGroupAndParents in group.c, Changed all * DistributeGroupMessage calls to go to all players * 6/16/97 myronth Fixed call to InternalAddGroupToGroup (#9745) and * fixed Delete messages for shortcuts on DestroyGroup * (#9739) * 6/20/97 myronth Changed AddGroupToGroup to check if a group exists * and not send a duplicate message (#10139) * 6/24/97 myronth Changed AddPlayerToGroup to check if a player exists * and not send a duplicate message (#10287) * 7/30/97 myronth Added support for standard lobby messaging * 8/11/97 myronth Added guidInstance handling in standard lobby requests * 8/19/97 myronth Removed bogus assert * 9/29/97 myronth Ignore SetPlayerName/Data msgs for local players (#12554) * 10/3/97 myronth Fixed player & group data for remote players/groups (#10961) * 10/7/97 myronth Fixed LP version checking for player & group data (regresssion) * 10/8/97 myronth Rolled back fix for #10961 (group & player data) * 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 DPLP_SetGroupOwner * 11/5/97 myronth Expose lobby ID's as DPID's in lobby sessions * 11/6/97 myronth Made SendChatMessage handle a dwFromID of * DPID_SERVERPLAYER (#12843) * 11/19/97 myronth Fixed VALID_DPLAY_GROUP macro (#12841) * 2/13/98 aarono changed InternalDeletePlayer, added flag. * 8/30/00 aarono B#43812 improper construction of DATA CHANGED. * in SendDataChangedLocally. ***************************************************************************/ #include "dplobpr.h" //-------------------------------------------------------------------------- // // Functions // //-------------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "PRV_SendBuildParentalHeirarchyMessage" void PRV_SendBuildParentalHeirarchyMessage(LPDPLOBBYI_DPLOBJECT this, DWORD dwGroupID, DWORD dwParentID) { SPDATA_BUILDPARENTALHEIRARCHY bph; HRESULT hr; DPF(7, "Entering PRV_SendBuildParentalHeirarchyMessage"); DPF(9, "Parameters: 0x%08x, %lu, %lu", this, dwGroupID, dwParentID); // Setup the SPDATA structure memset(&bph, 0, sizeof(SPDATA_BUILDPARENTALHEIRARCHY)); bph.dwSize = sizeof(SPDATA_BUILDPARENTALHEIRARCHY); bph.lpISP = PRV_GetDPLobbySPInterface(this); bph.dwGroupID = dwGroupID; bph.dwMessage = DPSYS_ADDGROUPTOGROUP; bph.dwParentID = dwParentID; // Call the BuildParentalHeirarchy method in the SP if(CALLBACK_EXISTS(BuildParentalHeirarchy)) { // 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, BuildParentalHeirarchy, &bph); ENTER_DPLOBBY(); } else { // BuildParentalHeirarchy is required DPF_ERR("The Lobby Provider callback for BuildParentalHeirarchy doesn't exist -- it's required"); ASSERT(FALSE); } } // PRV_SendBuildParentalHeirarchyMessage #undef DPF_MODNAME #define DPF_MODNAME "DPLP_AddGroupToGroup" HRESULT DPLAPI DPLP_AddGroupToGroup(LPDPLOBBYSP lpILP, LPSPDATA_ADDREMOTEGROUPTOGROUP lpd) { SPDATA_CREATEREMOTEGROUPINGROUP cgig; SPDATA_DESTROYREMOTEGROUP dg; SPDATA_CREATEREMOTEGROUP cg; LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; LPDPLAYI_PLAYER lpPlayer = NULL; LPDPLAYI_GROUP lpGroup = NULL; LPDPLAYI_GROUP lpGroupTo = NULL; LPDPLAYI_GROUP lpAnchor = NULL; LPDPLAYI_SUBGROUP lpSubgroup = NULL; MSG_PLAYERMGMTMESSAGE msg; BOOL bCreated = FALSE; DPF(7, "Entering DPLP_AddGroupToGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_ADDREMOTEGROUPTOGROUP structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // First see if the group is in our map table. If it's not, // we should just ignore this message because it's for a // group we are not currently in. if(!IsLobbyIDInMapTable(this, lpd->dwAnchorID)) { LEAVE_DPLOBBY(); DPF(8, "Recieved AddGroupToGroup message for unknown anchor group, dwGroupID = %lu, discarding message", lpd->dwAnchorID); return DPERR_INVALIDGROUP; } // Now see if the group is in our map table. If it is, we // probably want to update the name. If it's not, we need to // add them to the nametable and the map table if(!IsLobbyIDInMapTable(this, lpd->dwGroupID)) { // See if the group is a root group (remember hidden groups won't // get pushed down to us). If it is, then just create it. if(!(lpd->dwParentID)) { // Setup the SPDATA struct for CreateRemoteGroup memset(&cg, 0, sizeof(SPDATA_CREATEREMOTEGROUP)); cg.dwSize = sizeof(SPDATA_CREATEREMOTEGROUP); cg.dwGroupID = lpd->dwGroupID; cg.lpName = lpd->lpName; cg.dwFlags = lpd->dwGroupFlags; if(this->dwLPVersion > DPLSP_DX5VERSION) cg.dwGroupOwnerID = lpd->dwGroupOwnerID; else cg.dwGroupOwnerID = DPID_SERVERPLAYER; // Call our internal remote create hr = DPLP_CreateGroup((LPDPLOBBYSP)this->lpInterfaces, &cg); if(FAILED(hr)) { LEAVE_DPLOBBY(); DPF_ERRVAL("Failed creating remote parent group, hr = 0x%08x", hr); return hr; } bCreated = TRUE; } else { // See if it's parent shows up in the map table, if it doesn't, // we need to send a message to the server to tell it to build // the entire tree for us if(!IsLobbyIDInMapTable(this, lpd->dwParentID)) { DPF(8, "Sending message to server to build parental heirarchy, ignoring AddGroupToGroup message"); PRV_SendBuildParentalHeirarchyMessage(this, lpd->dwGroupID, lpd->dwAnchorID); LEAVE_DPLOBBY(); return DPERR_INVALIDGROUP; } // Setup the SPDATA struct for CreateRemoteGroupInGroup memset(&cgig, 0, sizeof(SPDATA_CREATEREMOTEGROUPINGROUP)); cgig.dwSize = sizeof(SPDATA_CREATEREMOTEGROUPINGROUP); cgig.dwParentID = lpd->dwParentID; cgig.dwGroupID = lpd->dwGroupID; cgig.lpName = lpd->lpName; cgig.dwFlags = lpd->dwGroupFlags; if(this->dwLPVersion > DPLSP_DX5VERSION) cgig.dwGroupOwnerID = lpd->dwGroupOwnerID; else cgig.dwGroupOwnerID = DPID_SERVERPLAYER; // Call our internal remote create hr = DPLP_CreateGroupInGroup((LPDPLOBBYSP)this->lpInterfaces, &cgig); if(FAILED(hr)) { LEAVE_DPLOBBY(); DPF_ERRVAL("Failed creating remote group in group, hr = 0x%08x", hr); return hr; } bCreated = TRUE; } } // Take the dplay lock ENTER_DPLAY(); // Make sure the group isn't already in the parent group. If it is, // we just want to return DP_OK and exit so that we don't send any // duplicate messages. lpAnchor = GroupFromID(this->lpDPlayObject, lpd->dwAnchorID); if(!lpAnchor) { DPF_ERR("Unable to find group in nametable"); hr = DPERR_INVALIDGROUP; goto ERROR_DPLP_ADDGROUPTOGROUP; } lpGroup = GroupFromID(this->lpDPlayObject, lpd->dwGroupID); if(!lpGroup) { DPF_ERR("Unable to find group in nametable"); hr = DPERR_INVALIDGROUP; goto ERROR_DPLP_ADDGROUPTOGROUP; } lpSubgroup = lpAnchor->pSubgroups; while(lpSubgroup) { if (lpSubgroup->pGroup == lpGroup) { DPF(2,"Group already in group!"); hr = DP_OK; goto ERROR_DPLP_ADDGROUPTOGROUP; } // check next node lpSubgroup = lpSubgroup->pNextSubgroup; } // So now we should have a valid group and valid player in both // the map table and the nametable, so call dplay's AGtoG function hr = InternalAddGroupToGroup((LPDIRECTPLAY)this->lpDPlayObject->pInterfaces, lpd->dwAnchorID, lpd->dwGroupID, DPGROUP_SHORTCUT, FALSE); if(FAILED(hr)) { // If we created the player and mapped it, then destroy the // player and unmap it. if(bCreated) { // Setup the SPDATA struct for DestroyRemoteGroup memset(&dg, 0, sizeof(SPDATA_DESTROYREMOTEGROUP)); dg.dwSize = sizeof(SPDATA_DESTROYREMOTEGROUP); dg.dwGroupID = lpd->dwGroupID; // Call our internal remote create hr = DPLP_DestroyGroup((LPDPLOBBYSP)this->lpInterfaces, &dg); if(FAILED(hr)) { DPF_ERRVAL("Failed destroying remote group, hr = 0x%08x", hr); goto ERROR_DPLP_ADDGROUPTOGROUP; } } // If we failed, don't send the system message DPF_ERRVAL("Failed adding remote group to group from the lobby, hr = 0x%08x", hr); goto ERROR_DPLP_ADDGROUPTOGROUP; } // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERR("Unable to find group in nametable"); hr = DPERR_INVALIDGROUP; goto ERROR_DPLP_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 = lpd->dwGroupID; msg.dwGroupID = lpd->dwAnchorID; // 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); } ERROR_DPLP_ADDGROUPTOGROUP: // Drop the lock LEAVE_LOBBY_ALL(); return hr; } // DPLP_AddGroupToGroup #undef DPF_MODNAME #define DPF_MODNAME "DPLP_AddPlayerToGroup" HRESULT DPLAPI DPLP_AddPlayerToGroup(LPDPLOBBYSP lpILP, LPSPDATA_ADDREMOTEPLAYERTOGROUP lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; DPID dpidPlayer; LPDPLAYI_PLAYER lpPlayer = NULL; LPDPLAYI_GROUP lpGroupTo = NULL; LPDPLAYI_GROUP lpGroup = NULL; LPDPLAYI_GROUPNODE lpGroupnode = NULL; MSG_PLAYERMGMTMESSAGE msg, cpmsg; BOOL bCreated = FALSE; DWORD dwPlayerFlags = 0; DPF(7, "Entering DPLP_AddPlayerToGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_ADDREMOTEPLAYERTOGROUP structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // First see if the group is in our map table. If it's not, // we should just ignore this message because it's for a // group we are not currently in. if(!IsLobbyIDInMapTable(this, lpd->dwGroupID)) { LEAVE_DPLOBBY(); DPF(8, "Recieved AddPlayerToGroup message for unknown group, dwGroupID = %lu, discarding message", lpd->dwGroupID); return DPERR_INVALIDGROUP; } // Fix up the player flags if(lpd->dwPlayerFlags & DPPLAYER_SPECTATOR) dwPlayerFlags |= DPLAYI_PLAYER_SPECTATOR; if(lpd->dwPlayerFlags & DPPLAYER_SERVERPLAYER) dwPlayerFlags |= DPLAYI_PLAYER_APPSERVER; // Take the dplay lock ENTER_DPLAY(); // Now see if the player is in our map table. If it is, we // probably want to update the name. If it's not, we need to // add them to the nametable and the map table if(!IsLobbyIDInMapTable(this, lpd->dwPlayerID)) { // It doesn't show up in our map table, so create a new // nametable entry for them and put them in our map table. hr = PRV_CreateAndMapNewPlayer(this, &dpidPlayer, lpd->lpName, NULL, NULL, 0, dwPlayerFlags, lpd->dwPlayerID, FALSE); if(FAILED(hr)) { DPF(8, "Unable to add player to nametable or map table, hr = 0x%08x", hr); goto ERROR_DPLP_ADDPLAYER; } bCreated = TRUE; } // Make sure the player isn't already in the group. If it is, // we just want to return DP_OK and exit so that we don't send any // duplicate messages. lpGroup = GroupFromID(this->lpDPlayObject, lpd->dwGroupID); if(!lpGroup) { DPF_ERR("Unable to find group in nametable"); hr = DPERR_INVALIDGROUP; goto ERROR_DPLP_ADDPLAYER; } lpPlayer = PlayerFromID(this->lpDPlayObject, lpd->dwPlayerID); if(!lpPlayer) { DPF_ERR("Unable to find player in nametable"); hr = DPERR_INVALIDPLAYER; goto ERROR_DPLP_ADDPLAYER; } lpGroupnode = lpGroup->pGroupnodes; while(lpGroupnode) { if(lpGroupnode->pPlayer == lpPlayer) { DPF(2, "Player already in group!"); hr = DP_OK; goto ERROR_DPLP_ADDPLAYER; } // check next node lpGroupnode = lpGroupnode->pNextGroupnode; } // So now we should have a valid group and valid player in both // the map table and the nametable, so call dplay with the add message hr = InternalAddPlayerToGroup((LPDIRECTPLAY)this->lpDPlayObject->pInterfaces, lpd->dwGroupID, lpd->dwPlayerID, FALSE); if(FAILED(hr)) { // If we created the player and mapped it, then destroy the // player and unmap it. if(bCreated) { // Remove the player from the nametable InternalDestroyPlayer(this->lpDPlayObject, lpPlayer, FALSE, FALSE); } // If we failed, don't send the system message DPF_ERRVAL("Failed adding remote player to group from the lobby, hr = 0x%08x", hr); goto ERROR_DPLP_ADDPLAYER; } // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERRVAL("Unable to find group in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDGROUP; goto ERROR_DPLP_ADDPLAYER; } // If we created this player, we need to send a CreatePlayer message ahead // of the AddPlayerToGroup message if(bCreated) { // Now build the system message (at least the parts we need) memset(&cpmsg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&cpmsg); SET_MESSAGE_COMMAND(&cpmsg, DPSP_MSG_CREATEPLAYER); cpmsg.dwPlayerID = lpd->dwPlayerID; // Call dplay's DistributeGroupMessage function to put the message // in the queues of all the appropriate players hr = DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)&cpmsg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding CreatePlayer message to player's receive queue from lobby, hr = 0x%08x", hr); } } // Now build the system message for AddPlayerToGroup memset(&msg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_ADDPLAYERTOGROUP); msg.dwPlayerID = lpd->dwPlayerID; msg.dwGroupID = lpd->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); } // We need to see if this player is the group owner. If it is, // we need to send a SetGroupOwner message as well. if(lpd->dwPlayerID == lpGroup->dwOwnerID) { // Now send the message PRV_SendGroupOwnerMessageLocally(this, lpd->dwGroupID, lpd->dwPlayerID, 0); } ERROR_DPLP_ADDPLAYER: // Drop the lock LEAVE_LOBBY_ALL(); return hr; } // DPLP_AddPlayerToGroup #undef DPF_MODNAME #define DPF_MODNAME "DPLP_CreateGroup" HRESULT DPLAPI DPLP_CreateGroup(LPDPLOBBYSP lpILP, LPSPDATA_CREATEREMOTEGROUP lpd) { LPDPLOBBYI_DPLOBJECT this; MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_GROUP lpGroupTo = NULL; HRESULT hr = DP_OK; DPID dpidGroup; DWORD dwInternalFlags = 0; DWORD dwOwnerID; DPF(7, "Entering DPLP_CreateGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_CREATEREMOTEGROUP structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Fix the flags from external to internal if(lpd->dwFlags & DPGROUP_STAGINGAREA) dwInternalFlags = DPLAYI_GROUP_STAGINGAREA; if(lpd->dwFlags & DPGROUP_HIDDEN) dwInternalFlags |= DPLAYI_GROUP_HIDDEN; // Take the lock ENTER_DPLAY(); // First see if the group is in our map table. If it is, // we just want to return. If it's not, we want to add // them and send the appropriate message if(IsLobbyIDInMapTable(this, lpd->dwGroupID)) { DPF(2, "Received a CreateGroup message for a group we already know about"); hr = DP_OK; goto ERROR_DPLP_CREATEGROUP; } else { // Make the owner default to the server player if we have a problem dwOwnerID = DPID_SERVERPLAYER; // If we are talking to at least a DX6 lobby provider, we should // be able to use the GroupOwnerID element if(this->dwLPVersion > DPLSP_DX5VERSION) dwOwnerID = lpd->dwGroupOwnerID; // Create a new entry in the nametable and map the ID's hr = PRV_CreateAndMapNewGroup(this, &dpidGroup, lpd->lpName, lpd->lpData, lpd->dwDataSize, dwInternalFlags, lpd->dwGroupID, 0, dwOwnerID); if(FAILED(hr)) { // If we fail, we don't want to send the system message DPF_ERRVAL("Unable to add group to nametable or map table, hr = 0x%08x", hr); goto ERROR_DPLP_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 = lpd->dwGroupID; // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERRVAL("Unable to find group in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDGROUP; goto ERROR_DPLP_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(8, "Failed adding CreateGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } ERROR_DPLP_CREATEGROUP: // Drop the lock LEAVE_LOBBY_ALL(); return hr; } // DPLP_CreateGroup #undef DPF_MODNAME #define DPF_MODNAME "DPLP_CreateGroupInGroup" HRESULT DPLAPI DPLP_CreateGroupInGroup(LPDPLOBBYSP lpILP, LPSPDATA_CREATEREMOTEGROUPINGROUP lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; DPID dpidGroup; LPDPLAYI_PLAYER lpPlayer = NULL; LPDPLAYI_GROUP lpGroup = NULL; LPDPLAYI_GROUP lpGroupTo = NULL; MSG_PLAYERMGMTMESSAGE msg; BOOL bCreated = FALSE; DWORD dwInternalFlags = 0; DWORD dwOwnerID; DPF(7, "Entering DPLP_CreateGroupInGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_CREATEREMOTEGROUPINGROUP structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // First see if the group is in our map table. If it's not, // we should just ignore this message because it's for a // group we are not currently in. if(!IsLobbyIDInMapTable(this, lpd->dwParentID)) { LEAVE_DPLOBBY(); DPF_ERRVAL("Recieved CreateGroupInGroup message for unknown parent group, dwGroupID = %lu, discarding message", lpd->dwParentID); return DPERR_INVALIDGROUP; } // Take the dplay lock ENTER_DPLAY(); // First see if the group is in our map table. If it is, // we just want to return. If it's not, we want to add // them and send the appropriate message if(IsLobbyIDInMapTable(this, lpd->dwGroupID)) { DPF(2, "Received a CreateGroupInGroup message for a group we already know about"); hr = DP_OK; goto ERROR_DPLP_CREATEGROUPINGROUP; } else { // Setup the internal flags if(lpd->dwFlags & DPGROUP_STAGINGAREA) dwInternalFlags = DPLAYI_GROUP_STAGINGAREA; if(lpd->dwFlags & DPGROUP_HIDDEN) dwInternalFlags |= DPLAYI_GROUP_HIDDEN; // Make the owner default to the server player if we have a problem dwOwnerID = DPID_SERVERPLAYER; // If we are talking to at least a DX6 lobby provider, we should // be able to use the GroupOwnerID element if(this->dwLPVersion > DPLSP_DX5VERSION) dwOwnerID = lpd->dwGroupOwnerID; // It doesn't show up in our map table, so create a new // nametable entry for them and put them in our map table. hr = PRV_CreateAndMapNewGroup(this, &dpidGroup, lpd->lpName, NULL, 0, dwInternalFlags, lpd->dwGroupID, lpd->dwParentID, dwOwnerID); if(FAILED(hr)) { DPF(8, "Unable to add group to nametable or map table, hr = 0x%08x", hr); goto ERROR_DPLP_CREATEGROUPINGROUP; } bCreated = TRUE; } // So now we should have a valid group and valid player in both // the map table and the nametable, so call dplay with the add message hr = InternalAddGroupToGroup((LPDIRECTPLAY)this->lpDPlayObject->pInterfaces, lpd->dwParentID, lpd->dwGroupID, lpd->dwFlags, FALSE); if(FAILED(hr)) { // If we created the player and mapped it, then destroy the // player and unmap it. if(bCreated) { // Get a pointer to dplay's group struct lpGroup = GroupFromID(this->lpDPlayObject, lpd->dwGroupID); // Remove the group from the nametable if(lpGroup){ InternalDestroyGroup(this->lpDPlayObject, lpGroup, FALSE); } } // If we failed, don't send the system message DPF_ERRVAL("Failed creating remote group in group from the lobby, hr = 0x%08x", hr); goto ERROR_DPLP_CREATEGROUPINGROUP; } // 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 = lpd->dwGroupID; // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERRVAL("Unable to find group in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDGROUP; goto ERROR_DPLP_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(8, "Failed adding CreateGroupInGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } ERROR_DPLP_CREATEGROUPINGROUP: // Drop the lock LEAVE_LOBBY_ALL(); return hr; } // DPLP_CreateGroupInGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_DeleteRemoteGroupFromGroup" HRESULT PRV_DeleteRemoteGroupFromGroup(LPDPLOBBYI_DPLOBJECT this, LPSPDATA_DELETEREMOTEGROUPFROMGROUP lpd, BOOL fPropagate, LPDPLAYI_GROUP lpStopParent) { LPDPLAYI_GROUP lpGroupTo = NULL; LPDPLAYI_GROUP lpGroup = NULL, lpParentGroup = NULL; MSG_PLAYERMGMTMESSAGE msg; HRESULT hr = DP_OK; DPF(7, "Entering PRV_DeleteRemoteGroupFromGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x, %lu, 0x%08x", this, lpd, fPropagate, lpStopParent); // First see if the group is in our map table. If it's not, // we should just ignore this message because it's for a // group we are not currently in. if(!IsLobbyIDInMapTable(this, lpd->dwParentID)) { DPF(8, "Recieved DeleteGroupFromGroup message for unknown parent group, dwGroupID = %lu, discarding message", lpd->dwParentID); return DPERR_INVALIDGROUP; } // Now make sure the group is in our map table. If it's not, // we should just ignore this message because it's for a // group we don't know about. if(!IsLobbyIDInMapTable(this, lpd->dwGroupID)) { DPF(8, "Recieved DeleteGroupFromGroup message for unknown group, dwGroupID = %lu, discarding message", lpd->dwGroupID); return DPERR_INVALIDGROUP; } // Take the lock ENTER_DPLAY(); // Get dplay's internal group structures lpParentGroup = GroupFromID(this->lpDPlayObject, lpd->dwParentID); if(!lpParentGroup) { LEAVE_DPLAY(); DPF(8, "Unable to find parent group in nametable"); return DPERR_INVALIDGROUP; } lpGroup = GroupFromID(this->lpDPlayObject, lpd->dwGroupID); if(!lpGroup) { LEAVE_DPLAY(); DPF(8, "Unable to find group in nametable"); return DPERR_INVALIDGROUP; } // Call dplay's internal removegroupfromgroup to remove the attachment // in the nametable hr = RemoveGroupFromGroup(lpParentGroup, lpGroup); if(FAILED(hr)) { DPF(8, "Failed removing group from group, hr = 0x%08x", hr); goto EXIT_DPLP_DELETEREMOTEGROUPFROMGROUP; } // If the fPropagate flag is not set, we don't want to send this message if(fPropagate) { // 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 = lpd->dwGroupID; msg.dwGroupID = lpd->dwParentID; lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF(8, "Unable to find system group in nametable"); goto EXIT_DPLP_DELETEREMOTEGROUPFROMGROUP; } // 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); } } // Even if we couldn't send the message above, destroy the group anyway EXIT_DPLP_DELETEREMOTEGROUPFROMGROUP: // Destroy the group and any of it's parents if there are no more local // references to it or any of it's heirarchy PRV_DestroyGroupAndParents(this, lpGroup, lpStopParent); // Drop the lock LEAVE_DPLAY(); return hr; } // PRV_DeleteRemoteGroupFromGroup #undef DPF_MODNAME #define DPF_MODNAME "DPLP_DeleteGroupFromGroup" HRESULT DPLAPI DPLP_DeleteGroupFromGroup(LPDPLOBBYSP lpILP, LPSPDATA_DELETEREMOTEGROUPFROMGROUP lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; DPF(7, "Entering DPLP_DeleteGroupFromGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_DELETEREMOTEGROUPFROMGROUP structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Call our internal routine, setting the propagate flag to TRUE so that // we post the appropriate message in the player's receive queue hr = PRV_DeleteRemoteGroupFromGroup(this, lpd, TRUE, NULL); LEAVE_DPLOBBY(); return hr; } // DPLP_DeleteGroupFromGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_DeleteRemotePlayerFromGroup" HRESULT PRV_DeleteRemotePlayerFromGroup(LPDPLOBBYI_DPLOBJECT this, LPSPDATA_DELETEREMOTEPLAYERFROMGROUP lpd, BOOL fPropagate) { LPDPLAYI_PLAYER lpPlayer = NULL; LPDPLAYI_GROUP lpGroupTo = NULL; MSG_PLAYERMGMTMESSAGE msg, dpmsg; HRESULT hr; DPF(7, "Entering PRV_DeleteRemotePlayerFromGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x, %lu", this, lpd, fPropagate); // First see if the group is in our map table. If it's not, // we should just ignore this message because it's for a // group we are not currently in. if(!IsLobbyIDInMapTable(this, lpd->dwGroupID)) { DPF(8, "Recieved DeletePlayerFromGroup message for unknown group, dwGroupID = %lu, discarding message", lpd->dwGroupID); return DPERR_INVALIDGROUP; } // Now make sure the player is in our map table. If it's not, // we should just ignore this message because it's for a // player we don't know about. if(!IsLobbyIDInMapTable(this, lpd->dwPlayerID)) { DPF(8, "Recieved DeletePlayerFromGroup message for unknown player, dwPlayerID = %lu, discarding message", lpd->dwGroupID); return DPERR_INVALIDPLAYER; } // Take the lock ENTER_DPLAY(); // Call dplay's internal removeplayerfromgroup to remove the attachment // in the nametable hr = InternalDeletePlayerFromGroup((LPDIRECTPLAY)this->lpDPlayObject->pInterfaces, lpd->dwGroupID, lpd->dwPlayerID, FALSE); if(FAILED(hr)) { DPF_ERRVAL("Failed removing player from group, hr = 0x%08x", hr); goto ERROR_DPLP_DELETEPLAYERFROMGROUP; } // If the fPropagate flag is not set, we don't want to send this message if(fPropagate) { // 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 = lpd->dwPlayerID; msg.dwGroupID = lpd->dwGroupID; // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERRVAL("Unable to find group in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDGROUP; goto ERROR_DPLP_DELETEPLAYERFROMGROUP; } // 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 DeletePlayerFromGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } } // Get dplay's internal group & player structures lpPlayer = PlayerFromID(this->lpDPlayObject, lpd->dwPlayerID); if(!lpPlayer) { // So if this fails, the above call to InternalDeletePlayerFromGroup // shouldn't have succeeded either DPF_ERR("Unable to find player in nametable"); ASSERT(FALSE); goto ERROR_DPLP_DELETEPLAYERFROMGROUP; } // Now we need to decide if this is the last group this player was in. If // it is, then we need to destroy the player as well, and remove them from // our map table. Of course, only destroy the player if it is a remote player. if((!(lpPlayer->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) && (!(lpPlayer->dwFlags & DPLAYI_PLAYER_PLAYERINGROUP))) { // However, before we do this, we need to send a DestroyPlayer // message to all the people who got the DeletePlayerFromGroup if(lpGroupTo && fPropagate) { // Now build the system message (at least the parts we need) memset(&dpmsg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&dpmsg); SET_MESSAGE_COMMAND(&dpmsg, DPSP_MSG_DELETEPLAYER); dpmsg.dwPlayerID = lpd->dwPlayerID; // Call dplay's DistributeGroupMessage function to put the message // in the queues of all the appropriate players DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)&dpmsg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding DestroyPlayer message to player's receive queue from lobby, hr = 0x%08x", hr); } } // Destroy the player and remove it from the nametable InternalDestroyPlayer(this->lpDPlayObject, lpPlayer, FALSE, FALSE); } ERROR_DPLP_DELETEPLAYERFROMGROUP: // Drop the lock LEAVE_DPLAY(); return hr; } // PRV_DeleteRemotePlayerFromGroup #undef DPF_MODNAME #define DPF_MODNAME "DPLP_DeletePlayerFromGroup" HRESULT DPLAPI DPLP_DeletePlayerFromGroup(LPDPLOBBYSP lpILP, LPSPDATA_DELETEREMOTEPLAYERFROMGROUP lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; DPF(7, "Entering DPLP_DeletePlayerFromGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_DELETEREMOTEPLAYERFROMGROUP structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Call our internal routine, setting the propagate flag to TRUE so that // we post the appropriate message in the player's receive queue hr = PRV_DeleteRemotePlayerFromGroup(this, lpd, TRUE); LEAVE_DPLOBBY(); return hr; } // DPLP_DeletePlayerFromGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_BroadcastDestroyGroupMessage" HRESULT DPLAPI PRV_BroadcastDestroyGroupMessage(LPDPLOBBYI_DPLOBJECT this, DWORD dwGroupID) { MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_GROUP lpGroupTo = NULL; HRESULT hr; // 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_DELETEGROUP); msg.dwGroupID = dwGroupID; // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERR("Unable to find system group in nametable"); hr = DPERR_INVALIDGROUP; } else { // Call dplay's DistributeGroupMessage function to put the message in the queue hr = DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)&msg, sizeof(MSG_PLAYERMGMTMESSAGE), FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding DestroyGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } } return hr; } // PRV_BroadcastDestroyGroupMessage #undef DPF_MODNAME #define DPF_MODNAME "PRV_RemoveSubgroupsAndPlayersFromGroup" void PRV_RemoveSubgroupsAndPlayersFromGroup(LPDPLOBBYI_DPLOBJECT this, LPDPLAYI_GROUP lpGroup, DWORD dwGroupID, BOOL bRemoteOnly) { SPDATA_DELETEREMOTEPLAYERFROMGROUP dpd; LPDPLAYI_GROUPNODE lpGroupnode = NULL; LPDPLAYI_GROUPNODE lpNextGroupnode = NULL; HRESULT hr = DP_OK; DPF(7, "Entering PRV_RemoveSubgroupsAndPlayersFromGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", this, lpGroup); ASSERT(lpGroup); // Destroy any subgroups hanging off of this group PRV_DestroySubgroups(this, lpGroup, bRemoteOnly); // Walk the list of nodes, removing each player from the group manually. // The reason for doing this manually is so that the lobby gets a chance // to remove every remote player out of the nametable whose only existence // was inside this room. It also allows the lobby to remove the player's // ID from the map table. Do this by calling the lobby's // DPLP_DeletePlayerFromGroup function which responds to that message. // Setup the DeletePlayerFromGroup data structure memset(&dpd, 0, sizeof(SPDATA_DELETEREMOTEPLAYERFROMGROUP)); dpd.dwSize = sizeof(SPDATA_DELETEREMOTEPLAYERFROMGROUP); dpd.dwGroupID = dwGroupID; // Walk the list of groupnodes, deleting all of the remote players lpGroupnode = lpGroup->pGroupnodes; while(lpGroupnode) { // Save the next groupnode lpNextGroupnode = lpGroupnode->pNextGroupnode; // If the player is local, skip them if(lpGroupnode->pPlayer->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL) { lpGroupnode = lpNextGroupnode; continue; } // Get the lobby ID for the player dpd.dwPlayerID = lpGroup->pGroupnodes->pPlayer->dwID; // Now call the lobby's delete function, setting the fPropagate flag // to true so we put a delete message in the player's queue hr = PRV_DeleteRemotePlayerFromGroup(this, &dpd, TRUE); if(FAILED(hr)) { // Same here, if this fails, something is tragically wrong // with the map table, so just continue; ASSERT(FALSE); break; } // Move to the next node lpGroupnode = lpNextGroupnode; } } // PRV_RemoveSubgroupsAndPlayersFromGroup #undef DPF_MODNAME #define DPF_MODNAME "PRV_SendDeleteShortcutMessageForExitingGroup" void PRV_SendDeleteShortcutMessageForExitingGroup(LPDPLOBBYI_DPLOBJECT this, LPDPLAYI_GROUP lpGroup) { MSG_PLAYERMGMTMESSAGE msg; LPDPLAYI_GROUP lpGroupTo = NULL; LPDPLAYI_GROUP lpGroupTemp = NULL; LPDPLAYI_SUBGROUP lpSubgroupTemp = NULL; UINT nGroupsIn; HRESULT hr; DPF(7, "Entering PRV_SendDeleteShortcutMessageForExitingGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", this, lpGroup); // Take the dplay lock since we will be walking dplay's group list ENTER_DPLAY(); // Get the number of subgroups this group is in nGroupsIn = lpGroup->nGroups; // Setup the static parts of the message, and get a pointer to the system group if(nGroupsIn) { // Build the message struct memset(&msg, 0, sizeof(MSG_PLAYERMGMTMESSAGE)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_DELETEGROUPFROMGROUP); msg.dwPlayerID = lpGroup->dwID; // Get a pointer to the system group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { LEAVE_DPLAY(); DPF_ERR("Unable to get a pointer to the system group - not sending deletegroupfromgroup messages"); return; } } // Walk the list of groups, and send a DeleteGroupFromGroup message // for each shortcut lpGroupTemp = this->lpDPlayObject->pGroups; while(nGroupsIn && lpGroupTemp) { // Walk the list of subgroups for the group lpSubgroupTemp = lpGroupTemp->pSubgroups; while(nGroupsIn && lpSubgroupTemp) { // If the group is our group, send a message, but only if // it is not the parent group (since we will never do a // DeleteGroupFromGroup on a parent-child) if(lpSubgroupTemp->pGroup == lpGroup) { // Make sure it's not the group's parent if(lpGroup->dwIDParent != lpGroupTemp->dwID) { // Send the message msg.dwGroupID = lpGroupTemp->dwID; // 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 DeleteGroupFromGroup message to player's receive queue from lobby, hr = 0x%08x", hr); } } // Decrement the count of subgroups nGroupsIn--; } // Move to the next subgroup lpSubgroupTemp = lpSubgroupTemp->pNextSubgroup; } // Move to the next group lpGroupTemp = lpGroupTemp->pNextGroup; } ASSERT(!nGroupsIn); // Drop the dplay lock since we're done LEAVE_DPLAY(); } // PRV_SendDeleteShortcutMessageForExitingGroup #undef DPF_MODNAME #define DPF_MODNAME "DPLP_DestroyGroup" HRESULT DPLAPI DPLP_DestroyGroup(LPDPLOBBYSP lpILP, LPSPDATA_DESTROYREMOTEGROUP lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; LPDPLAYI_GROUP lpGroup = NULL; LPDPLAYI_GROUPNODE lpGroupNode = NULL; DPF(7, "Entering DPLP_DestroyGroup"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_DESTROYGROUP structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // First see if the group is in our map table. If it's not, // we should just ignore this message because it's for a // group we are not currently in. if(!IsLobbyIDInMapTable(this, lpd->dwGroupID)) { LEAVE_DPLOBBY(); DPF(8, "Recieved DestroyGroup message for unknown group, dwGroupID = %lu, discarding message", lpd->dwGroupID); return DPERR_INVALIDGROUP; } // This is either a group we are in, or it is a root group. If it has // any players, it's a group we are in, so we need to delete all remote // players from the nametable and map table (if this is the only group // they are in). // Take the lock ENTER_DPLAY(); // So, get dplay's internal group structure lpGroup = GroupFromID(this->lpDPlayObject, lpd->dwGroupID); if(!lpGroup) { // If we don't have an lpGroup, we need to fail because some of // the functions below will crash if lpGroup is invalid. DPF(8, "Unable to find group in nametable, dpidGroup = %lu", lpd->dwGroupID); LEAVE_LOBBY_ALL(); 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 remote subgroups and players PRV_RemoveSubgroupsAndPlayersFromGroup(this, lpGroup, lpd->dwGroupID, FALSE); // Now send a DestroyGroup system message to all the local players hr = PRV_BroadcastDestroyGroupMessage(this, lpd->dwGroupID); // Now call dplay's destroy group hr = InternalDestroyGroup(this->lpDPlayObject, lpGroup, FALSE); if(FAILED(hr)) { DPF(8, "Failed destroying group from nametable, hr = 0x%08x", hr); } // Drop the locks LEAVE_LOBBY_ALL(); return hr; } // DPLP_DestroyGroup #undef DPF_MODNAME #define DPF_MODNAME "DPLP_GetSPDataPointer" HRESULT DPLAPI DPLP_GetSPDataPointer(LPDPLOBBYSP lpDPLSP, LPVOID * lplpData) { LPDPLOBBYI_DPLOBJECT this; // Make sure the SP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpDPLSP); if( !VALID_DPLOBBY_PTR( this ) ) { DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Go ahead and save the pointer *lplpData = this->lpSPData; return DP_OK; } // DPLP_GetSPDataPointer #undef DPF_MODNAME #define DPF_MODNAME "PRV_HandleLobbySystemMessage" HRESULT PRV_HandleLobbySystemMessage(LPDPLOBBYI_DPLOBJECT this, LPSPDATA_HANDLEMESSAGE lpd) { LPDPLMSG_GENERIC lpmsg = lpd->lpBuffer; LPDPLMSG_GETPROPERTYRESPONSE lpgpr = NULL; LPDPLOBBYI_REQUESTNODE lprn = NULL; HRESULT hr = DP_OK; // If it's a property message, we need to deal with the request switch(lpmsg->dwType) { case DPLSYS_GETPROPERTYRESPONSE: case DPLSYS_SETPROPERTYRESPONSE: { // Cast it to a GetPropertyResponse message lpgpr = (LPDPLMSG_GETPROPERTYRESPONSE)lpmsg; // Find the request ID in our list of pending requests lprn = this->lprnHead; while(lprn) { if(lprn->dwRequestID == lpgpr->dwRequestID) break; else lprn = lprn->lpNext; } // Print some debug spew if we didn't find it, but return DP_OK since // we "handled" the message if(!lprn) { DPF(5, "Unable to find request ID in pending request list"); return DP_OK; } // See if we slammed the guid, and replace it with GUID_NULL if we did if(lprn->dwFlags & GN_SLAMMED_GUID) lpgpr->guidPlayer = GUID_NULL; // If we found it, swap out the request ID, and send it to the // appropriate place lpgpr->dwRequestID = lprn->dwAppRequestID; if(lprn->dwFlags & GN_SELF_LOBBIED) { // Put the message in the lobby message receive queue hr = PRV_InjectMessageInQueue(lprn->lpgn, DPLMSG_STANDARD, lpgpr, lpd->dwBufSize, FALSE); if(FAILED(hr)) { DPF_ERRVAL("Failed to put message in lobby receive queue, hr = 0x%08x", hr); goto EXIT_HANDLELOBBYSYSTEMMESSAGE; } } else { // Call SendLobbyMessage to send the message to the game hr = PRV_WriteClientData(lprn->lpgn, DPLMSG_STANDARD, lpgpr, lpd->dwBufSize); if(FAILED(hr)) { DPF_ERRVAL("Failed to forward message to game, hr = 0x%08x", hr); goto EXIT_HANDLELOBBYSYSTEMMESSAGE; } } break; } default: break; } EXIT_HANDLELOBBYSYSTEMMESSAGE: // Remove the pending request node if we serviced it (which would have // happened if we have a valid pointer to it) if(lprn) PRV_RemoveRequestNode(this, lprn); return hr; } // PRV_HandleLobbySystemMessage #undef DPF_MODNAME #define DPF_MODNAME "DPLP_HandleMessage" HRESULT DPLAPI DPLP_HandleMessage(LPDPLOBBYSP lpILP, LPSPDATA_HANDLEMESSAGE lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; LPMSG_PLAYERMESSAGE lpmsg = NULL; LPBYTE lpByte = NULL; DWORD dwSize; BOOL bAllocBuffer = FALSE; LPDPLAYI_PLAYER lpPlayer = NULL; DPF(7, "Entering DPLP_HandleMessage"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_HANDLEMESSAGE structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // If the message is a lobby system message, process it // NOTE: Make sure the size of the SPDATA_HANDLEMESSAGE is big enough // to contain a flags field (the shipping 5.0 bits did not have // this field, but the 5.1 bits did). if((lpd->dwSize > DPLOBBYPR_SIZE_HANDLEMESSAGE_DX50) && (lpd->dwFlags & DPSEND_LOBBYSYSTEMMESSAGE)) { hr = PRV_HandleLobbySystemMessage(this, lpd); if(FAILED(hr)) { DPF_ERRVAL("Unable to handle lobby system message, hr = 0x%08x", hr); } LEAVE_DPLOBBY(); return hr; } // REVIEW!!!! -- We should be able to handle a generic send to a group // as well as a player. Currently, I don't think we do. // If this session is using naked messages, we can just send the buffer. // Otherwise, we need to allocate a MSG_PLAYERMESSAGE struct and fill // in the header. if(this->lpDPlayObject->lpsdDesc->dwFlags & DPSESSION_NOMESSAGEID) { lpmsg = lpd->lpBuffer; dwSize = lpd->dwBufSize; } else { // Calculate the size of the message dwSize = sizeof(MSG_PLAYERMESSAGE) + lpd->dwBufSize; // Allocate memory for a message buffer lpmsg = DPMEM_ALLOC(dwSize); if(!lpmsg) { DPF_ERR("Unable to allocate temporary message buffer"); hr = DPERR_OUTOFMEMORY; goto ERROR_DPLP_HANDLEMESSAGE; } // Copy in the message header lpmsg->idFrom = lpd->dwFromID; lpmsg->idTo = lpd->dwToID; // Copy in the message lpByte = (LPBYTE)lpmsg + sizeof(MSG_PLAYERMESSAGE); memcpy(lpByte, lpd->lpBuffer, lpd->dwBufSize); // Set our flag indicating that we allocated a buffer bAllocBuffer = TRUE; } // Take the lock ENTER_DPLAY(); // Find dplay's internal player struct for the To player lpPlayer = PlayerFromID(this->lpDPlayObject, lpd->dwToID); if(!lpPlayer) { LEAVE_DPLAY(); DPF_ERRVAL("Unable to find player in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDPLAYER; goto ERROR_DPLP_HANDLEMESSAGE; } // Call dplay's handleplayermessage function to put the message in the queue hr = HandlePlayerMessage(lpPlayer, (LPBYTE)lpmsg, dwSize, TRUE, 0); if(FAILED(hr)) { DPF(8, "Failed adding message to player's receive queue from lobby, hr = 0x%08x", hr); } // Drop the lock LEAVE_DPLAY(); ERROR_DPLP_HANDLEMESSAGE: if(bAllocBuffer && lpmsg) DPMEM_FREE(lpmsg); LEAVE_DPLOBBY(); return hr; } // DPLP_HandleMessage #undef DPF_MODNAME #define DPF_MODNAME "DPLP_SendChatMessage" HRESULT DPLAPI DPLP_SendChatMessage(LPDPLOBBYSP lpILP, LPSPDATA_CHATMESSAGE lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; LPMSG_CHAT lpmsg = NULL; LPBYTE lpByte = NULL; DWORD dwSize; LPDPLAYI_PLAYER lpPlayer = NULL; LPDPLAYI_GROUP lpGroup = NULL; DWORD dwStringSize; BOOL bToGroup = FALSE; DPF(7, "Entering DPLP_SendChatMessage"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_HANDLEMESSAGE structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Calculate the size of the message dwStringSize = WSTRLEN_BYTES(lpd->lpChat->lpszMessage); dwSize = sizeof(MSG_CHAT) + dwStringSize; // Allocate memory for a message buffer lpmsg = DPMEM_ALLOC(dwSize); if(!lpmsg) { DPF_ERR("Unable to allocate temporary message buffer"); hr = DPERR_OUTOFMEMORY; goto ERROR_DPLP_SENDCHATMESSAGE; } // Copy in the message header SET_MESSAGE_HDR(lpmsg); SET_MESSAGE_COMMAND(lpmsg,DPSP_MSG_CHAT); lpmsg->dwIDFrom = lpd->dwFromID; lpmsg->dwIDTo = lpd->dwToID; lpmsg->dwFlags = lpd->lpChat->dwFlags; lpmsg->dwMessageOffset = sizeof(MSG_CHAT); // Copy in the message lpByte = (LPBYTE)lpmsg + sizeof(MSG_CHAT); memcpy(lpByte, lpd->lpChat->lpszMessage, dwStringSize); // Take the lock ENTER_DPLAY(); // Make sure it's from a valid player or the server player if(lpd->dwFromID != DPID_SERVERPLAYER) { lpPlayer = PlayerFromID(this->lpDPlayObject, lpd->dwFromID); if(!VALID_DPLAY_PLAYER(lpPlayer)) { LEAVE_DPLAY(); DPF_ERR("Received chat message FROM invalid player id!!"); hr = DPERR_INVALIDPLAYER; goto ERROR_DPLP_SENDCHATMESSAGE; } } // See who the message is for lpPlayer = PlayerFromID(this->lpDPlayObject, lpd->dwToID); if(!VALID_DPLAY_PLAYER(lpPlayer)) { // See if it's to a group lpGroup = GroupFromID(this->lpDPlayObject, lpd->dwToID); if(!VALID_DPLAY_GROUP(lpGroup)) { LEAVE_DPLAY(); DPF_ERR("Received chat message for invalid player / group"); hr = DPERR_INVALIDPLAYER; goto ERROR_DPLP_SENDCHATMESSAGE; } bToGroup = TRUE; } // Send it out if(bToGroup) { // Send the message hr = DistributeGroupMessage(this->lpDPlayObject, lpGroup, (LPBYTE)lpmsg, dwSize, FALSE, 0); } else { // Send the message hr = HandlePlayerMessage(lpPlayer, (LPBYTE)lpmsg, dwSize, FALSE, 0); } // Drop the lock LEAVE_DPLAY(); ERROR_DPLP_SENDCHATMESSAGE: if(lpmsg) DPMEM_FREE(lpmsg); LEAVE_DPLOBBY(); return hr; } // DPLP_SendChatMessage #undef DPF_MODNAME #define DPF_MODNAME "PRV_SendDataChangedMessageLocally" HRESULT PRV_SendDataChangedMessageLocally(LPDPLOBBYI_DPLOBJECT this, DPID dpidPlayer, LPVOID lpData, DWORD dwDataSize) { LPDPLAYI_GROUP lpGroupTo = NULL; HRESULT hr = DP_OK; LPMSG_PLAYERDATA lpmsg = NULL; LPBYTE lpByte = NULL; DWORD dwSize; DPF(7, "Entering PRV_SendDataChangedMessageLocally"); DPF(9, "Parameters: 0x%08x, %lu, 0x%08x, %lu", this, dpidPlayer, lpData, dwDataSize); // Take the lock ENTER_DPLAY(); // Setup the message to put in the player's queue // Calculate the size of the message dwSize = sizeof(MSG_PLAYERDATA) + dwDataSize; // Allocate memory for the message lpmsg = DPMEM_ALLOC(dwSize); if(!lpmsg) { DPF_ERR("Unable to allocate memory for temporary message structure"); // Since the name has been changed, we'll just return success here hr = DP_OK; goto EXIT_SENDDATACHANGED; } // Now build the system message SET_MESSAGE_HDR(lpmsg); SET_MESSAGE_COMMAND(lpmsg, DPSP_MSG_PLAYERDATACHANGED); lpmsg->dwPlayerID = dpidPlayer; lpmsg->dwDataSize = dwDataSize; lpmsg->dwDataOffset = sizeof(MSG_PLAYERDATA); // Copy in the data lpByte = (LPBYTE)lpmsg + sizeof(MSG_PLAYERDATA); memcpy(lpByte, lpData, dwDataSize); // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERRVAL("Unable to find group in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDGROUP; goto EXIT_SENDDATACHANGED; } // Call dplay's DistributeGroupMessage function to put the message // in the queues of all the appropriate players hr = DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)lpmsg, dwSize, FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding SetGroupData message to player's receive queue from lobby, hr = 0x%08x", hr); } EXIT_SENDDATACHANGED: // Free our message if(lpmsg) DPMEM_FREE(lpmsg); // Drop the lock LEAVE_DPLAY(); return hr; } // PRV_SendDataChangedMessageLocally #undef DPF_MODNAME #define DPF_MODNAME "PRV_SendNameChangedMessageLocally" HRESULT PRV_SendNameChangedMessageLocally(LPDPLOBBYI_DPLOBJECT this, DPID dpidPlayer, LPDPNAME lpName, BOOL bPlayer) { LPDPLAYI_GROUP lpGroupTo = NULL; HRESULT hr = DP_OK; LPMSG_PLAYERNAME lpmsg = NULL; DWORD dwSize, dwShortSize = 0, dwLongSize = 0; LPBYTE lpByte = NULL; DPF(7, "Entering PRV_SendNameChangedMessageLocally"); DPF(9, "Parameters: 0x%08x, %lu, 0x%08x, 0x%08x", this, dpidPlayer, lpName, bPlayer); // Take the lock ENTER_DPLAY(); // Setup the message to put in the player's queue // Calculate the size of the message if(lpName->lpszShortName) dwShortSize = WSTRLEN_BYTES(lpName->lpszShortName); if(lpName->lpszLongName) dwLongSize = WSTRLEN_BYTES(lpName->lpszLongName); dwSize = sizeof(MSG_PLAYERNAME) + dwShortSize + dwLongSize; // Allocate memory for the message lpmsg = DPMEM_ALLOC(dwSize); if(!lpmsg) { DPF_ERR("Unable to allocate memory for temporary message structure"); // Since the name has been changed, we'll just return success here hr = DP_OK; goto EXIT_SENDNAMECHANGED; } // Now build the system message SET_MESSAGE_HDR(lpmsg); if(bPlayer) SET_MESSAGE_COMMAND(lpmsg, DPSP_MSG_PLAYERNAMECHANGED); else SET_MESSAGE_COMMAND(lpmsg, DPSP_MSG_GROUPNAMECHANGED); lpmsg->dwPlayerID = dpidPlayer; lpmsg->dwShortOffset = sizeof(MSG_PLAYERNAME); lpmsg->dwLongOffset = sizeof(MSG_PLAYERNAME) + dwShortSize; // Copy in the names lpByte = (LPBYTE)lpmsg + sizeof(MSG_PLAYERNAME); memcpy(lpByte, lpName->lpszShortName, dwShortSize); lpByte += dwShortSize; memcpy(lpByte, lpName->lpszLongName, dwLongSize); // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERRVAL("Unable to find group in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDGROUP; goto EXIT_SENDNAMECHANGED; } // Call dplay's DistributeGroupMessage function to put the message // in the queues of all the appropriate players hr = DistributeGroupMessage(this->lpDPlayObject, lpGroupTo, (LPBYTE)lpmsg, dwSize, FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding SetGroupName message to player's receive queue from lobby, hr = 0x%08x", hr); } EXIT_SENDNAMECHANGED: // Free our message if(lpmsg) DPMEM_FREE(lpmsg); // Drop the lock LEAVE_DPLAY(); return hr; } // PRV_SendNameChangedMessageLocally #undef DPF_MODNAME #define DPF_MODNAME "DPLP_SetGroupName" HRESULT DPLAPI DPLP_SetGroupName(LPDPLOBBYSP lpILP, LPSPDATA_SETREMOTEGROUPNAME lpd) { LPDPLOBBYI_DPLOBJECT this; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr = DP_OK; DPF(7, "Entering DPLP_SetGroupName"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_SETGROUPNAME structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // First see if the group is in our map table. If it is not, // we should just ignore this message because it's for a // group we don't know about. if(!IsLobbyIDInMapTable(this, lpd->dwGroupID)) { LEAVE_DPLOBBY(); DPF(8, "Recieved SetGroupName message for unknown group, dwGroupID = %lu, discarding message", lpd->dwGroupID); return DPERR_INVALIDGROUP; } // Take the lock ENTER_DPLAY(); // See if the group is local or remote. If it's local, ignore this message // and just return DP_OK becuase we've already sent this message locally. lpGroup = GroupFromID(this->lpDPlayObject, lpd->dwGroupID); if((!lpGroup) || (lpGroup->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { hr = DP_OK; goto ERROR_DPLP_SETGROUPNAME; } // Call dplay's internalsetname function to update the name in the cache hr = InternalSetName((LPDIRECTPLAY)this->lpDPlayObject->pInterfaces, lpd->dwGroupID, lpd->lpName, FALSE, lpd->dwFlags, FALSE); if(FAILED(hr)) { DPF(8, "Failed to SetGroupName internally for remote group, hr = 0x%08x", hr); goto ERROR_DPLP_SETGROUPNAME; } // Send the message to all the local players hr = PRV_SendNameChangedMessageLocally(this, lpd->dwGroupID, lpd->lpName, FALSE); ERROR_DPLP_SETGROUPNAME: // Drop the locks LEAVE_LOBBY_ALL(); return hr; } // DPLP_SetGroupName #undef DPF_MODNAME #define DPF_MODNAME "PRV_SendGroupOwnerMessageLocally" HRESULT PRV_SendGroupOwnerMessageLocally(LPDPLOBBYI_DPLOBJECT this, DPID dpidGroup, DPID dpidNewOwner, DPID dpidOldOwner) { LPDPLAYI_GROUP lpGroupTo = NULL; HRESULT hr = DP_OK; MSG_GROUPOWNERCHANGED msg; DPF(7, "Entering PRV_SendGroupOwnerMessageLocally"); DPF(9, "Parameters: 0x%08x, 0x%08x, 0x%08x, 0x%08x", this, dpidGroup, dpidNewOwner, dpidOldOwner); // Take the lock ENTER_DPLAY(); // Now build the system message memset(&msg, 0, sizeof(MSG_GROUPOWNERCHANGED)); SET_MESSAGE_HDR(&msg); SET_MESSAGE_COMMAND(&msg, DPSP_MSG_GROUPOWNERCHANGED); msg.dwIDGroup = dpidGroup; msg.dwIDNewOwner = dpidNewOwner; msg.dwIDOldOwner = dpidOldOwner; // Find dplay's internal group struct for the To group lpGroupTo = GroupFromID(this->lpDPlayObject, DPID_ALLPLAYERS); if(!lpGroupTo) { DPF_ERRVAL("Unable to find group in nametable, hr = 0x%08x", hr); hr = DPERR_INVALIDGROUP; goto EXIT_SENDGROUPOWNER; } // 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_GROUPOWNERCHANGED), FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding SetGroupOwner message to player's receive queue from lobby, hr = 0x%08x", hr); } EXIT_SENDGROUPOWNER: // Drop the lock LEAVE_DPLAY(); return hr; } // PRV_SendGroupOwnerMessageLocally #undef DPF_MODNAME #define DPF_MODNAME "DPLP_SetGroupOwner" HRESULT DPLAPI DPLP_SetGroupOwner(LPDPLOBBYSP lpILP, LPSPDATA_SETREMOTEGROUPOWNER lpd) { LPDPLOBBYI_DPLOBJECT this; LPDPLAYI_GROUP lpGroup = NULL; HRESULT hr = DP_OK; DWORD dwOldOwnerID; DPF(7, "Entering DPLP_SetGroupOwner"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_SETGROUPNAME structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // First see if the group is in our map table. If it is not, // we should just ignore this message because it's for a // group we don't know about. if(!IsLobbyIDInMapTable(this, lpd->dwGroupID)) { LEAVE_DPLOBBY(); DPF(8, "Recieved SetGroupOwner message for unknown group, dwGroupID = %lu, discarding message", lpd->dwGroupID); return DPERR_INVALIDGROUP; } // Take the lock ENTER_DPLAY(); // See if the group is local or remote. If it's local, ignore this message // and just return DP_OK becuase we've already sent this message locally. lpGroup = GroupFromID(this->lpDPlayObject, lpd->dwGroupID); if(!lpGroup) { hr = DP_OK; goto ERROR_DPLP_SETGROUPOWNER; } // If the player is already the owner of the group, we don't need // to do any processing (this is the buffer in case the server // sends us duplicate messages for stuff we've already sent locally) if(lpGroup->dwOwnerID == lpd->dwOwnerID) { hr = DP_OK; goto ERROR_DPLP_SETGROUPOWNER; } // Make sure the old owner is in our map table, otherwise just set // it to zero (the default) dwOldOwnerID = lpGroup->dwOwnerID; // Change the owner locally lpGroup->dwOwnerID = lpd->dwOwnerID; // Send a SetGroupOwner message locally PRV_SendGroupOwnerMessageLocally(this, lpd->dwGroupID, lpd->dwOwnerID, dwOldOwnerID); ERROR_DPLP_SETGROUPOWNER: // Drop the locks LEAVE_LOBBY_ALL(); return hr; } // DPLP_SetGroupOwner #undef DPF_MODNAME #define DPF_MODNAME "DPLP_SetPlayerName" HRESULT DPLAPI DPLP_SetPlayerName(LPDPLOBBYSP lpILP, LPSPDATA_SETREMOTEPLAYERNAME lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; LPDPLAYI_PLAYER lpPlayer = NULL; DPF(7, "Entering DPLP_SetPlayerName"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_SETPLAYERNAME structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // First see if the player is in our map table. If it is not, // we should just ignore this message because it's for a // player we don't know about. if(!IsLobbyIDInMapTable(this, lpd->dwPlayerID)) { LEAVE_DPLOBBY(); DPF(8, "Recieved SetPlayerName message for unknown player, dwPlayerID = %lu, discarding message", lpd->dwPlayerID); return DPERR_INVALIDPLAYER; } // Take the lock ENTER_DPLAY(); // See if the player is local or remote. If it's local, ignore this message // and just return DP_OK becuase we've already sent this message locally. lpPlayer = PlayerFromID(this->lpDPlayObject, lpd->dwPlayerID); if((!lpPlayer) || (lpPlayer->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { hr = DP_OK; goto ERROR_DPLP_SETPLAYERNAME; } // Call dplay's internalsetname function to update the name in the cache hr = InternalSetName((LPDIRECTPLAY)this->lpDPlayObject->pInterfaces, lpd->dwPlayerID, lpd->lpName, TRUE, lpd->dwFlags, FALSE); if(FAILED(hr)) { DPF(8, "Failed to SetPlayerName internally for remote group, hr = 0x%08x", hr); goto ERROR_DPLP_SETPLAYERNAME; } // Send the message to all the local players hr = PRV_SendNameChangedMessageLocally(this, lpd->dwPlayerID, lpd->lpName, TRUE); ERROR_DPLP_SETPLAYERNAME: // Drop the lock LEAVE_LOBBY_ALL(); return hr; } // DPLP_SetPlayerName #undef DPF_MODNAME #define DPF_MODNAME "DPLP_SetSessionDesc" HRESULT DPLAPI DPLP_SetSessionDesc(LPDPLOBBYSP lpILP, LPSPDATA_SETSESSIONDESC lpd) { LPDPLOBBYI_DPLOBJECT this; HRESULT hr = DP_OK; DPF(7, "Entering DPLP_SetSessionDesc"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_SETSESSIONDESC structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } LEAVE_DPLOBBY(); return hr; } // DPLP_SetSessionDesc #undef DPF_MODNAME #define DPF_MODNAME "DPLP_SetSPDataPointer" HRESULT DPLAPI DPLP_SetSPDataPointer(LPDPLOBBYSP lpDPLSP, LPVOID lpData) { LPDPLOBBYI_DPLOBJECT this; // Make sure the SP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpDPLSP); if( !VALID_DPLOBBY_PTR( this ) ) { DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Go ahead and save the pointer this->lpSPData = lpData; return DP_OK; } // DPLP_SetSPDataPointer #undef DPF_MODNAME #define DPF_MODNAME "PRV_BuildStartSessionMessage" HRESULT PRV_BuildStartSessionMessage(LPVOID * lplpmsg, LPDWORD lpdwSize, LPDPLCONNECTION lpConn, LPDPLAYI_PLAYER lpPlayer) { LPMSG_STARTSESSION lpmsg = NULL; DWORD dwPackageSize; DWORD dwSize; LPBYTE lpTemp = NULL; DPNAME dpn; HRESULT hr; // Setup a local DPNAME struct for the player if the names exist if((lpPlayer->lpszShortName) || (lpPlayer->lpszLongName)) { // Setup the struct memset(&dpn, 0, sizeof(DPNAME)); dpn.dwSize = sizeof(DPNAME); dpn.lpszShortName = lpPlayer->lpszShortName; dpn.lpszLongName = lpPlayer->lpszLongName; lpConn->lpPlayerName = &dpn; } else { // Make sure the PlayerName pointer is NULL lpConn->lpPlayerName = NULL; } // Calculate the size of our message in Unicode PRV_GetDPLCONNECTIONPackageSize(lpConn, &dwPackageSize, NULL); dwSize = sizeof(MSG_STARTSESSION) + dwPackageSize - sizeof(DPLOBBYI_PACKEDCONNHEADER); // Allocate memory for the message lpmsg = DPMEM_ALLOC(dwSize); if(!lpmsg) { DPF_ERR("Unable to allocate memory for temporary message structure"); return DPERR_OUTOFMEMORY; } // Now build the system message SET_MESSAGE_HDR(lpmsg); SET_MESSAGE_COMMAND(lpmsg, DPSP_MSG_STARTSESSION); // Set the DPLCONNECTION pointer lpmsg->dwConnOffset = sizeof(MSG_STARTSESSION); lpTemp = (LPBYTE)lpmsg + lpmsg->dwConnOffset; // Copy in the package hr = PRV_PackageDPLCONNECTION(lpConn, lpTemp, FALSE); if(FAILED(hr)) { DPF_ERRVAL("Unable to pack DPLCONNECTION struct, hr = 0x%08x", hr); DPMEM_FREE(lpmsg); return hr; } // Set the output pointers *lpdwSize = dwSize; *lplpmsg = lpmsg; return DP_OK; } // PRV_BuildStartSessionMessage #undef DPF_MODNAME #define DPF_MODNAME "DPLP_StartSession" HRESULT DPLAPI DPLP_StartSession(LPDPLOBBYSP lpILP, LPSPDATA_STARTSESSIONCOMMAND lpd) { LPDPLOBBYI_DPLOBJECT this = NULL; LPDPLAYI_DPLAY lpDP = NULL; DPLCONNECTION conn; LPBYTE lpmsg = NULL; HRESULT hr = DP_OK; LPDPLAYI_PLAYER lpPlayer = NULL; LPDPLAYI_GROUP lpGroup = NULL; DWORD dwMessageSize; LPDPLAYI_GROUPNODE lpGroupnode = NULL; UINT nPlayers; DPF(7, "Entering DPLP_StartSession"); DPF(9, "Parameters: 0x%08x, 0x%08x", lpILP, lpd); ENTER_DPLOBBY(); // Make sure the LP doesn't throw us a curve TRY { this = DPLOBJECT_FROM_INTERFACE(lpILP); if( !VALID_DPLOBBY_PTR( this ) ) { LEAVE_DPLOBBY(); DPF_ERR("Lobby Provider passed invalid DPLobby object!"); return DPERR_INVALIDOBJECT; } // Validate the struct pointer if(!lpd) { LEAVE_DPLOBBY(); DPF_ERR("SPDATA_STARTSESSIONCOMMAND structure pointer cannot be NULL"); return DPERR_INVALIDPARAMS; } } EXCEPT( EXCEPTION_EXECUTE_HANDLER ) { LEAVE_DPLOBBY(); DPF_ERR( "Exception encountered validating parameters" ); return DPERR_INVALIDPARAMS; } // Make a local copy of the DPLCONNECTION structure since we will // be modifying some elements of it. We are currently only modifying // elements in the DPLCONNECTION structure itself, so we can get // away with using it's pointers to SessionDesc and PlayerName structs, // but if we modify those in the future, we need to copy them as well memcpy(&conn, lpd->lpConn, sizeof(DPLCONNECTION)); // Make a local variable pointer to the dplay object lpDP = this->lpDPlayObject; // Make sure we know about this group if(!IsLobbyIDInMapTable(this, lpd->dwGroupID)) { DPF(8, "Received StartSessionCommand message for an unknown group, dwGroupID = %lu, discarding message", lpd->dwGroupID); LEAVE_DPLOBBY(); return DPERR_INVALIDGROUP; } // Take the dplay lock since we'll be looking at it's structures ENTER_DPLAY(); // See if the host is even in our nametable, if it isn't, we'll assume // we're not the host // See if the host is a local player, if it is, send separate messages if(IsLobbyIDInMapTable(this, lpd->dwHostID)) { // Get dplay's player struct for the host player lpPlayer = PlayerFromID(lpDP, lpd->dwHostID); // If we know the host player (we should) and he's local, we // want to send the host message first if((lpPlayer) && (lpPlayer->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL)) { // So set the host bit conn.dwFlags |= DPLCONNECTION_CREATESESSION; // Build the StartSession message for the host hr = PRV_BuildStartSessionMessage(&lpmsg, &dwMessageSize, &conn, lpPlayer); if(FAILED(hr)) { DPF_ERRVAL("Failed building StartSessionCommand message, hr = 0x%08x", hr); goto EXIT_DPLP_STARTSESSION; } // Now send the message to the host player alone hr = HandlePlayerMessage(lpPlayer, (LPBYTE)lpmsg, dwMessageSize, FALSE, 0); if(FAILED(hr)) { DPF(8, "Failed adding message to player's receive queue from lobby, hr = 0x%08x", hr); } // Free our message since we're done with it DPMEM_FREE(lpmsg); lpmsg = NULL; // Now fall through and send the join message to everyone else // in the group } } // We must be joining, so set the join bit, and make sure the host // bit isn't still set from above conn.dwFlags &= ~DPLCONNECTION_CREATESESSION; conn.dwFlags |= DPLCONNECTION_JOINSESSION; // Get a pointer to dplay's internal group structure lpGroup = GroupFromID(lpDP, lpd->dwGroupID); if(!lpGroup) { DPF(5, "Unable to find group in nametable, idGroup = %lu", lpd->dwGroupID); goto EXIT_DPLP_STARTSESSION; } // Figure out how many players we are looking for lpGroupnode = FindPlayerInGroupList(lpGroup->pSysPlayerGroupnodes,lpDP->pSysPlayer->dwID); if (!lpGroupnode) { ASSERT(FALSE); return E_UNEXPECTED; } nPlayers = lpGroupnode->nPlayers; // Walk the list of groupnodes, looking for nPlayers local players to give // the message to, excluding the host lpGroupnode = lpGroup->pGroupnodes; while ((nPlayers > 0) && (lpGroupnode)) { if ((lpGroupnode->pPlayer->dwFlags & DPLAYI_PLAYER_PLAYERLOCAL) && (lpGroupnode->pPlayer->dwID != lpd->dwHostID)) { // Build the StartSession (join) message for this player hr = PRV_BuildStartSessionMessage(&lpmsg, &dwMessageSize, &conn, lpGroupnode->pPlayer); if(FAILED(hr)) { DPF(5, "Failed building StartSessionCommand message, hr = 0x%08x", hr); goto EXIT_DPLP_STARTSESSION; } // Send the message to this player hr = HandlePlayerMessage(lpGroupnode->pPlayer, lpmsg, dwMessageSize, FALSE, 0); // Free our message if(lpmsg) DPMEM_FREE(lpmsg); lpmsg = NULL; nPlayers--; } // local & !host lpGroupnode = lpGroupnode->pNextGroupnode; } // while EXIT_DPLP_STARTSESSION: if(lpmsg) DPMEM_FREE(lpmsg); LEAVE_LOBBY_ALL(); return hr; } // DPLP_StartSession