Put it all together - Playing with party - (Unity module)
Connect the UI to the play with party flow
In the previous section, you created functions to handle party events related to gameplay, displaying notifications and prompts accordingly. To ensure that only the party leader can initiate a game session and matchmaking, this section introduces functions to perform the necessary validation.
-
Open the
PlayingWithPartyWrapper_Starterclass and create the function below to track party member game session status. You will use this to validate whether party members are in any active game sessions.private void UpdatePartyMemberGameSessionStatus()
{
if (!IsInParty)
{
return;
}
MenuCanvas menuCanvas = MenuManager.Instance.GetCurrentMenu();
bool isInMatchLobby = menuCanvas != null && menuCanvas is MatchLobbyMenu;
bool isInGameplay = SceneManager.GetActiveScene().buildIndex is GameConstant.GameSceneBuildIndex;
bool isInGameSession = isInMatchLobby || isInGameplay;
// No need to update if the status is the same.
if (isInGameSession == cachedInGameStatus)
{
return;
}
// Update party member game session status
Session.GetPartyDetails(CachedParty.id, (result) =>
{
if (result.IsError)
{
BytewarsLogger.LogWarning($"Update party member game session status failed. Error {result.Error.Code}: {result.Error.Message}");
return;
}
SessionV2PartySession partySession = result.Value;
SessionV2PartySessionUpdateRequest request = new();
request.version = partySession.version;
request.attributes = partySession.attributes;
// Parse old statuses.
Dictionary<string /*userId*/, bool /*isInGameSession*/> memberGameSessionStatuses = new();
if (request.attributes.TryGetValue(
PlayingWithPartyModels.PartyMembersGameSessionStatusesKey, out object value))
{
memberGameSessionStatuses = JObject.FromObject(value).ToObject<Dictionary<string, bool>>();
}
// Update current player game session status.
memberGameSessionStatuses[GameData.CachedPlayerState.PlayerId] = isInGameSession;
request.attributes[PlayingWithPartyModels.PartyMembersGameSessionStatusesKey] = memberGameSessionStatuses;
Session.PatchUpdateParty(partySession.id, request, (patchResult) =>
{
if (result.IsError)
{
BytewarsLogger.LogWarning($"Update party member game session status failed. Error {patchResult.Error.Code}: {patchResult.Error.Message}");
return;
}
BytewarsLogger.Log($"Update party member game session status success. Current status: {isInGameSession}");
cachedInGameStatus = isInGameSession;
});
});
} -
Then, create the function below to validate game session creation. This function checks whether the game session initiator is the party leader and verifies that no other party members are in any active game sessions.
private async UniTask<bool> IsValidToStartPartyGameSession()
{
// If not in party session, no need to validate.
if (!IsInParty)
{
return true;
}
// Only party leader is able to start party game session.
if (!IsPartyLeader)
{
MenuManager.Instance.PushNotification(new PushNotificationModel()
{
Message = PlayingWithPartyModels.PartyGameSessionMemberSafeguardMessage,
UseDefaultIconOnEmpty = false
});
return false;
}
// Get party member game session status.
UniTaskCompletionSource<bool> task = new();
Session.GetPartyDetails(CachedParty.id, (result) =>
{
if (result.IsError)
{
task.TrySetResult(false);
return;
}
// Parse party member game session status.
Dictionary<string /*userId*/, bool /*isInGameSession*/> memberGameSessionStatuses = new();
if (result.Value.attributes.TryGetValue(
PlayingWithPartyModels.PartyMembersGameSessionStatusesKey, out object value))
{
memberGameSessionStatuses = JObject.FromObject(value).ToObject<Dictionary<string, bool>>();
}
// Only validate active members.
string[] activeMembers = result.Value.members.
Where(x => x.StatusV2 == SessionV2MemberStatus.JOINED).
Select(x => x.id).ToArray();
memberGameSessionStatuses = memberGameSessionStatuses
.Where(x => activeMembers.Contains(x.Key))
.ToDictionary(x => x.Key, x => x.Value);
// Check if all member are available for new game session.
bool isAllMemberAvailable = memberGameSessionStatuses.All(x => x.Value == false);
if (!isAllMemberAvailable)
{
MenuManager.Instance.PushNotification(new PushNotificationModel()
{
Message = PlayingWithPartyModels.PartyGameSessionLeaderSafeguardMessage,
UseDefaultIconOnEmpty = false
});
}
task.TrySetResult(isAllMemberAvailable);
});
return await task.Task;
} -
Next, create the function below to validate joining a game session. It checks whether the game session to be joined has sufficient space available.
private async UniTask<bool> IsValidToJoinPartyGameSession(SessionV2GameSession gameSession)
{
if (!IsInParty)
{
return true;
}
if (!await IsValidToStartPartyGameSession())
{
return false;
}
// Count active members with joined status
int activeMemberCount = gameSession.members.Count(m => m.StatusV2 == SessionV2MemberStatus.JOINED);
bool isAvailable = !gameSession.IsFull && gameSession?.configuration.maxPlayers - activeMemberCount >= CachedParty?.members.Length;
// Notify that no more slots to join the session.
if (!isAvailable)
{
MenuManager.Instance.PushNotification(new PushNotificationModel()
{
Message = PlayingWithPartyModels.JoinPartyGameSessionSafeguardMessage,
UseDefaultIconOnEmpty = false
});
}
return isAvailable;
} -
Then, create the function below to validate starting matchmaking. It checks whether the game mode supports playing with a party by verifying that the maximum number of players per team is greater than one.
private async UniTask<bool> IsValidToStartPartyMatchmaking(InGameMode gameMode)
{
if (!IsInParty)
{
return true;
}
if (!await IsValidToStartPartyGameSession())
{
return false;
}
// No need to validate if there is only one member in party.
if (CachedParty?.members.Length <= 1)
{
return true;
}
// Check whether matchmaking with party is supported using the specified game mode.
bool isSupported = gameMode is InGameMode.MatchmakingTeamDeathmatch or InGameMode.CreateMatchTeamDeathmatch;
if (!isSupported)
{
MenuManager.Instance.PushNotification(new PushNotificationModel()
{
Message = PlayingWithPartyModels.PartyMatchmakingSafeguardMessage,
UseDefaultIconOnEmpty = false
});
}
return isSupported;
} -
Now, bind those functions to the related UI elements in the game. In this case, they should be triggered when the player clicks on the Start Matchmaking, Create Game Session, or Join Game Session buttons. In Byte Wars, we have prepared delegates for binding, so you can simply add the code below in the
OnEnable()function.private UnityAction<Scene, LoadSceneMode> onSceneLoaded;
private Action<MenuCanvas> onMenuChanged;
...
private void OnEnable()
{
// ..
OnValidateToStartMatchmaking = IsValidToStartPartyMatchmaking;
OnValidateToStartGameSession = IsValidToStartPartyGameSession;
OnValidateToJoinGameSession = IsValidToJoinPartyGameSession;
onSceneLoaded = (scene, mode) => UpdatePartyMemberGameSessionStatus();
onMenuChanged = (menu) => UpdatePartyMemberGameSessionStatus();
SceneManager.sceneLoaded += onSceneLoaded;
MenuManager.OnMenuChanged += onMenuChanged;
} -
Finally, unbind those functions when the wrapper is deinitialized. To do this, add the code below to the
OnDisable()function.private void OnDisable()
{
// ..
OnValidateToStartMatchmaking = gameMode => UniTask.FromResult(true);
OnValidateToStartGameSession = () => UniTask.FromResult(true);
OnValidateToJoinGameSession = sessionId => UniTask.FromResult(true);
SceneManager.sceneLoaded -= onSceneLoaded;
MenuManager.OnMenuChanged -= onMenuChanged;
} -
Now, when the player tries to access UIs that are related to Byte Wars online sessions, validation will be performed first before executing any features related to playing with a party. These UIs include accessing Quick Play for matchmaking, creating a party session or match session, and browsing game sessions.
-
The
IsValidToStartPartyGameSession()function will be called when the player clicks on Play Online > Create A Session > Create Elimination and Play Online > Create Match Session > [any game mode button] > [any network mode button]. -
The
IsValidToJoinPartyGameSession()function will be called when the player clicks on the Join button from Play Online > Browse Matches. -
The
IsValidToStartPartyMatchmaking()function will be called when the player selects Server Type as matchmaking from Play Online > Quick Play > [any game mode button] > [any network mode button].
-
-
You will see a notification displaying a validation message, such as "Cannot matchmake in Elimination mode when in a party" or "Only the Party Leader can start an Online Session".
Resources
-
The files used in this tutorial section are available in the Unity Byte Wars GitHub repository.