Skip to main content

Basic Setup

This guide explains how to set up the basic environment for using the PlayNANOO REST API in Unreal Engine C++.

1. Prerequisites

You must obtain the following information from the PlayNANOO console in advance.

Obtaining API Authentication Information

Access the PlayNANOO console and check the following information.

Path: Console > Channel > Settings > API & Platform Management

Information you need to obtain:

  • Game ID: Channel unique identifier
  • Service Key: Service key used for API requests
  • Secret Key: Secret key used for generating security signatures
Security Notice

The Secret Key must never be exposed. Manage it through environment variables or a separate configuration file.

2. Unreal Engine C++ Implementation

Here's how to implement PlayNANOO API calls in Unreal Engine.

2.1 Adding Module Dependencies

First, add HTTP-related modules to your project's Build.cs file.

// YourProject.Build.cs
PublicDependencyModuleNames.AddRange(new string[]
{
"Core",
"CoreUObject",
"Engine",
"InputCore",
"Http", // HTTP requests
"Json", // JSON parsing
"JsonUtilities" // JSON utilities
});

2.2 Device Information Structure (FDeviceInfo)

All API requests must include client device information. Use the following structure to automatically collect device information.

// DeviceInfo.h
#pragma once

#include "CoreMinimal.h"
#include "Json.h"
#include "GenericPlatform/GenericPlatformMisc.h"
#include "Internationalization/Internationalization.h"

struct FDeviceInfo
{
FString UUID;
FString Nickname;
FString DeviceId;
FString Version;
FString Platform;
FString DeviceModel;
FString DeviceOS;
FString DeviceLanguage;
FString DeviceCountry;
FString DeviceTimeOffset;

FDeviceInfo()
{
// Player information managed by the game (set after login)
UUID = UGameDataManager::Get()->GetUUID();
Nickname = UGameDataManager::Get()->GetNickname();

// Automatic device information collection
DeviceId = FPlatformMisc::GetDeviceId();
Version = UGameDataManager::Get()->GetVersionNumber();
Platform = GetPlatformName();
DeviceModel = FPlatformMisc::GetDefaultDeviceProfileName();
DeviceOS = FPlatformMisc::GetOSVersion();
DeviceLanguage = GetDeviceLanguage();
DeviceCountry = GetCountryCode();
DeviceTimeOffset = FString::FromInt(GetTimeOffset());
}

// Add device information to JSON object
void AddToJson(TSharedPtr<FJsonObject>& JsonObject) const
{
JsonObject->SetStringField(TEXT("uuid"), UUID);
JsonObject->SetStringField(TEXT("nickname"), Nickname);
JsonObject->SetStringField(TEXT("device_id"), DeviceId);
JsonObject->SetStringField(TEXT("version"), Version);
JsonObject->SetStringField(TEXT("platform"), Platform);
JsonObject->SetStringField(TEXT("device_model"), DeviceModel);
JsonObject->SetStringField(TEXT("device_os"), DeviceOS);
JsonObject->SetStringField(TEXT("device_language"), DeviceLanguage);
JsonObject->SetStringField(TEXT("device_country"), DeviceCountry);
JsonObject->SetStringField(TEXT("device_timeoffset"), DeviceTimeOffset);
}

private:
FString GetPlatformName() const
{
#if PLATFORM_WINDOWS
return TEXT("Windows");
#elif PLATFORM_MAC
return TEXT("Mac");
#elif PLATFORM_IOS
return TEXT("iOS");
#elif PLATFORM_ANDROID
return TEXT("Android");
#elif PLATFORM_LINUX
return TEXT("Linux");
#else
return TEXT("Unknown");
#endif
}

FString GetDeviceLanguage() const
{
FString Culture = FInternationalization::Get().GetCurrentCulture()->GetTwoLetterISOLanguageName();
return Culture.ToUpper();
}

FString GetCountryCode() const
{
FString Culture = FInternationalization::Get().GetCurrentCulture()->GetRegion();
if (Culture.IsEmpty())
{
FString Language = GetDeviceLanguage();
if (Language == TEXT("KO")) return TEXT("KR");
if (Language == TEXT("JA")) return TEXT("JP");
if (Language == TEXT("ZH")) return TEXT("CN");
if (Language == TEXT("DE")) return TEXT("DE");
if (Language == TEXT("FR")) return TEXT("FR");
if (Language == TEXT("ES")) return TEXT("ES");
return TEXT("US");
}
return Culture.ToUpper();
}

int32 GetTimeOffset() const
{
FDateTime Now = FDateTime::Now();
FDateTime UtcNow = FDateTime::UtcNow();
FTimespan Offset = Now - UtcNow;
return FMath::RoundToInt(Offset.GetTotalSeconds());
}
};
Data Management

UGameDataManager is a singleton class that you need to implement in your game. It manages player information such as UUID, Nickname, and AccessToken.

2.3 Request Body Creation Helper Function

A helper function that automatically includes device information in all API requests.

// PlayNANOOHelper.h
#pragma once

#include "CoreMinimal.h"
#include "Json.h"
#include "Http.h"
#include "DeviceInfo.h"
#include "PlayNANOOAuth.h"

class FPlayNANOOHelper
{
public:
static TSharedPtr<FJsonObject> CreateRequestBody()
{
TSharedPtr<FJsonObject> Body = MakeShareable(new FJsonObject());
FDeviceInfo DeviceInfo;
DeviceInfo.AddToJson(Body);
return Body;
}

static FString ToJsonString(const TSharedPtr<FJsonObject>& JsonObject)
{
FString JsonBody;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&JsonBody);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
return JsonBody;
}

static void SetCommonHeaders(TSharedRef<IHttpRequest>& Request, bool bRequireToken = true)
{
Request->SetHeader(TEXT("Content-Type"), TEXT("application/json"));
Request->SetHeader(TEXT("Accept"), TEXT("application/json"));
Request->SetHeader(TEXT("X-Playnanoo-Key"), FPlayNANOOAuth::ServiceKey);
Request->SetHeader(TEXT("X-Playnanoo-Id"), FPlayNANOOAuth::GameId);

if (bRequireToken)
{
FString AccessToken = UGameDataManager::Get()->GetAccessToken();
Request->SetHeader(TEXT("Authorization"), FString::Printf(TEXT("Bearer %s"), *AccessToken));
}
}
};

2.4 API Request Example

void UMyGame::SaveStorage(const FString& Key, const FString& Value)
{
TSharedPtr<FJsonObject> Body = FPlayNANOOHelper::CreateRequestBody();
Body->SetStringField(TEXT("key"), Key);
Body->SetStringField(TEXT("value"), Value);

FString JsonBody = FPlayNANOOHelper::ToJsonString(Body);

TSharedRef<IHttpRequest> Request = FHttpModule::Get().CreateRequest();
Request->SetURL(TEXT("https://service-api.playnanoo.com/storage/v20220701/set"));
Request->SetVerb(TEXT("PUT"));
FPlayNANOOHelper::SetCommonHeaders(Request, true);
Request->SetContentAsString(JsonBody);

Request->OnProcessRequestComplete().BindLambda(
[](FHttpRequestPtr Req, FHttpResponsePtr Res, bool bSuccess)
{
if (bSuccess && Res.IsValid())
{
UE_LOG(LogTemp, Log, TEXT("Save successful: %s"), *Res->GetContentAsString());
}
});

Request->ProcessRequest();
}