Files
UnrealEngine/Engine/Plugins/Online/IOS/OnlineSubsystemIOS/Source/Private/OnlineAchievementsInterfaceIOS.cpp
2025-05-18 13:04:45 +08:00

300 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
// Module includes
#include "OnlineAchievementsInterfaceIOS.h"
#include "OnlineSubsystem.h"
// Game Center includes
#include <GameKit/GKAchievement.h>
#include <GameKit/GKAchievementDescription.h>
FOnlineAchievementsIOS::FOnlineAchievementsIOS(class FOnlineSubsystemIOS* InSubsystem)
{
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("FOnlineSubsystemIOS::FOnlineAchievementsIOS()"));
IOSSubsystem = InSubsystem;
}
void FOnlineAchievementsIOS::QueryAchievements(const FUniqueNetId& PlayerId, const FOnQueryAchievementsCompleteDelegate& Delegate)
{
// Make a copy of the delegate so it won't be a reference inside the FIOSAsyncTask
FOnQueryAchievementsCompleteDelegate CopiedDelegate = Delegate;
dispatch_async(dispatch_get_main_queue(), ^
{
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *loadedAchievements, NSError *error)
{
bool bSuccess = (error == NULL);
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("FOnlineSubsystemIOS::loadAchievementsWithCompletionHandler - %d"), bSuccess);
if (bSuccess)
{
// Write the achievements back to our Achievements array
[FIOSAsyncTask CreateTaskWithBlock : ^ bool(void)
{
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("Loaded %d achievements"), [loadedAchievements count]);
Achievements.Empty();
for( int32 AchievementIdx = 0; AchievementIdx < [loadedAchievements count]; AchievementIdx++ )
{
GKAchievement* LoadedAchievement = [loadedAchievements objectAtIndex : AchievementIdx];
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("Loaded achievement: %s"), *FString(LoadedAchievement.identifier));
Achievements.AddZeroed();
FOnlineAchievement& OnlineAchievement = Achievements[AchievementIdx];
OnlineAchievement.Id = LoadedAchievement.identifier;
OnlineAchievement.Progress = LoadedAchievement.percentComplete;
}
CopiedDelegate.ExecuteIfBound(PlayerId, true);
return true;
}];
}
else
{
UE_LOG_ONLINE_ACHIEVEMENTS(Warning, TEXT("Failed to load achievements with error [%d]"), [error code]);
// Report back to the game thread this failed.
[FIOSAsyncTask CreateTaskWithBlock : ^ bool(void)
{
CopiedDelegate.ExecuteIfBound(PlayerId, false);
return true;
}];
}
}
];
});
}
void FOnlineAchievementsIOS::QueryAchievementDescriptions(const FUniqueNetId& PlayerId, const FOnQueryAchievementsCompleteDelegate& Delegate)
{
FOnQueryAchievementsCompleteDelegate CopiedDelegate = Delegate;
dispatch_async(dispatch_get_main_queue(), ^
{
[GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler : ^ (NSArray *descriptions, NSError *error)
{
bool bSuccess = (error == NULL);
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("FOnlineSubsystemIOS::loadAchievementDescriptionsWithCompletionHandler - %d"), bSuccess);
if (bSuccess)
{
// Report back to the game thread whether this succeeded.
[FIOSAsyncTask CreateTaskWithBlock : ^ bool(void)
{
AchievementDescriptions.Empty();
for (GKAchievementDescription* desc in descriptions)
{
FString Id(desc.identifier);
FOnlineAchievementDesc OnlineAchievementDesc;
OnlineAchievementDesc.Title = FText::FromString(desc.title);
OnlineAchievementDesc.LockedDesc = FText::FromString(desc.unachievedDescription);
OnlineAchievementDesc.UnlockedDesc = FText::FromString(desc.achievedDescription);
OnlineAchievementDesc.bIsHidden = desc.hidden;
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("============================================"));
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("Loaded achievement id: %s"), *Id);
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("Loaded achievement title: %s"), *OnlineAchievementDesc.Title.ToString());
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("Loaded achievement locked desc: %s"), *OnlineAchievementDesc.LockedDesc.ToString());
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("Loaded achievement unlocked desc: %s"), *OnlineAchievementDesc.UnlockedDesc.ToString());
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("Loaded achievement hidden: %d"), OnlineAchievementDesc.bIsHidden);
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("============================================"));
AchievementDescriptions.Add(Id, OnlineAchievementDesc);
}
CopiedDelegate.ExecuteIfBound(PlayerId, true);
return true;
}];
}
else
{
UE_LOG_ONLINE_ACHIEVEMENTS(Warning, TEXT("Failed to load achievement descriptions with error [%d]"), [error code]);
// Report the failure back to the game thread
[FIOSAsyncTask CreateTaskWithBlock : ^ bool(void)
{
CopiedDelegate.ExecuteIfBound(PlayerId, false);
return true;
}];
}
}];
});
}
void FOnlineAchievementsIOS::WriteAchievements(const FUniqueNetId& PlayerId, FOnlineAchievementsWriteRef& InWriteObject, const FOnAchievementsWrittenDelegate& Delegate)
{
// Make a copy of the delegate so it won't be a reference inside the FIOSAsyncTask
FOnAchievementsWrittenDelegate CopiedDelegate = Delegate;
// Hold a reference to the write object for the completion handler to write to.
FOnlineAchievementsWriteRef WriteObject = InWriteObject;
WriteObject->WriteState = EOnlineAsyncTaskState::InProgress;
NSMutableArray* UnreportedAchievements = [NSMutableArray arrayWithCapacity:WriteObject->Properties.Num()];
[UnreportedAchievements retain];
int32 StatIdx = 0;
for (FStatPropertyArray::TConstIterator It(WriteObject->Properties); It; ++It)
{
// Access the stat and the value.
const FVariantData& Stat = It.Value();
// Create an achievement object which should be reported to the server.
const FString AchievementName(It.Key());
NSString* AchievementID = [NSString stringWithFString:AchievementName];
GKAchievement* Achievement = [[[GKAchievement alloc] initWithIdentifier:AchievementID] autorelease];
Achievement.showsCompletionBanner = YES;
// Setup the percentage complete with the value we are writing from the variant type
switch (Stat.GetType())
{
case EOnlineKeyValuePairDataType::Int32:
{
int32 Value;
Stat.GetValue(Value);
Achievement.percentComplete = (float)Value;
break;
}
case EOnlineKeyValuePairDataType::Float:
{
float Value;
Stat.GetValue(Value);
Achievement.percentComplete = Value;
break;
}
default:
{
UE_LOG_ONLINE_ACHIEVEMENTS(Error, TEXT("FOnlineSubsystemIOS Trying to write an achievement with incompatible format. Not a float or int"));
break;
}
}
[UnreportedAchievements addObject:Achievement];
}
// flush the achievements to the server
if ([UnreportedAchievements count] > 0)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[GKAchievement reportAchievements : UnreportedAchievements withCompletionHandler : ^ (NSError* Error)
{
if (Error == nil)
{
for (GKAchievement* achievement in UnreportedAchievements)
{
FString AchievementId(achievement.identifier);
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("Successfully reported achievement: %s, isCompleted:%d"), *AchievementId, [achievement isCompleted] ? 1 : 0);
// Report any completed achievements to the game thread
if ([achievement isCompleted])
{
[FIOSAsyncTask CreateTaskWithBlock : ^ bool(void)
{
TriggerOnAchievementUnlockedDelegates(PlayerId, AchievementId);
return true;
}];
}
}
}
else
{
UE_LOG_ONLINE_ACHIEVEMENTS(Warning, TEXT("Failed to report achievements with error[%d]"), [Error code]);
}
// Release any handle of the achievements back to the IOS gc
[UnreportedAchievements release];
// Report whether this succeeded or not back to whoever is listening.
WriteObject->WriteState = (Error == nil) ? EOnlineAsyncTaskState::Done : EOnlineAsyncTaskState::Failed;
// Report back to the game thread whether this succeeded.
[FIOSAsyncTask CreateTaskWithBlock : ^ bool(void)
{
CopiedDelegate.ExecuteIfBound(PlayerId, Error == nil);
return true;
}];
}
];
});
}
else
{
UE_LOG_ONLINE_ACHIEVEMENTS(Warning, TEXT("No achievements were written to be flushed"));
WriteObject->WriteState = EOnlineAsyncTaskState::Failed;
CopiedDelegate.ExecuteIfBound(PlayerId, false);
}
}
EOnlineCachedResult::Type FOnlineAchievementsIOS::GetCachedAchievement(const FUniqueNetId& PlayerId, const FString& AchievementId, FOnlineAchievement& OutAchievement)
{
for (int32 AchievementIdx = 0; AchievementIdx < Achievements.Num(); AchievementIdx++)
{
if (Achievements[AchievementIdx].Id == AchievementId)
{
OutAchievement = Achievements[AchievementIdx];
return EOnlineCachedResult::Success;
}
}
return EOnlineCachedResult::NotFound;
}
EOnlineCachedResult::Type FOnlineAchievementsIOS::GetCachedAchievements(const FUniqueNetId& PlayerId, TArray<FOnlineAchievement>& OutAchievements)
{
// look up achievements for player
OutAchievements = Achievements;
// did we have them cached?
return (OutAchievements.Num() > 0) ? EOnlineCachedResult::Success : EOnlineCachedResult::NotFound;
}
EOnlineCachedResult::Type FOnlineAchievementsIOS::GetCachedAchievementDescription(const FString& AchievementId, FOnlineAchievementDesc& OutAchievementDesc)
{
if (FOnlineAchievementDesc* FoundDesc = AchievementDescriptions.Find(AchievementId))
{
OutAchievementDesc = *FoundDesc;
return EOnlineCachedResult::Success;
}
else
{
return EOnlineCachedResult::NotFound;
}
}
#if !UE_BUILD_SHIPPING
bool FOnlineAchievementsIOS::ResetAchievements( const FUniqueNetId& PlayerId )
{
dispatch_async(dispatch_get_main_queue(), ^
{
[GKAchievement resetAchievementsWithCompletionHandler : ^(NSError *error)
{
bool bSuccess = error == NULL;
UE_LOG_ONLINE_ACHIEVEMENTS(Display, TEXT("FOnlineAchievementsIOS::ResetAchievements - %d"), bSuccess ? 1 : 0);
if (bSuccess)
{
// Wipe out the achievement descriptions back on the game thread.
[FIOSAsyncTask CreateTaskWithBlock : ^ bool(void)
{
Achievements.Empty();
AchievementDescriptions.Empty();
return true;
}];
}
}
];
});
return true;
};
#endif // !UE_BUILD_SHIPPING