int32 UPartyBeaconState::GetTeamForCurrentPlayer(const FUniqueNetId& PlayerId) const
{
	int32 TeamNum = INDEX_NONE;
	if (PlayerId.IsValid())
	{
		for (int32 ResIdx = 0; ResIdx < Reservations.Num(); ResIdx++)
		{
			const FPartyReservation& Reservation = Reservations[ResIdx];
			for (int32 PlayerIdx = 0; PlayerIdx < Reservation.PartyMembers.Num(); PlayerIdx++)
			{
				// find the player id in the existing list of reservations
				if (*Reservation.PartyMembers[PlayerIdx].UniqueId == PlayerId)
				{
					TeamNum = Reservation.TeamNum;
					break;
				}
			}
		}

		UE_LOG(LogBeacon, Display, TEXT("Assigning player %s to team %d"),
			*PlayerId.ToString(),
			TeamNum);
	}
	else
	{
		UE_LOG(LogBeacon, Display, TEXT("Invalid player when attempting to find team assignment"));
	}

	return TeamNum;
}
bool FOnlineAchievementsNull::ResetAchievements(const FUniqueNetId& PlayerId)
{
    if (!ReadAchievementsFromConfig())
    {
        // we don't have achievements
        UE_LOG_ONLINE(Warning, TEXT("No achievements have been configured"));
        return false;
    }

    FUniqueNetIdString NullId(PlayerId);
    TArray<FOnlineAchievement> * PlayerAch = PlayerAchievements.Find(NullId);
    if (NULL == PlayerAch)
    {
        // achievements haven't been read for a player
        UE_LOG_ONLINE(Warning, TEXT("Could not find achievements for player %s"), *PlayerId.ToString());
        return false;
    }

    // treat each achievement as unlocked
    const int32 AchNum = PlayerAch->Num();
    for (int32 AchIdx = 0; AchIdx < AchNum; ++AchIdx)
    {
        (*PlayerAch)[ AchIdx ].Progress = 0.0;
    }

    return true;
};
	virtual void FireEvent_SendGameInvite(const FUniqueNetId& LocalUserId, const FUniqueNetId& ToUser) const override
	{
		static const FString EventName = TEXT("Social.GameInvite.Send");

		if (AnalyticsProvider.IsValid())
		{
			if (LocalUserId.IsValid())
			{
				TArray<FAnalyticsEventAttribute> Attributes;
				Attributes.Add(FAnalyticsEventAttribute(TEXT("User"), LocalUserId.ToString()));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("Friend"), ToUser.ToString()));
				AddPresenceAttributes(LocalUserId, Attributes);
				AnalyticsProvider->RecordEvent(EventName, Attributes);
			}
		}
	}
/** Resets a player's notification handlers */
void FOnlineNotificationHandler::ResetPlayerNotificationBindings(const FUniqueNetId& PlayerId)
{
	NotificationTypeBindingsMap* FoundPlayerBindings = PlayerBindingMap.Find(PlayerId.ToString());
	if (FoundPlayerBindings)
	{
		FoundPlayerBindings->Reset();
	}
}
FString FOnlineIdentityFacebook::GetPlayerNickname(const FUniqueNetId& UserId) const
{
	const TSharedRef<FUserOnlineAccountFacebook>* FoundUserAccount = UserAccounts.Find(UserId.ToString());
	if (FoundUserAccount != NULL)
	{
		const TSharedRef<FUserOnlineAccountFacebook>& UserAccount = *FoundUserAccount;
		return UserAccount->RealName;
	}
	return TEXT("");
}
	virtual void FireEvent_AddFriend(const FUniqueNetId& LocalUserId, const FString& FriendName, const FUniqueNetId& FriendId, EFindFriendResult::Type Result, bool bRecentPlayer) const override
	{
		static const FString EventName = TEXT("Social.AddFriend");

		if (AnalyticsProvider.IsValid())
		{
			if (LocalUserId.IsValid())
			{
				TArray<FAnalyticsEventAttribute> Attributes;
				Attributes.Add(FAnalyticsEventAttribute(TEXT("User"), LocalUserId.ToString()));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("Friend"), FriendId.ToString()));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("FriendName"), FriendName));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("Result"), EFindFriendResult::ToString(Result)));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("bRecentPlayer"), bRecentPlayer));
				AddPresenceAttributes(LocalUserId, Attributes);
				AnalyticsProvider->RecordEvent(EventName, Attributes);
			}
		}
	}
TSharedPtr<FUserOnlineAccount> FOnlineIdentityAmazon::GetUserAccount(const FUniqueNetId& UserId) const
{
	TSharedPtr<FUserOnlineAccount> Result;

	const TSharedRef<FUserOnlineAccountAmazon>* FoundUserAccount = UserAccounts.Find(UserId.ToString());
	if (FoundUserAccount != NULL)
	{
		Result = *FoundUserAccount;
	}

	return Result;
}
FDelegateHandle FOnlineNotificationHandler::AddPlayerNotificationBinding_Handle(const FUniqueNetId& PlayerId, FString NotificationType, const FOnlineNotificationBinding& NewBinding)
{
	if (!NewBinding.NotificationDelegate.IsBound())
	{
		UE_LOG(LogOnline, Error, TEXT("Adding empty notification binding for type %s"), *NotificationType);
		return FDelegateHandle();
	}

	NotificationTypeBindingsMap& FoundPlayerBindings = PlayerBindingMap.FindOrAdd(PlayerId.ToString());
	TArray<FOnlineNotificationBinding>& FoundPlayerTypeBindings = FoundPlayerBindings.FindOrAdd(NotificationType);
	FoundPlayerTypeBindings.Add(NewBinding);
	return FoundPlayerTypeBindings.Last().NotificationDelegate.GetHandle();
}
	virtual void FireEvent_RecordToggleChat(const FUniqueNetId& LocalUserId, const FString& Channel, bool bEnabled) const override
	{
		static const FString EventName = TEXT("Social.Chat.Toggle");
		if (AnalyticsProvider.IsValid())
		{
			if (LocalUserId.IsValid())
			{
				TArray<FAnalyticsEventAttribute> Attributes;
				Attributes.Add(FAnalyticsEventAttribute(TEXT("User"), LocalUserId.ToString()));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("Channel"), Channel));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("bEnabled"), bEnabled));
				AddPresenceAttributes(LocalUserId, Attributes);
				AnalyticsProvider->RecordEvent(EventName, Attributes);
			}
		}
	}
	virtual void FireEvent_FriendRemoved(const FUniqueNetId& LocalUserId, const IFriendItem& Friend, const FString& RemoveReason) const override
	{
		static const FString EventName = TEXT("Social.FriendRemoved");
		if (AnalyticsProvider.IsValid())
		{
			if (LocalUserId.IsValid())
			{
				TArray<FAnalyticsEventAttribute> Attributes;
				Attributes.Add(FAnalyticsEventAttribute(TEXT("User"), LocalUserId.ToString()));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("Friend"), Friend.GetUniqueID()->ToString()));
				Attributes.Add(FAnalyticsEventAttribute(TEXT("RemoveReason"), RemoveReason));
				AddPresenceAttributes(LocalUserId, Attributes);
				AnalyticsProvider->RecordEvent(EventName, Attributes);
			}
		}
	}
/** Remove the player notification handler for a type */
void FOnlineNotificationHandler::RemovePlayerNotificationBinding(const FUniqueNetId& PlayerId, FString NotificationType, FDelegateHandle RemoveHandle)
{
	int32 BindingsRemoved = 0;

	NotificationTypeBindingsMap* FoundPlayerBindings = PlayerBindingMap.Find(PlayerId.ToString());

	if (FoundPlayerBindings)
	{
		TArray<FOnlineNotificationBinding>* FoundPlayerTypeBindings = FoundPlayerBindings->Find(NotificationType);

		if (FoundPlayerTypeBindings)
		{
			BindingsRemoved = FoundPlayerTypeBindings->RemoveAll([=](const FOnlineNotificationBinding& Binding) { return Binding.NotificationDelegate.GetHandle() == RemoveHandle; });
		}
	}

	if (BindingsRemoved == 0)
	{
		UE_LOG_ONLINE(Error, TEXT("Attempted to remove binding that could not be found for player %s type %s"), *PlayerId.ToDebugString(), *NotificationType);
	}
}
/** Add a notification binding for a type */
void FOnlineNotificationHandler::AddPlayerNotificationBinding(const FUniqueNetId& PlayerId, FString NotificationType, const FOnlineNotificationBinding& NewBinding)
{
	if (!NewBinding.NotificationDelegate.IsBound())
	{
		UE_LOG(LogOnline, Error, TEXT("Adding empty notification binding for type %s"), *NotificationType);
		return;
	}

	NotificationTypeBindingsMap& FoundPlayerBindings = PlayerBindingMap.FindOrAdd(PlayerId.ToString());

	TArray<FOnlineNotificationBinding>& FoundPlayerTypeBindings = FoundPlayerBindings.FindOrAdd(NotificationType);


	for (int32 i = 0; i < FoundPlayerTypeBindings.Num(); i++)
	{
		if (FoundPlayerTypeBindings[i].NotificationDelegate.DEPRECATED_Compare(NewBinding.NotificationDelegate))
		{
			UE_LOG(LogOnline, Error, TEXT("Adding identical notification binding for type %s"), *NotificationType);
			return;
		}
	}

	FoundPlayerTypeBindings.Add(NewBinding);
}
FString FOnlineIdentityNull::GetPlayerNickname(const FUniqueNetId& UserId) const
{
	return UserId.ToString();
}
void FOnlineAchievementsSteam::WriteAchievements(const FUniqueNetId& PlayerId, FOnlineAchievementsWriteRef& WriteObject, const FOnAchievementsWrittenDelegate& Delegate)
{
	if (!bHaveConfiguredAchievements)
	{
		// we don't have achievements
		UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been configured in .ini"));

		WriteObject->WriteState = EOnlineAsyncTaskState::Failed;
		Delegate.ExecuteIfBound(PlayerId, false);
		return;
	}

	FUniqueNetIdSteam SteamId(PlayerId);
	// check if this is our player (cannot report for someone else)
	if (SteamUser() == NULL || SteamUser()->GetSteamID() != SteamId)
	{
		// we don't have achievements
		UE_LOG_ONLINE(Warning, TEXT("Cannot report Steam achievements for non-local player %s"), *PlayerId.ToString());

		WriteObject->WriteState = EOnlineAsyncTaskState::Failed;
		Delegate.ExecuteIfBound(PlayerId, false);
		return;
	}

	const TArray<FOnlineAchievement> * PlayerAch = PlayerAchievements.Find(SteamId);
	if (NULL == PlayerAch)
	{
		// achievements haven't been read for a player
		UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been read for player %s"), *PlayerId.ToString());

		WriteObject->WriteState = EOnlineAsyncTaskState::Failed;
		Delegate.ExecuteIfBound(PlayerId, false);
		return;
	}

	const int32 AchNum = PlayerAch->Num();
	for (FStatPropertyArray::TConstIterator It(WriteObject->Properties); It; ++It)
	{
		const FString AchievementId = It.Key().ToString();
		UE_LOG_ONLINE(Verbose, TEXT("WriteObject AchievementId: '%s'"), *AchievementId);
		for (int32 AchIdx = 0; AchIdx < AchNum; ++AchIdx)
		{
			if ((*PlayerAch)[ AchIdx ].Id == AchievementId)
			{
				// do not unlock it now, but after a successful write
#if !UE_BUILD_SHIPPING
				float Value = 0.0f;
				It.Value().GetValue(Value);
				if (Value <= 0.0f)
				{
					UE_LOG_ONLINE(Verbose, TEXT("Resetting achievement '%s'"), *AchievementId);
					SteamUserStats()->ClearAchievement(TCHAR_TO_UTF8(*AchievementId));
				}
				else
				{
#endif // !UE_BUILD_SHIPPING

					UE_LOG_ONLINE(Verbose, TEXT("Setting achievement '%s'"), *AchievementId);
					SteamUserStats()->SetAchievement(TCHAR_TO_UTF8(*AchievementId));

#if !UE_BUILD_SHIPPING
				}
#endif // !UE_BUILD_SHIPPING
			
				break;
			}
		}
	}

	StatsInt->WriteAchievementsInternal(SteamId, WriteObject, Delegate);
};
bool FOnlineAchievementsSteam::ResetAchievements(const FUniqueNetId& PlayerId)
{
	if (!bHaveConfiguredAchievements)
	{
		// we don't have achievements
		UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been configured in .ini"));
		return false;
	}

	FUniqueNetIdSteam SteamId(PlayerId);
	// check if this is our player (cannot report for someone else)
	if (SteamUser() == NULL || SteamUser()->GetSteamID() != SteamId)
	{
		// we don't have achievements
		UE_LOG_ONLINE(Warning, TEXT("Cannot clear Steam achievements for non-local player %s"), *PlayerId.ToString());
		return false;
	}

	const TArray<FOnlineAchievement> * PlayerAch = PlayerAchievements.Find(SteamId);
	if (NULL == PlayerAch)
	{
		// achievements haven't been read for a player
		UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been read for player %s"), *PlayerId.ToString());
		return false;
	}

	const int32 AchNum = PlayerAch->Num();
	for (int32 AchIdx = 0; AchIdx < AchNum; ++AchIdx)
	{
		SteamUserStats()->ClearAchievement(TCHAR_TO_UTF8(*(*PlayerAch)[ AchIdx ].Id));
	}

	// TODO: provide a separate method to just store stats?
	StatsInt->FlushLeaderboards(FName(TEXT("UNUSED")));
	return true;
};
EOnlineCachedResult::Type FOnlineAchievementsSteam::GetCachedAchievements(const FUniqueNetId& PlayerId, TArray<FOnlineAchievement> & OutAchievements)
{
	if (!bHaveConfiguredAchievements)
	{
		// we don't have achievements
		UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been configured in .ini"));
		return EOnlineCachedResult::NotFound;
	}

	const TArray<FOnlineAchievement> * PlayerAch = PlayerAchievements.Find(FUniqueNetIdSteam (PlayerId));
	if (NULL == PlayerAch)
	{
		// achievements haven't been read for a player
		UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been read for player %s"), *PlayerId.ToString());
		return EOnlineCachedResult::NotFound;
	}

	OutAchievements = *PlayerAch;
	return EOnlineCachedResult::Success;
};
EOnlineCachedResult::Type FOnlineAchievementsSteam::GetCachedAchievement(const FUniqueNetId& PlayerId, const FString& AchievementId, FOnlineAchievement& OutAchievement)
{
	if (!bHaveConfiguredAchievements)
	{
		// we don't have achievements
		UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been configured in .ini"));
		return EOnlineCachedResult::NotFound;
	}

	const TArray<FOnlineAchievement> * PlayerAch = PlayerAchievements.Find(FUniqueNetIdSteam (PlayerId));
	if (NULL == PlayerAch)
	{
		// achievements haven't been read for a player
		UE_LOG_ONLINE(Warning, TEXT("Steam achievements have not been read for player %s"), *PlayerId.ToString());
		return EOnlineCachedResult::NotFound;
	}

	const int32 AchNum = PlayerAch->Num();
	for (int32 AchIdx = 0; AchIdx < AchNum; ++AchIdx)
	{
		if ((*PlayerAch)[ AchIdx ].Id == AchievementId)
		{
			OutAchievement = (*PlayerAch)[ AchIdx ];
			return EOnlineCachedResult::Success;
		}
	}

	// no such achievement
	UE_LOG_ONLINE(Warning, TEXT("Could not find Steam achievement '%s' for player %s"), *AchievementId, *PlayerId.ToString());
	return EOnlineCachedResult::NotFound;
};