bool IsPlayerInSessionImpl(IOnlineSession* SessionInt, FName SessionName, const FUniqueNetId& UniqueId)
{
	bool bFound = false;
	FNamedOnlineSession* Session = SessionInt->GetNamedSession(SessionName);
	if (Session != NULL)
	{
		const bool bIsSessionOwner = *Session->OwningUserId == UniqueId;

		FUniqueNetIdMatcher PlayerMatch(UniqueId);
		if (bIsSessionOwner || 
			Session->RegisteredPlayers.FindMatch(PlayerMatch) != INDEX_NONE)
		{
			bFound = true;
		}
	}
	return bFound;
}
bool FOnlineSessionNull::UnregisterPlayers(FName SessionName, const TArray< TSharedRef<const FUniqueNetId> >& Players)
{
	bool bSuccess = true;

	FNamedOnlineSession* Session = GetNamedSession(SessionName);
	if (Session)
	{
		for (int32 PlayerIdx=0; PlayerIdx < Players.Num(); PlayerIdx++)
		{
			const TSharedRef<const FUniqueNetId>& PlayerId = Players[PlayerIdx];

			FUniqueNetIdMatcher PlayerMatch(*PlayerId);
			int32 RegistrantIndex = Session->RegisteredPlayers.IndexOfByPredicate(PlayerMatch);
			if (RegistrantIndex != INDEX_NONE)
			{
				Session->RegisteredPlayers.RemoveAtSwap(RegistrantIndex);
				UnregisterVoice(*PlayerId);

				// update number of open connections
				if (Session->NumOpenPublicConnections < Session->SessionSettings.NumPublicConnections)
				{
					Session->NumOpenPublicConnections++;
				}
				else if (Session->NumOpenPrivateConnections < Session->SessionSettings.NumPrivateConnections)
				{
					Session->NumOpenPrivateConnections++;
				}
			}
			else
			{
				UE_LOG_ONLINE(Warning, TEXT("Player %s is not part of session (%s)"), *PlayerId->ToDebugString(), *SessionName.ToString());
			}
		}
	}
	else
	{
		UE_LOG_ONLINE(Warning, TEXT("No game present to leave for session (%s)"), *SessionName.ToString());
		bSuccess = false;
	}

	TriggerOnUnregisterPlayersCompleteDelegates(SessionName, Players, bSuccess);
	return bSuccess;
}
bool FOnlineSessionNull::RegisterPlayers(FName SessionName, const TArray< TSharedRef<const FUniqueNetId> >& Players, bool bWasInvited)
{
	bool bSuccess = false;
	FNamedOnlineSession* Session = GetNamedSession(SessionName);
	if (Session)
	{
		bSuccess = true;

		for (int32 PlayerIdx=0; PlayerIdx<Players.Num(); PlayerIdx++)
		{
			const TSharedRef<const FUniqueNetId>& PlayerId = Players[PlayerIdx];

			FUniqueNetIdMatcher PlayerMatch(*PlayerId);
			if (Session->RegisteredPlayers.IndexOfByPredicate(PlayerMatch) == INDEX_NONE)
			{
				Session->RegisteredPlayers.Add(PlayerId);
				RegisterVoice(*PlayerId);

				// update number of open connections
				if (Session->NumOpenPublicConnections > 0)
				{
					Session->NumOpenPublicConnections--;
				}
				else if (Session->NumOpenPrivateConnections > 0)
				{
					Session->NumOpenPrivateConnections--;
				}
			}
			else
			{
				RegisterVoice(*PlayerId);
				UE_LOG_ONLINE(Log, TEXT("Player %s already registered in session %s"), *PlayerId->ToDebugString(), *SessionName.ToString());
			}			
		}
	}
	else
	{
		UE_LOG_ONLINE(Warning, TEXT("No game present to join for session (%s)"), *SessionName.ToString());
	}

	TriggerOnRegisterPlayersCompleteDelegates(SessionName, Players, bSuccess);
	return bSuccess;
}
TSharedPtr<FVoicePacket> FOnlineVoiceSteam::SerializeRemotePacket(FArchive& Ar)
{
	TSharedPtr<FVoicePacketSteam> NewPacket = MakeShareable(new FVoicePacketSteam());
	NewPacket->Serialize(Ar);
	if (Ar.IsError() == false && NewPacket->GetBufferSize() > 0)
	{
		if (!SteamSubsystem->IsDedicated())
		{
			FUniqueNetIdMatcher PlayerMatch(*NewPacket->GetSender());
			if (MuteList.IndexOfByPredicate(PlayerMatch) == INDEX_NONE)
			{
				VoiceData.RemotePackets.Add(NewPacket);
			}
		}

		return NewPacket;
	}

	return NULL;
}
TSharedPtr<FVoicePacket> FOnlineVoiceImpl::SerializeRemotePacket(FArchive& Ar)
{
	TSharedPtr<FVoicePacketImpl> NewPacket = MakeShareable(new FVoicePacketImpl());
	NewPacket->Serialize(Ar);
	if (Ar.IsError() == false && NewPacket->GetBufferSize() > 0)
	{
		if (!IsRunningDedicatedServer())
		{
			FUniqueNetIdMatcher PlayerMatch(*NewPacket->GetSender());
			if (MuteList.FindMatch(PlayerMatch) == INDEX_NONE)
			{
				VoiceData.RemotePackets.Add(NewPacket);
			}
		}

		return NewPacket;
	}

	return NULL;
}
void APartyBeaconHost::Tick(float DeltaTime)
{
	IOnlineSessionPtr SessionsInt = Online::GetSessionInterface();
	if (SessionsInt.IsValid())
	{
		FNamedOnlineSession* Session = SessionsInt->GetNamedSession(SessionName);
		if (Session)
		{
			TArray< TSharedPtr<FUniqueNetId> > PlayersToLogout;
			for (int32 ResIdx=0; ResIdx < Reservations.Num(); ResIdx++)
			{
				FPartyReservation& PartyRes = Reservations[ResIdx];

				// Determine if we have a client connection for the reservation 
				bool bIsConnectedReservation = false;
				for (int32 ClientIdx=0; ClientIdx < ClientActors.Num(); ClientIdx++)
				{
					APartyBeaconClient* Client = Cast<APartyBeaconClient>(ClientActors[ClientIdx]);
					if (Client)
					{
						const FPartyReservation& ClientRes = Client->GetPendingReservation();
						if (ClientRes.PartyLeader == PartyRes.PartyLeader)
						{
							bIsConnectedReservation = true;
							break;
						}
					}
					else
					{
						UE_LOG(LogBeacon, Error, TEXT("Missing PartyBeaconClient found in ClientActors array"));
						ClientActors.RemoveAtSwap(ClientIdx);
						ClientIdx--;
					}
				}

				// Don't update clients that are still connected
				if (bIsConnectedReservation)
				{
					for (int32 PlayerIdx=0; PlayerIdx < PartyRes.PartyMembers.Num(); PlayerIdx++)
					{
						FPlayerReservation& PlayerEntry = PartyRes.PartyMembers[PlayerIdx];
						PlayerEntry.ElapsedTime = 0.0f;
					}
				}
				// Once a client disconnects, update the elapsed time since they were found as a registrant in the game session
				else
				{
					for (int32 PlayerIdx=0; PlayerIdx < PartyRes.PartyMembers.Num(); PlayerIdx++)
					{
						FPlayerReservation& PlayerEntry = PartyRes.PartyMembers[PlayerIdx];

						// Determine if the player is the owner of the session	
						const bool bIsSessionOwner = Session->OwningUserId.IsValid() && (*Session->OwningUserId == *PlayerEntry.UniqueId);

						// Determine if the player member is registered in the game session
						if (SessionsInt->IsPlayerInSession(SessionName, *PlayerEntry.UniqueId) ||
							// Never timeout the session owner
							bIsSessionOwner)
						{
							FUniqueNetIdMatcher PlayerMatch(*PlayerEntry.UniqueId);
							int32 FoundIdx = PlayersPendingJoin.FindMatch(PlayerMatch);
							if (FoundIdx != INDEX_NONE)
							{
								UE_LOG(LogBeacon, Display, TEXT("Beacon (%s): pending player %s found in session (%s)."),
									*GetName(),
									*PlayerEntry.UniqueId->ToDebugString(),
									*SessionName.ToString());

								// reset elapsed time since found
								PlayerEntry.ElapsedTime = 0.0f;
								// also remove from pending join list
								PlayersPendingJoin.RemoveAtSwap(FoundIdx);
							}
						}
						else
						{
							// update elapsed time
							PlayerEntry.ElapsedTime += DeltaTime;

							// if the player is pending it's initial join then check against TravelSessionTimeoutSecs instead
							FUniqueNetIdMatcher PlayerMatch(*PlayerEntry.UniqueId);
							int32 FoundIdx = PlayersPendingJoin.FindMatch(PlayerMatch);
							const bool bIsPlayerPendingJoin = FoundIdx != INDEX_NONE;
							// if the timeout has been exceeded then add to list of players 
							// that need to be logged out from the beacon
							if ((bIsPlayerPendingJoin && PlayerEntry.ElapsedTime > TravelSessionTimeoutSecs) ||
								(!bIsPlayerPendingJoin && PlayerEntry.ElapsedTime > SessionTimeoutSecs))
							{
								PlayersToLogout.AddUnique(PlayerEntry.UniqueId.GetUniqueNetId());
							}
						}
					}
				}
			}

			// Logout any players that timed out
			for (int32 LogoutIdx=0; LogoutIdx < PlayersToLogout.Num(); LogoutIdx++)
			{
				bool bFound = false;
				const TSharedPtr<FUniqueNetId>& UniqueId = PlayersToLogout[LogoutIdx];
				float ElapsedSessionTime = 0.f;
				for (int32 ResIdx=0; ResIdx < Reservations.Num(); ResIdx++)
				{
					FPartyReservation& PartyRes = Reservations[ResIdx];
					for (int32 PlayerIdx=0; PlayerIdx < PartyRes.PartyMembers.Num(); PlayerIdx++)
					{
						FPlayerReservation& PlayerEntry = PartyRes.PartyMembers[PlayerIdx];
						if (*PlayerEntry.UniqueId == *UniqueId)
						{
							ElapsedSessionTime = PlayerEntry.ElapsedTime;
							bFound = true;
							break;
						}
					}

					if (bFound)
					{
						break;
					}
				}

				UE_LOG(LogBeacon, Display, TEXT("Beacon (%s): player logout due to timeout for %s, elapsed time = %0.3f"),
					*GetName(),
					*UniqueId->ToDebugString(),
					ElapsedSessionTime);
				// Also remove from pending join list
				PlayersPendingJoin.RemoveSingleSwap(UniqueId);
				// let the beacon handle the logout and notifications/delegates
				FUniqueNetIdRepl RemovedId(UniqueId);
				HandlePlayerLogout(RemovedId);
			}
		}
	}
}