I got it working. Had to create a user. Here’s the working code.
.hpp:
// Copyright 2024 David Simoes - All rights reserved.
#pragma once
#include "CoreMinimal.h"
#include <eos_init.h>
#include <eos_common.h>
#include <eos_sdk.h>
#include <eos_achievements.h>
/**
*
*/
class CHRONO_API EosSingleton
{
public:
// Delete copy constructor and assignment operator to prevent copies
EosSingleton(const EosSingleton &) = delete;
EosSingleton &operator=(const EosSingleton &) = delete;
// Static method to access the singleton instance
static EosSingleton &getInstance()
{
static EosSingleton instance; // Guaranteed to be destroyed, instantiated on first use
return instance;
}
void tick();
void UnlockAchievements(const char **AchievementIds, uint32_t AchievementsCount);
private:
EosSingleton();
~EosSingleton();
void InitSdk();
void CreatePlatform();
void AuthLogin(char *token_arg);
void ConnectInterface(EOS_EpicAccountId account_id);
void ConnectLogin(const char *token_arg);
void CreateUser(const EOS_ContinuanceToken &ContinuanceToken);
EOS_ProductUserId LocalProductUserId = nullptr; // not the same as EOS_EpicAccountId
EOS_HAuth AuthHandle = nullptr;
EOS_HPlatform PlatformHandle = nullptr;
EOS_HAchievements AchievementsHandle = nullptr;
EOS_HConnect ConnectionHandle = nullptr;
};
And the .cpp:
// Copyright 2024 David Simoes - All rights reserved.
#include "EosSingleton.h"
#include <eos_sdk.h>
#include <eos_auth.h>
#include <eos_connect.h>
#include <eos_achievements.h>
EosSingleton::EosSingleton()
{
InitSdk();
CreatePlatform();
if (PlatformHandle == nullptr)
{
return;
}
FString token_arg;
if (!FParse::Value(FCommandLine::Get(), TEXT("AUTH_PASSWORD="), token_arg))
{
UE_LOG(LogTemp, Log, TEXT("############### AUTH_PASSWORD not found."));
return;
}
UE_LOG(LogTemp, Log, TEXT("############### AUTH_PASSWORD = %s"), *token_arg);
AchievementsHandle = EOS_Platform_GetAchievementsInterface(PlatformHandle);
AuthHandle = EOS_Platform_GetAuthInterface(PlatformHandle);
ConnectionHandle = EOS_Platform_GetConnectInterface(PlatformHandle);
AuthLogin(TCHAR_TO_UTF8(*token_arg));
UE_LOG(LogTemp, Log, TEXT("############### DONE"));
}
EosSingleton::~EosSingleton()
{
}
void EosSingleton::tick()
{
if (PlatformHandle == nullptr)
{
return;
}
EOS_Platform_Tick(PlatformHandle);
}
void EosSingleton::InitSdk()
{
EOS_InitializeOptions SDKOptions;
SDKOptions.ApiVersion = EOS_INITIALIZE_API_LATEST;
SDKOptions.AllocateMemoryFunction = NULL;
SDKOptions.ReallocateMemoryFunction = NULL;
SDKOptions.ReleaseMemoryFunction = NULL;
SDKOptions.ProductName = "Chrono";
SDKOptions.ProductVersion = "1.1";
SDKOptions.Reserved = NULL;
SDKOptions.SystemInitializeOptions = NULL;
SDKOptions.OverrideThreadAffinity = NULL;
EOS_EResult Result = EOS_Initialize(&SDKOptions);
if (Result != EOS_EResult::EOS_Success && Result != EOS_EResult::EOS_AlreadyConfigured)
{
UE_LOG(LogTemp, Log, TEXT("############### Fatal Error - EOS_Initialize failed. %d"), static_cast<int>(Result));
}
}
void EosSingleton::CreatePlatform()
{
FString SandboxId;
if (!FParse::Value(FCommandLine::Get(), TEXT("epicsandboxid="), SandboxId))
{
UE_LOG(LogTemp, Log, TEXT("############### epicsandboxid not found."));
return;
}
FString DeploymentId;
if (!FParse::Value(FCommandLine::Get(), TEXT("epicdeploymentid="), DeploymentId))
{
UE_LOG(LogTemp, Log, TEXT("############### epicdeploymentid not found."));
return;
}
EOS_Platform_Options PlatformOptions = {};
PlatformOptions.ApiVersion = EOS_PLATFORM_OPTIONS_API_LATEST;
PlatformOptions.Reserved = NULL;
PlatformOptions.ProductId = "..."; // TODO
PlatformOptions.SandboxId = TCHAR_TO_UTF8(*SandboxId);
PlatformOptions.ClientCredentials.ClientId = "..."; // TODO
PlatformOptions.ClientCredentials.ClientSecret = "..."; // TODO
PlatformOptions.bIsServer = false;
PlatformOptions.EncryptionKey = "..."; // TODO
PlatformOptions.DeploymentId = TCHAR_TO_UTF8(*DeploymentId);
PlatformOptions.Flags = 0;
// CacheDirectory
PlatformOptions.TickBudgetInMilliseconds = 0;
PlatformOptions.RTCOptions = NULL;
PlatformOptions.IntegratedPlatformOptionsContainerHandle = NULL;
// Create the platform handle.
PlatformHandle = EOS_Platform_Create(&PlatformOptions);
if (PlatformHandle == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT("############### Failed to create EOS platform."));
}
}
void EosSingleton::AuthLogin(char *token_arg)
{
EOS_Auth_Credentials credentials;
credentials.ApiVersion = EOS_AUTH_CREDENTIALS_API_LATEST;
credentials.Id = NULL;
credentials.Token = token_arg;
credentials.Type = EOS_ELoginCredentialType::EOS_LCT_ExchangeCode;
EOS_Auth_LoginOptions login_options;
login_options.ApiVersion = EOS_AUTH_LOGIN_API_LATEST;
login_options.Credentials = &credentials;
login_options.ScopeFlags = EOS_EAuthScopeFlags::EOS_AS_BasicProfile;
// login_options.LoginFlags = 0;
if (AuthHandle == NULL)
{
UE_LOG(LogTemp, Warning, TEXT("failed requesting AuthInterface"));
return;
}
EOS_Auth_Login(AuthHandle, &login_options, nullptr, [](const EOS_Auth_LoginCallbackInfo *Data)
{
if (Data->ResultCode == EOS_EResult::EOS_Success)
{
// Login successful
UE_LOG(LogTemp, Log, TEXT("############### EOS_Auth_Login success"));
EosSingleton::getInstance().ConnectInterface(Data->LocalUserId);
}
else
{
// Handle login error
UE_LOG(LogTemp, Warning, TEXT("############### Fatal Error - EOS_Auth_Login failed."));
} });
}
void EosSingleton::ConnectInterface(EOS_EpicAccountId account_id)
{
// https://dev.epicgames.com/docs/game-services/eos-connect-interface#user-authentication-refresh-notification
EOS_Auth_CopyIdTokenOptions auth_options;
auth_options.ApiVersion = EOS_AUTH_COPYIDTOKEN_API_LATEST;
auth_options.AccountId = account_id;
EOS_Auth_IdToken *id_token = new EOS_Auth_IdToken();
EOS_EResult result = EOS_Auth_CopyIdToken(AuthHandle, &auth_options, &id_token);
if (result != EOS_EResult::EOS_Success)
{
UE_LOG(LogTemp, Log, TEXT("############### Fatal Error - EOS_Auth_CopyIdToken failed. %d"), static_cast<int>(result));
return;
}
ConnectLogin(id_token->JsonWebToken);
}
void EosSingleton::ConnectLogin(const char *token_arg)
{
EOS_Connect_Credentials credentials;
credentials.ApiVersion = EOS_CONNECT_CREDENTIALS_API_LATEST;
credentials.Token = token_arg;
credentials.Type = EOS_EExternalCredentialType::EOS_ECT_EPIC_ID_TOKEN;
EOS_Connect_LoginOptions login_options;
login_options.ApiVersion = EOS_CONNECT_LOGIN_API_LATEST;
login_options.Credentials = &credentials;
login_options.UserLoginInfo = NULL;
EOS_Connect_Login(ConnectionHandle, &login_options, nullptr, [](const EOS_Connect_LoginCallbackInfo *Data)
{
if (Data->ResultCode == EOS_EResult::EOS_Success)
{
// Login successful
UE_LOG(LogTemp, Log, TEXT("############### EOS_Connect_Login good."));
EosSingleton::getInstance().LocalProductUserId = Data->LocalUserId;
}
else if (Data->ResultCode == EOS_EResult::EOS_InvalidUser)
{
UE_LOG(LogTemp, Log, TEXT("############### EOS_Connect_Login invalid user."));
EosSingleton::getInstance().CreateUser(Data->ContinuanceToken);
}
else
{
// Handle login error
UE_LOG(LogTemp, Warning, TEXT("############### Fatal Error - EOS_Connect_Login failed. %d"), static_cast<int>(Data->ResultCode));
} });
}
void EosSingleton::CreateUser(const EOS_ContinuanceToken &ContinuanceToken)
{
EOS_Connect_CreateUserOptions options;
options.ApiVersion = EOS_CONNECT_CREATEUSER_API_LATEST;
options.ContinuanceToken = ContinuanceToken;
EOS_Connect_CreateUser(ConnectionHandle, &options, nullptr, [](const EOS_Connect_CreateUserCallbackInfo *Data)
{
if (Data->ResultCode == EOS_EResult::EOS_Success)
{
UE_LOG(LogTemp, Log, TEXT("############### EOS_Connect_CreateUser good."));
EosSingleton::getInstance().LocalProductUserId = Data->LocalUserId;
}
else
{
UE_LOG(LogTemp, Warning, TEXT("############### Fatal Error - EOS_Connect_CreateUser failed. %d"), static_cast<int>(Data->ResultCode));
} });
}
void EosSingleton::UnlockAchievements(const char **AchievementIds, uint32_t AchievementsCount)
{
UE_LOG(LogTemp, Log, TEXT("############### UnlockAchievements"));
if (LocalProductUserId == NULL)
{
UE_LOG(LogTemp, Log, TEXT("############### UnlockAchievements but unknown LocalProductUserId."));
return;
}
EOS_Achievements_UnlockAchievementsOptions UnlockAchievementOptions = {};
UnlockAchievementOptions.ApiVersion = EOS_ACHIEVEMENTS_UNLOCKACHIEVEMENTS_API_LATEST;
UnlockAchievementOptions.UserId = LocalProductUserId;
UnlockAchievementOptions.AchievementIds = AchievementIds;
UnlockAchievementOptions.AchievementsCount = AchievementsCount;
EOS_Achievements_UnlockAchievements(AchievementsHandle, &UnlockAchievementOptions, nullptr, [](const EOS_Achievements_OnUnlockAchievementsCompleteCallbackInfo *Data)
{
if (Data->ResultCode == EOS_EResult::EOS_Success)
{
UE_LOG(LogTemp, Log, TEXT("############### EOS_Achievements_UnlockAchievements good."));
}
else if (EOS_EResult_IsOperationComplete(Data->ResultCode) == EOS_FALSE)
{
// If the code gets here, the operation is retrying, meaning it is not yet complete.
UE_LOG(LogTemp, Log, TEXT("############### EOS_Achievements_UnlockAchievements false, retrying."));
}
else
{
UE_LOG(LogProcess, Error, TEXT("############### Error unlocking achievement! %d"), static_cast<int>(Data->ResultCode));
} });
}
In my gamemode, I have something like this:
virtual void Tick(float DeltaTime) override
{
EosSingleton::getInstance().tick();
}
UFUNCTION(BlueprintCallable, Category = Achievements)
void TriggerSomeAchievement(FString achiev_id)
{
const char *CharPtr = TCHAR_TO_UTF8(*achiev_id);
EosSingleton::getInstance().UnlockAchievements(&CharPtr, 1);
}