前回 で動作確認用のプロジェクトのセットアップをしました。
まずは SDK で確認したような認証部分から見ていきたいと思います。
Epic Online Services サンプルの AuthAndFriends のコードリーディング ⑤ - You are done!
上記で確認した内容のうち、今回は EOS_Platform_Tick
について見ていきたいと思います。
というのも、これが実行されていないと EOS へのリクエストの Callback が実行されないので、これがどこで実行されているか確認しておきたいためです。
EOS_Platform_Tick の呼び出し
EOSShared
Plugin の EOSSDKManager.cpp
で呼び出されています。
bool FEOSSDKManager::Tick(float) { //LLM_SCOPE(ELLMTag::EOSSDK); // TODO for (EOS_HPlatform PlatformHandle : PlatformHandles) { QUICK_SCOPE_CYCLE_COUNTER(FEOSSDKManager_Tick); EOS_Platform_Tick(PlatformHandle); } return true; } void FEOSPlatformHandle::Tick() { QUICK_SCOPE_CYCLE_COUNTER(FEOSPlatformHandle_Tick); EOS_Platform_Tick(PlatformHandle); }
QUICK_SCOPE_CYCLE_COUNTER
に関しては以下を参照。
統計システムの概要 | Unreal Engine ドキュメント
後者の単独の PlatformHandle
を呼び出している方は、 VoiceChat の方から呼び出されているので、今回は省略します。前者の FEOSSDKManager::Tick
を中心に見ていきます。
FEOSSDKManager::Tick の呼び出し
同じファイルの以下のメソッドから呼び出されています。
IEOSPlatformHandlePtr FEOSSDKManager::CreatePlatform(const EOS_Platform_Options& PlatformOptions) { IEOSPlatformHandlePtr SharedPlatform; if (IsInitialized()) { const EOS_HPlatform PlatformHandle = EOS_Platform_Create(&PlatformOptions); if (PlatformHandle) { PlatformHandles.Emplace(PlatformHandle); SharedPlatform = MakeShared<FEOSPlatformHandle, ESPMode::ThreadSafe>(*this, PlatformHandle); if (!TickerHandle.IsValid()) { TickerHandle = FTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateRaw(this, &FEOSSDKManager::Tick), 0.0f); } } else { UE_LOG(LogEOSSDK, Warning, TEXT("FEOSSDKManager::CreatePlatform failed. EosPlatformHandle=nullptr")); } } else { UE_LOG(LogEOSSDK, Warning, TEXT("FEOSSDKManager::CreatePlatform failed. SDK not initialized")); } return SharedPlatform; }
EOS_Platform_Create
Epic Online Services サンプルの AuthAndFriends のコードリーディング ② - You are done!
上記でも触れていますが、SDK の Sample でも EOS_Platform_Create
を呼び出しています。
加えて SDK の Sample では、main ループの中で EOS_Platform_Tick
も呼び出されています。
では、Unreal Engine としては、どうやってループ処理に EOS_Platform_Tick
が組み込まれているのでしょうか。
FTicker
- FTicker::GetCoreTicker | Unreal Engine Documentation
- FTicker::AddTicker | Unreal Engine Documentation
FTicker
に今回の処理が追加されています。FTicker
については以下のスライドの 24 ページ目に説明があります。
処理の起点となっている FEngineLoop::Tick
に紐付いているようです。つまり、EOS_Platform_Tick
が期待通り呼び出されていることが確認できました。
UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について
ここまでで、一旦今回の目的は果たせたのですが、ついでに OnlineSubsytemEOS
Plugin の初期化に関して掴んでおきたいと思います。
FEOSSDKManager::CreatePlatform 以降の呼び出し階層
- FEOSSDKManager::CreatePlatform - FEOSHelpers::CreatePlatform - FOnlineSubsystemEOS::PlatformCreate - FOnlineSubsystemEOS::Init - FOnlineFactoryEOS::CreateSubsystem ---------------- 以降は OnlineSubsystem Plugin ------ - FOnlineSubsystemModule::GetOnlineSubsystem
以降は気になる部分だけ見ていきます。
FOnlineSubsystemEOS::PlatformCreate
/** Common method for creating the EOS platform */ bool FOnlineSubsystemEOS::PlatformCreate() { FString ArtifactName; FParse::Value(FCommandLine::Get(), TEXT("EpicApp="), ArtifactName); // Find the settings for this artifact FEOSArtifactSettings ArtifactSettings; if (!UEOSSettings::GetSettingsForArtifact(ArtifactName, ArtifactSettings)) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS::PlatformCreate() failed to find artifact settings object for artifact (%s)"), *ArtifactName); return false; } // Create platform instance FEOSPlatformOptions PlatformOptions; FCStringAnsi::Strncpy(PlatformOptions.ClientIdAnsi, TCHAR_TO_UTF8(*ArtifactSettings.ClientId), EOS_OSS_STRING_BUFFER_LENGTH); FCStringAnsi::Strncpy(PlatformOptions.ClientSecretAnsi, TCHAR_TO_UTF8(*ArtifactSettings.ClientSecret), EOS_OSS_STRING_BUFFER_LENGTH); FCStringAnsi::Strncpy(PlatformOptions.ProductIdAnsi, TCHAR_TO_UTF8(*ArtifactSettings.ProductId), EOS_OSS_STRING_BUFFER_LENGTH); FCStringAnsi::Strncpy(PlatformOptions.SandboxIdAnsi, TCHAR_TO_UTF8(*ArtifactSettings.SandboxId), EOS_OSS_STRING_BUFFER_LENGTH); FCStringAnsi::Strncpy(PlatformOptions.DeploymentIdAnsi, TCHAR_TO_UTF8(*ArtifactSettings.DeploymentId), EOS_OSS_STRING_BUFFER_LENGTH); PlatformOptions.bIsServer = IsRunningDedicatedServer() ? EOS_TRUE : EOS_FALSE; PlatformOptions.Reserved = nullptr; FEOSSettings EOSSettings = UEOSSettings::GetSettings(); uint64 OverlayFlags = 0; if (!EOSSettings.bEnableOverlay) { OverlayFlags |= EOS_PF_DISABLE_OVERLAY; } if (!EOSSettings.bEnableSocialOverlay) { OverlayFlags |= EOS_PF_DISABLE_SOCIAL_OVERLAY; } PlatformOptions.Flags = IsRunningGame() ? OverlayFlags : EOS_PF_DISABLE_OVERLAY; // Make the cache directory be in the user's writable area const FString CacheDir = EOSHelpersPtr->PlatformCreateCacheDir(ArtifactName, EOSSettings.CacheDir); FCStringAnsi::Strncpy(PlatformOptions.CacheDirectoryAnsi, TCHAR_TO_UTF8(*CacheDir), EOS_OSS_STRING_BUFFER_LENGTH); FCStringAnsi::Strncpy(PlatformOptions.EncryptionKeyAnsi, TCHAR_TO_UTF8(*ArtifactSettings.EncryptionKey), EOS_ENCRYPTION_KEY_MAX_BUFFER_LEN); #if WITH_EOS_RTC EOS_Platform_RTCOptions RtcOptions = { 0 }; RtcOptions.ApiVersion = EOS_PLATFORM_RTCOPTIONS_API_LATEST; RtcOptions.PlatformSpecificOptions = nullptr; PlatformOptions.RTCOptions = &RtcOptions; #endif EOSPlatformHandle = EOSHelpersPtr->CreatePlatform(PlatformOptions); if (EOSPlatformHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS::PlatformCreate() failed to init EOS platform")); return false; } return true; }
UEOSSettings::GetSettingsForArtifact
の部分は以下なのですが、Project Settings の EOS Settings で Artifacts に設定した内容を取得しているのが分かります。
bool UEOSSettings::GetSettingsForArtifact(const FString& ArtifactName, FEOSArtifactSettings& OutSettings) { return UEOSSettings::ManualGetSettingsForArtifact(ArtifactName, OutSettings); } bool UEOSSettings::ManualGetSettingsForArtifact(const FString& ArtifactName, FEOSArtifactSettings& OutSettings) { static TOptional<FString> CachedDefaultArtifactName; static TOptional<TArray<FEOSArtifactSettings>> CachedArtifactSettings; if (!CachedDefaultArtifactName.IsSet()) { CachedDefaultArtifactName.Emplace(); GConfig->GetString(INI_SECTION, TEXT("DefaultArtifactName"), *CachedDefaultArtifactName, GEngineIni); } if (!CachedArtifactSettings.IsSet()) { CachedArtifactSettings.Emplace(); TArray<FString> Artifacts; GConfig->GetArray(INI_SECTION, TEXT("Artifacts"), Artifacts, GEngineIni); for (const FString& Line : Artifacts) { FEOSArtifactSettings Artifact; Artifact.ParseRawArrayEntry(Line); CachedArtifactSettings->Add(Artifact); } } // 略
以降も設定値を取得し FEOSPlatformOptions
を組み立て、CreatePlatform
に渡しているのが分かります。
FOnlineSubsystemEOS::Init
bool FOnlineSubsystemEOS::Init() { // Determine if we are the default and if we're the platform OSS FString DefaultOSS; GConfig->GetString(TEXT("OnlineSubsystem"), TEXT("DefaultPlatformService"), DefaultOSS, GEngineIni); FString PlatformOSS; GConfig->GetString(TEXT("OnlineSubsystem"), TEXT("NativePlatformService"), PlatformOSS, GEngineIni); bIsDefaultOSS = DefaultOSS == TEXT("EOS"); bIsPlatformOSS = PlatformOSS == TEXT("EOS"); // Check for being launched by EGS bWasLaunchedByEGS = FParse::Param(FCommandLine::Get(), TEXT("EpicPortal")); FEOSSettings EOSSettings = UEOSSettings::GetSettings(); if (!IsRunningDedicatedServer() && IsRunningGame() && !bWasLaunchedByEGS && EOSSettings.bShouldEnforceBeingLaunchedByEGS) { FString ArtifactName; FParse::Value(FCommandLine::Get(), TEXT("EpicApp="), ArtifactName); UE_LOG_ONLINE(Warning, TEXT("FOnlineSubsystemEOS::Init() relaunching artifact (%s) via the store"), *ArtifactName); FPlatformProcess::LaunchURL(*FString::Printf(TEXT("com.epicgames.launcher://store/product/%s?action=launch&silent=true"), *ArtifactName), nullptr, nullptr); FPlatformMisc::RequestExit(false); return false; } EOSSDKManager = IEOSSDKManager::Get(); if (!EOSSDKManager) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS::Init() failed to get EOSSDKManager interface")); return false; } if (!PlatformCreate()) { return false; } // Get handles for later use AuthHandle = EOS_Platform_GetAuthInterface(*EOSPlatformHandle); if (AuthHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get auth handle")); return false; } UserInfoHandle = EOS_Platform_GetUserInfoInterface(*EOSPlatformHandle); if (UserInfoHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get user info handle")); return false; } UIHandle = EOS_Platform_GetUIInterface(*EOSPlatformHandle); if (UIHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get UI handle")); return false; } FriendsHandle = EOS_Platform_GetFriendsInterface(*EOSPlatformHandle); if (FriendsHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get friends handle")); return false; } PresenceHandle = EOS_Platform_GetPresenceInterface(*EOSPlatformHandle); if (PresenceHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get presence handle")); return false; } ConnectHandle = EOS_Platform_GetConnectInterface(*EOSPlatformHandle); if (ConnectHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get connect handle")); return false; } SessionsHandle = EOS_Platform_GetSessionsInterface(*EOSPlatformHandle); if (SessionsHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get sessions handle")); return false; } StatsHandle = EOS_Platform_GetStatsInterface(*EOSPlatformHandle); if (StatsHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get stats handle")); return false; } LeaderboardsHandle = EOS_Platform_GetLeaderboardsInterface(*EOSPlatformHandle); if (LeaderboardsHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get leaderboards handle")); return false; } MetricsHandle = EOS_Platform_GetMetricsInterface(*EOSPlatformHandle); if (MetricsHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get metrics handle")); return false; } AchievementsHandle = EOS_Platform_GetAchievementsInterface(*EOSPlatformHandle); if (AchievementsHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get achievements handle")); return false; } P2PHandle = EOS_Platform_GetP2PInterface(*EOSPlatformHandle); if (P2PHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get p2p handle")); return false; } // Disable ecom if not part of EGS if (bWasLaunchedByEGS) { EcomHandle = EOS_Platform_GetEcomInterface(*EOSPlatformHandle); if (EcomHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get ecom handle")); return false; } StoreInterfacePtr = MakeShareable(new FOnlineStoreEOS(this)); } TitleStorageHandle = EOS_Platform_GetTitleStorageInterface(*EOSPlatformHandle); if (TitleStorageHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get title storage handle")); return false; } PlayerDataStorageHandle = EOS_Platform_GetPlayerDataStorageInterface(*EOSPlatformHandle); if (PlayerDataStorageHandle == nullptr) { UE_LOG_ONLINE(Error, TEXT("FOnlineSubsystemEOS: failed to init EOS platform, couldn't get player data storage handle")); return false; } SocketSubsystem = MakeShareable(new FSocketSubsystemEOS(this)); FString ErrorMessage; SocketSubsystem->Init(ErrorMessage); UserManager = MakeShareable(new FUserManagerEOS(this)); SessionInterfacePtr = MakeShareable(new FOnlineSessionEOS(this)); // Set the bucket id to use for all sessions based upon the name and version to avoid upgrade issues SessionInterfacePtr->Init(EOSSDKManager->GetProductName() + TEXT("_") + EOSSDKManager->GetProductVersion()); StatsInterfacePtr = MakeShareable(new FOnlineStatsEOS(this)); LeaderboardsInterfacePtr = MakeShareable(new FOnlineLeaderboardsEOS(this)); AchievementsInterfacePtr = MakeShareable(new FOnlineAchievementsEOS(this)); TitleFileInterfacePtr = MakeShareable(new FOnlineTitleFileEOS(this)); UserCloudInterfacePtr = MakeShareable(new FOnlineUserCloudEOS(this)); // We initialized ok so we can tick StartTicker(); return true; }
見ての通りですが、PlatformCreate
後に、取得した PlatformHandle
を使って、利用する EOS のインターフェースの Handle を次々に取得しています。
このクラスがかなりの依存性を一手に引き受けているようです。
この FOnlineSubsystemEOS
クラスが以下のように OnlineSubsystemEOS の主要な公開 API になっているので、使い勝手として集約されているのかもしれません。(この辺の設計のプラクティスはわかっていないです)
class ONLINESUBSYSTEMEOS_API FOnlineSubsystemEOS : public FOnlineSubsystemImpl
余談ですが、継承している FOnlineSubsystemImpl
は以下の定義になっており FTickerObjectBase
も実装する形になっています。内部では FTicker::GetCoreTicker()
が使われており、ExecuteNextTick
関数を通して Callback の実行を登録できるようになっています。
/** * FOnlineSubsystemImpl - common functionality to share across online platforms, not intended for direct use */ class ONLINESUBSYSTEM_API FOnlineSubsystemImpl : public IOnlineSubsystem , public FTickerObjectBase
まとめ
今回は OnlineSubsystemEOS の中で EOS_Platform_Tick
がどのように呼び出されているかを SDK の Sample と比較しつつ確認しました。
また、初期化部分を簡単に眺めることで、FOnlineSubsystemEOS
クラスがプラグインの API として多くの役割を持っていることが確認できました。
次回は認証部分が SDK の Sample と比較してどのように実装されているかを確認していきたいと思います。