FArchive& operator<<( FArchive& Ar, FUniqueNetIdRepl& UniqueNetId)
{
	int32 Size = UniqueNetId.IsValid() ? UniqueNetId->GetSize() : 0;
	Ar << Size;

	if (Size > 0)
	{
		if (Ar.IsSaving())
		{
			check(UniqueNetId.IsValid());
			FString Contents = UniqueNetId->ToString();
			Ar << Contents;
		}
		else if (Ar.IsLoading())
		{
			FString Contents;
			Ar << Contents;	// that takes care about possible overflow

			// Don't need to distinguish OSS interfaces here with world because we just want the create function below
			IOnlineIdentityPtr IdentityInt = Online::GetIdentityInterface();
			if (IdentityInt.IsValid())
			{
				TSharedPtr<FUniqueNetId> UniqueNetIdPtr = IdentityInt->CreateUniquePlayerId(Contents);
				UniqueNetId.SetUniqueNetId(UniqueNetIdPtr);
			}
		}
	}

	return Ar;
}
/** 
 * Serialize the voice packet data to a buffer 
 *
 * @param Ar buffer to write into
 */
void FVoicePacketImpl::Serialize(class FArchive& Ar)
{
	// Make sure not to overflow the buffer by reading an invalid amount
	if (Ar.IsLoading())
	{
		FString SenderStr;
		Ar << SenderStr;

		// Don't need to distinguish OSS interfaces here with world because we just want the create function below
		IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
		IOnlineIdentityPtr IdentityInt = OnlineSub->GetIdentityInterface();
		if (IdentityInt.IsValid())
		{
			Sender = IdentityInt->CreateUniquePlayerId(SenderStr);
		}

		Ar << Length;
		// Verify the packet is a valid size
		if (Length <= MAX_VOICE_DATA_SIZE)
		{
			Buffer.Empty(Length);
			Buffer.AddUninitialized(Length);
			Ar.Serialize(Buffer.GetData(), Length);

#if DEBUG_VOICE_PACKET_ENCODING
			uint32 CRC = 0;
			Ar << CRC;
			if (CRC != FCrc::MemCrc32(Buffer.GetData(), Length))
			{
				UE_LOG(LogVoice, Warning, TEXT("CRC Mismatch in voice packet"));
				Length = 0;
			}
#endif
		}
		else
		{
			Length = 0;
		}
	}
	else
	{
		check(Sender.IsValid());
		FString SenderStr = Sender->ToString();
		Ar << SenderStr;
		Ar << Length;

		// Always safe to save the data as the voice code prevents overwrites
		Ar.Serialize(Buffer.GetData(), Length);

#if DEBUG_VOICE_PACKET_ENCODING
		uint32 CRC = FCrc::MemCrc32(Buffer.GetData(), Length);
		Ar << CRC;
#endif
	}
}
bool FOnlineSubsystemImpl::HandleFriendExecCommands(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
{
	bool bWasHandled = false;

	if (FParse::Command(&Cmd, TEXT("BLOCK")))
	{
		FString LocalNumStr = FParse::Token(Cmd, false);
		int32 LocalNum = FCString::Atoi(*LocalNumStr);

		FString UserId = FParse::Token(Cmd, false);
		if (UserId.IsEmpty() || LocalNum < 0 || LocalNum > MAX_LOCAL_PLAYERS)
		{
			UE_LOG_ONLINE(Warning, TEXT("usage: FRIEND BLOCK <localnum> <userid>"));
		}
		else
		{
			IOnlineIdentityPtr IdentityInt = GetIdentityInterface();
			if (IdentityInt.IsValid())
			{
				TSharedPtr<const FUniqueNetId> BlockUserId = IdentityInt->CreateUniquePlayerId(UserId);
				IOnlineFriendsPtr FriendsInt = GetFriendsInterface();
				if (FriendsInt.IsValid())
				{
					FriendsInt->BlockPlayer(0, *BlockUserId);
				}
			}
		}
	}
	else if (FParse::Command(&Cmd, TEXT("DUMPBLOCKED")))
	{
		IOnlineFriendsPtr FriendsInt = GetFriendsInterface();
		if (FriendsInt.IsValid())
		{
			FriendsInt->DumpBlockedPlayers();
		}
		bWasHandled = true;
	}

	return bWasHandled;
}
void UUpdateManager::StartPatchCheck()
{
	ensure(ChecksEnabled());

	UGameInstance* GameInstance = GetGameInstance();
	check(GameInstance);
	bool bStarted = false;

	SetUpdateState(EUpdateState::CheckingForPatch);

	IOnlineSubsystem* OnlineSubConsole = IOnlineSubsystem::GetByPlatform();
	if (OnlineSubConsole)
	{
		IOnlineIdentityPtr OnlineIdentityConsole = OnlineSubConsole->GetIdentityInterface();
		if (OnlineIdentityConsole.IsValid())
		{
			ULocalPlayer* LP = GameInstance->GetFirstGamePlayer();
			if (LP != nullptr)
			{
				const int32 ControllerId = LP->GetControllerId();
				TSharedPtr<const FUniqueNetId> UserId = OnlineIdentityConsole->GetUniquePlayerId(ControllerId);
				if (UserId.IsValid())
				{
					bStarted = true;
					OnlineIdentityConsole->GetUserPrivilege(*UserId,
						EUserPrivileges::CanPlayOnline, IOnlineIdentity::FOnGetUserPrivilegeCompleteDelegate::CreateUObject(this, &ThisClass::OnCheckForPatchComplete, true));
				}
				else
				{
					UE_LOG(LogHotfixManager, Warning, TEXT("No valid platform user id when starting patch check!"));
				}
			}
			else
			{
				UE_LOG(LogHotfixManager, Warning, TEXT("No local player to perform check!"));
			}
		}
	}
	else
	{
		if (GameInstance->IsDedicatedServerInstance())
		{
			bStarted = true;
			PatchCheckComplete(EPatchCheckResult::NoPatchRequired);
		}
		else
		{
			UWorld* World = GetWorld();
			IOnlineIdentityPtr IdentityInt = Online::GetIdentityInterface(World);
			if (IdentityInt.IsValid())
			{
				ULocalPlayer* LP = GameInstance->GetFirstGamePlayer();
				if (LP != nullptr)
				{
					const int32 ControllerId = LP->GetControllerId();
					TSharedPtr<const FUniqueNetId> UserId = IdentityInt->GetUniquePlayerId(ControllerId);
					if (!bInitialUpdateFinished && !UserId.IsValid())
					{
						// Invalid user for "before title/login" check, underlying code doesn't need a valid user currently
						UserId = IdentityInt->CreateUniquePlayerId(TEXT("InvalidUser"));
					}

					if (UserId.IsValid())
					{
						bStarted = true;
						IdentityInt->GetUserPrivilege(*UserId,
							EUserPrivileges::CanPlayOnline, IOnlineIdentity::FOnGetUserPrivilegeCompleteDelegate::CreateUObject(this, &ThisClass::OnCheckForPatchComplete, false));
					}
					else
					{
						UE_LOG(LogHotfixManager, Warning, TEXT("No valid user id when starting patch check!"));
					}
				}
				else
				{
					UE_LOG(LogHotfixManager, Warning, TEXT("No local player to perform check!"));
				}
			}
		}
	}

	if (!bStarted)
	{
		// Any failure to call GetUserPrivilege will result in completing the flow via this path
		PatchCheckComplete(EPatchCheckResult::PatchCheckFailure);
	}
}