Game info, versions and platforms
Everything the SDK does is scoped to one game. The game id and API key you set at initialization decide which catalog you read, which capabilities are switched on, and which version taxonomy your mod filters are validated against. This page covers the read APIs on ICFCoreApi that report what the backend believes about your game, plus platforms and utilities, in cfcore-sdk-ue.
Initialize the SDK first. In C++, every sub-interface accessor on ICFCore except Utils() returns nullptr until initialization succeeds, so calling through one early is a null dereference. Null-check the accessor, do not just check ICFCore::IsInitialized. In Blueprint, UCFCoreSubsystem guards every node and fires its error pin with an FCFCoreError whose code is ECFCoreErrorCodes::FailedToInitialize. Every callback is marshalled back to the game thread.
Settings that scope every call on this page
Field on FCFCoreSettings | Type | Effect |
|---|---|---|
gameId | int64 | Formatted into the request path (/v1/games/{gameId}). The only thing scoping GetGame, GetVersionTypes, GetVersions and GetVersionsDetailed. Must be greater than zero, or initialization fails with MissingGameId before any network request. |
apiKey | FString | Sent as an API key header when non empty. Must be non-empty, or initialization fails with MissingApiKey before any network request. |
provider | ECFCoreExternalAuthProvider | Store or platform provider. Values: None, Steam, PSN, XBL, WB, Epic, GOG. WB is Warner Brothers. |
isServer | bool | Set true on a dedicated server build so the reported platform string becomes its _server variant, and adds a mod-server-request header. Also gates the server-only query behavior described below. |
isServerPcOnly | bool | Relevant only when isServer is true. Feeds filterPcOnly on the server's mod query: true returns the latest server mod that has a corresponding Windows mod, false returns one that has all supported platforms. |
Get both gameId and apiKey from your game record in the Developer Portal at console.curseforge.com.
No read API reports which platform string your build sent, so if a server build matches the wrong files, check isServer first.
Two query behaviours take effect only when isServer is true: devModIds, which requests unpublished mod revisions for testing, and filterPcOnly, which isServerPcOnly feeds.
Blueprint: build the struct with Make Settings From Project Config (which reads Edit > Project Settings > Plugins > CFCore) or Make Settings for inline values, both on UCFCoreBPLibrary. isServer is on that settings page, so a server target can set it without code. Your game id and API key come from the Developer Portal at console.curseforge.com, see Setting up your game.
The game model
ICFCoreApi::GetGame takes one FGetGameDelegate and no filter.
Field on FGame | Type | Meaning |
|---|---|---|
id | int64 | The CurseForge game id. Matches your gameId. |
name | FString | Display name. |
slug | FString | URL safe short name used in CurseForge web paths. |
dateModified | FDateTime | When the game record was last modified. |
assets | FGameAssets | Icon, tile and cover art URLs. |
status | ECFCoreStatus | Lifecycle state of the game record. |
apiStatus | ECFCoreApiStatus | Whether the game is publicly visible through the API. |
supportedFeatures | FGameSupportedFeatures | Capability flags enabled for this game. |
FGame has no platform field and no version list. FGameAssets is three FString URLs with no defaults, so any can arrive empty and no SDK API fetches or caches them: iconUrl (small square icon), tileUrl (card or grid art), coverUrl (wide hero art).
ECFCoreStatus | Numeric | ECFCoreApiStatus | Numeric | |
|---|---|---|---|---|
None | 0 | None | 0 | |
Draft | 1 | Private | 1 | |
Test | 2 | Public | 2 | |
PendingReview | 3 | |||
Rejected | 4 | |||
Approved | 5 | |||
Live | 6 |
During pre-launch integration your game record will usually not be Live or Public, so treat both as diagnostics for an internal debug overlay, not as gates in shipping code. See Testing and launching your game.
How it works
- Initialize the SDK and wait for the callback.
- Call the get game API. In C++ one delegate carries both the game and the error, in Blueprint they are two pins.
- Read
supportedFeaturesfirst if you branch on capability, then the presentation fields. - Cache for the session. There is no change event for the game record.
#include <cfcore_context.h>
#include <api/cfcore_api.h>
#include <api/models/game.h>
void AMyModsMenu::QueryGameInfo() {
cfcore::ICFCoreApi* api = cfcore::CFCoreContext::GetInstance()->Api();
if (api == nullptr) {
return; // Api() is nullptr until Initialize succeeds.
}
api->GetGame(
cfcore::ICFCoreApi::FGetGameDelegate::CreateLambda(
[](TOptional<FGame> opt_game,
const TOptional<FCFCoreApiResponseError>& opt_err) {
if (opt_err.IsSet()) {
UE_LOG(LogTemp, Error, TEXT("GetGame failed (%d, unreachable %d)"),
opt_err->errorCode, opt_err->serverUnreachable ? 1 : 0);
return;
}
if (!opt_game.IsSet()) { return; }
const FGame& game = opt_game.GetValue();
const bool subs = game.supportedFeatures.supportModSubscriptions;
// game.name, game.slug, game.assets.coverUrl for your header UI.
}));
}
Blueprint: Get Game Info on CFCoreSubsystem, under cfcore|Api. Pins are on_game (a game output of type FGame) and on_error (an error output of type FCFCoreError, fields isError, code, apiError, description).
FCFCoreApiResponseError carries classifier booleans past errorCode and description, and they are how you decide whether to retry: cancelled, badRequest, entityNotFound, serverUnreachable, missingPrivileges, tokenExpired, resourceExpired, failedToParseServerResponse.
Supported features: what the backend has enabled for your game
FGameSupportedFeatures is the runtime answer to which optional capabilities are switched on for your game id.
| Field | Type | Default | What it gates |
|---|---|---|---|
supportModSubscriptions | bool | false | The whole ICFCore::Subscription surface, configured by FCFCoreSettingsSubscriptions |
That is the complete set of flags in the current release. The subscription surface it gates is EnableAutoManagement, DisableAutoManagement, Subscribe, Unsubscribe, GetModsSubscriptions, SyncSubscriptions, GetSyncPlan and ExecuteSyncPlan. One flag. Use it as the visibility condition for your subscription UI: when it is false, do not render a subscribe button and do not call EnableAutoManagement, since those calls fail with HTTP errors when subscriptions are not enabled for the game. If it is false and you expected true, ask cfforstudios@overwolf.com to enable subscriptions for your game id.
The flag says the capability is enabled for the game, not that the player is authenticated. There is a separate ECFCoreErrorCodes::UserNotAuthenticated, and auto-subscribe runs on the first login after initialization, so gate the UI on the flag and the action on authentication state. Concepts are in Authentication overview.
Blueprint: break supportedFeatures out of FGame, feed supportModSubscriptions into a Branch and drive widget visibility from it. The subscription nodes sit under cfcore|Subscription.
Game version types and versions
A version type is a branch or release line of your game, configured per game in the Developer Portal at console.curseforge.com. Its numeric id is what you put into a mod search filter, so read the taxonomy at runtime and your filter UI stays correct when a branch is added.
Three calls, each taking one delegate and no filter:
| Method | Endpoint | Delivers |
|---|---|---|
GetVersionTypes | /v1/games/{gameId}/version-types | TOptional<TArray<FGameVersionType>> |
GetVersions | /v1/games/{gameId}/versions | TOptional<TArray<FGameVersionsByType>>, versions as strings |
GetVersionsDetailed | /v2/games/{gameId}/versions | const TOptional<TArray<FGameVersionsDetailedByType>>&, versions as objects with ids |
| Model | Field | Type | Meaning |
|---|---|---|---|
FGameVersionType | id | int64 | The version type id. Used as gameVersionTypeId in filters and on file models. |
gameId | int64 | The game this version type belongs to. | |
name | FString | Display name for a dropdown label. | |
slug | FString | URL safe short name. | |
FGameVersionsByType | type | int64 | The version type id. Join against FGameVersionType.id. |
versions | TArray<FString> | Version strings in that type, the values you pass as gameVersion. | |
FGameVersionsDetailedByType | type | int64 | The version type id. |
versions | TArray<FGameVersionDetailed> | The versions in that type, as objects. | |
FGameVersionDetailed | id | int64 | Numeric id of the individual game version. |
name | FString | Display name. | |
slug | FString | URL safe short name. |
FGameVersionsByType.type is an id, not a name, and that model has no name field. To label the group, call GetVersionTypes as well and join on id.
Which to use. GetVersions when you only need strings for a search filter, because FCFCoreSearchModsFilter.gameVersion is an FString. GetVersionsDetailed when you need the numeric version id, and the concrete reason is the upload path: FCreateModFileRequest.gameVersionIds is a TArray<int64>, so declaring which game versions a mod file supports means sending FGameVersionDetailed.id values. See Cloud cooking upload flows.
FGetVersionsDetailedDelegate declares its array parameter as a const reference, where the other two take theirs by value. Match the declaration or the lambda will not bind.
How it works
- Call the version types API and the versions API once after initialization.
- Join the arrays:
FGameVersionsByType.typeequalsFGameVersionType.id. - Render a two-level filter, version type as the group and version string as the entry. The shipped mod browser does this for you.
- A selected version string goes into the search filter as
gameVersion, a selected group asgameVersionTypeId.
api->GetVersionTypes(
cfcore::ICFCoreApi::FGetVersionTypesDelegate::CreateLambda(
[](TOptional<TArray<FGameVersionType>> opt_types,
const TOptional<FCFCoreApiResponseError>& opt_err) {
if (opt_err.IsSet() || !opt_types.IsSet()) { return; }
for (const FGameVersionType& type : opt_types.GetValue()) {
// type.id keys the dropdown entry, type.name labels it.
}
}));
// GetVersions has the same shape: FGameVersionsByType.type joins back to
// an FGameVersionType.id, and .versions holds the strings.
Blueprint: Get Version Types Info and Get Versions Info on CFCoreSubsystem, under cfcore|Api. Each has an on_results pin (a version_types or versions array) and an on_error pin carrying an FCFCoreError. The SDK exposes no Blueprint node for GetVersionsDetailed. Both detailed structs are BlueprintType with BlueprintReadOnly fields, so a Blueprint-only project can close the gap with a C++ wrapper UFUNCTION that null-checks Api(), calls GetVersionsDetailed and forwards the array through a dynamic delegate. Request parity through cfforstudios@overwolf.com, naming GetVersionsDetailed.
Using a version type id when filtering mods
The version type id is the join key between the version taxonomy and the mod catalog. It appears in one filter and on two read models.
| Where | Field | Type | Direction |
|---|---|---|---|
FCFCoreSearchModsFilter | gameVersionTypeId | int32 | You set it. Restricts results to mods with a file in that version type. |
FFileIndex (on FCFCoreMod.latestFilesIndexes) | gameVersionTypeId | int32 | You read it. Which version type each indexed latest file belongs to. |
FSortableGameVersion (on FFile.sortableGameVersions) | gameVersionTypeId | int32 | You read it. Which version type each declared game version of a file belongs to. |
FGameVersionType.id and FGameVersionsByType.type are int64, but all three gameVersionTypeId fields are int32. Narrow explicitly, and do not round-trip the value through a float in Blueprint. A filter field set to 0, an empty string, or an enum None means "no constraint", so a gameVersionTypeId of 0 does not filter.
FCFCoreSearchModsFilter filter;
filter.gameVersionTypeId = static_cast<int32>(SelectedVersionTypeId);
filter.sortField = ECFCoreModsSearchSortField::Popularity;
filter.sortOrder = ECFCoreSortOrder::Desc;
FCFCoreApiRequestPagination pagination; // index 0, pageSize 20 by default.
api->SearchMods(filter, pagination, MySearchDelegate);
Blueprint: build the filter with Unreal's auto-generated make-struct node for FCFCoreSearchModsFilter, set its gameVersionTypeId pin from the id captured out of Get Version Types Info, add Make Api Request Pagination (on UCFCoreBPLibrary) for a page other than the first, then connect both into Search Mods Info.
Platform support
Platform matters in three separate places, and conflating them is the most common way to reach a wrong conclusion about whether a target is supported: the string your build reports, the enum that declares what a cooked file was built for, and the matching a client does when joining a server.
The platform your build reports
The value the backend resolves against is a lowercase string, not ECFCorePlatform. It is chosen at compile time from Unreal's platform macros during SDK bootstrap and sent on every request as the x-platform header. When isServer is true the SDK appends _server.
| Unreal platform macro | Reported string |
|---|---|
PLATFORM_WINDOWS | windows |
PLATFORM_WINGDK | windows_gdk |
PLATFORM_XBOXONE | xbox_one |
PLATFORM_XSX | xbox_xsx, or xbox_xss when the console reports as Series S |
PLATFORM_PS4 | ps4 |
PLATFORM_PS5 | ps5 |
PLATFORM_MAC | mac |
PLATFORM_IOS | ios |
PLATFORM_TVOS | tvos |
PLATFORM_ANDROID | android |
PLATFORM_SWITCH | switch |
PLATFORM_HOLOLENS | hololens |
PLATFORM_LINUX | linux |
This resolution lives in private bootstrap code, so you cannot read the value your build reports from C++ or Blueprint, and there is no platform field on FGame. The selection is a chain of compile-time macro tests with no fallback branch: if you are porting to a target that is not listed, do not assume it degrades to windows. Confirm it with cfforstudios@overwolf.com, naming the Unreal platform macro your build defines.
The platform enum in source
ECFCorePlatform is a uint8 backed BlueprintType enum. These are the values in the current release, in declaration order.
| Value | Numeric | Value | Numeric | |
|---|---|---|---|---|
None | 0 | Mac | 7 | |
Windows | 1 | IOS | 8 | |
XboxOne | 2 | TVOS | 9 | |
XboxXS | 3 | Android | 10 | |
Linux | 4 | Switch | 11 | |
PS4 | 5 | WindowsServer | 12 | |
PS5 | 6 | LinuxServer | 13 |
This enum is not the request-path vocabulary. The only public struct carrying an ECFCorePlatform value in the current release is FCreateCookedModFileRequest.platform, which platform a manually cooked file was built for, on a struct used for manual cooking. It appears nowhere in the request pipeline. The enum answers what you declare a cooked artifact to be, the header string answers what your build is, and the two do not line up.
Mismatch with the published platform list
The Unreal overview page states platform support as Windows, PlayStation 4, PlayStation 5, Xbox, Windows (GDK), and Nintendo Switch. That list is narrower than either SDK vocabulary, and one entry on it has no enum member. Both facts are stated here rather than reconciled.
| Published entry | Reported string | Enum member |
|---|---|---|
| Windows | windows, plus windows_server when isServer | Windows (1), WindowsServer (12) |
| PlayStation 4 | ps4 | PS4 (5) |
| PlayStation 5 | ps5 | PS5 (6) |
| Xbox | xbox_one, xbox_xsx, xbox_xss | XboxOne (2), XboxXS (3) |
| Windows (GDK) | windows_gdk | none. There is no WindowsGDK member, which matters only on the manual cooking path. |
| Nintendo Switch | switch | Switch (11) |
| not published | linux, linux_server, mac, ios, tvos, android, hololens | Linux (4), Mac (7), IOS (8), TVOS (9), Android (10), LinuxServer (13) |
Presence in either vocabulary is not a statement that the platform is supported for your integration, and neither reflects a commercial or certification agreement. Treat the published list as authoritative for what is supported today, and confirm the platform set for your title with cfforstudios@overwolf.com before planning a port, naming the target and your game id. Console requirements are in Xbox and PlayStation compliance.
Matching files to the client's platform
Because the platform travels in a header, you never pass it. ICFCoreApi::MatchPlatformFiles takes an array of file ids and returns the FFile objects matching those ids for the current platform. Its filter, FMatchPlatformFilesFilter, has one field, fileIds (TArray<int64>). The implementation POSTs to /v1/mods/files/match-platform, where the backend reads the x-platform header. The intended use is client-server sessions: the server returns file ids, the client downloads the files matching its platform.
FMatchPlatformFilesFilter filter;
filter.fileIds = InFileIds;
// Both delegate parameters are by value here, unlike every other method
// on this interface. Copying the GetGame signature will not compile.
api->MatchPlatformFiles(
filter,
cfcore::ICFCoreApi::FMatchPlatformFilesDelegate::CreateLambda(
[](TOptional<TArray<FFile>> opt_files,
TOptional<FCFCoreApiResponseError> opt_err) {
if (opt_err.IsSet() || !opt_files.IsSet()) { return; }
for (const FFile& file : opt_files.GetValue()) { /* file.id */ }
}));
Blueprint: Match Platform Files By Ids on CFCoreSubsystem, under cfcore|Api. Pass an FMatchPlatformFilesFilter with fileIds populated. Results arrive on OnResults as a files array (same delegate type as Get Files Info By Ids), failures on OnError.
When nothing in the list matches, the client-server library path surfaces ECFCoreErrorCodes::NoPlatformFilesMatched. Handle it as a joinability failure with a player-facing message, not a transport error to retry. That code comes from the client-server flow, not from MatchPlatformFiles itself.
Diagnostic read APIs
| Method | Endpoint | Returns | Studio-facing |
|---|---|---|---|
GetActiveCookingVersion | /v1/cooking/sdk-version | FSDKVersion, one field: build (int32) | Yes |
GetDirectDownloadDomain | /v2/mod-files/direct-download-domain | FString | No, SDK internal |
GetDirectDownloadDomainEx | /v3/mod-files/direct-download-domain | FDirectDownloadDomainEx, one field: domain (FString) | No, SDK internal |
GetActiveCookingVersion is the only one of the three you call. build is the build number of the cloud cooker running for your game. Cooked mod files load only on a compatible engine build, so log it alongside a cooking failure and compare it when a mod that cooked last week stops loading after an engine upgrade. See Cloud cooking. Its delegate declares the first parameter as const TOptional<FSDKVersion>, by value with a top-level const, unlike most others on this interface.
Both domain calls exist for chunked downloading and are not meant to be called from your game. Do not call them and do not build download URLs by hand, use the library install path. They are listed here only so you recognise them in a network capture. See also Browsing and discovery, which names the same two. If you need to know which host must be reachable behind a corporate or console allowlist, ask cfforstudios@overwolf.com rather than reading it out of the SDK, and include your game id and target platform.
Blueprint: the SDK exposes no Blueprint node for any of these three. For the download domains that matches their internal marking. For the cooking version, FSDKVersion is a BlueprintType struct with a BlueprintReadOnly build field, so a C++ wrapper UFUNCTION closes the gap.
Utils and the compression service
ICFCore::Utils() returns an ICFCoreUtils*, which exposes one thing: Compression(), returning an ICompressionService*. This is the SDK's own zip implementation, exposed so mod tooling inside your title does not need a second compression dependency: packaging authored content into a zip before uploading it as a mod file revision, and unpacking a zip your own tooling produced.
This branch needs no initialization. Utils() returns a valid pointer unconditionally, Compression() never returns nullptr, and Zip Paths is the only Blueprint node in the plugin with no initialization guard, so build-time and editor tooling can compress without bringing the SDK up.
| Method | Parameters | Returns |
|---|---|---|
Zip | const TSharedRef<TArray<FString>> files_to_zip, const FString& output_zip_file, FProgressDelegate on_progress | TFuture<ECompressionError> |
Unzip | const FString& zip_file, const FString& output_folder, FProgressDelegate on_progress | TFuture<ECompressionError> |
files_to_zip may contain directories: a file path is taken as is, a directory is expanded recursively, entry names are made relative to the common root of the resolved inputs, and output_zip_file is removed if it appears in the list. Hand it a mod folder rather than enumerating files. It takes a TSharedRef because the call captures that reference into a thread-pool task and returns immediately, so build it with MakeShared<TArray<FString>> and do not mutate it afterwards.
FProgressDelegate delivers an FCompressionProgress:
| Field | Type | Meaning |
|---|---|---|
progress | int32 | Percentage of files processed, files done over total files. Not a byte percentage. |
file | FString | The file being processed. On zip the absolute source path, on unzip the entry name inside the archive. |
Progress fires only when the integer percentage changes, so consecutive small files can produce no callback. The value is seeded at -1 so a first callback at 0 is still delivered, and it is marshalled to the game thread, so you can drive UMG from it directly.
ECompressionError | Numeric | Meaning |
|---|---|---|
None | 0 | Success. |
FailedToReadZip | 1 | The archive could not be opened for reading (unzip). |
FailedToExtractFile | 2 | A file could not be extracted. The SDK log carries the detail. |
FailedToWriteFile | 3 | A file could not be written to the output location, or the output archive could not be created (zip). |
None is the success value, so compare against None rather than testing a boolean. None is also not proof an archive was produced: an empty input list completes with None after logging that there were no input files to zip, and an input list that resolves to no files completes with None after logging that zipping failed because no files were found. Check the output file after the future completes.
How it works
- Get the compression service from
Utils(). No initialization needed. - Build the input list as a shared array (zip) or point at an existing archive (unzip).
- Bind a progress delegate if you want a progress bar.
- Start the operation and keep the returned future. The work runs on a worker thread.
- Inspect the
ECompressionError. The promise is fulfilled on the game thread, so aTFuture::Nextcontinuation can touch UI directly.
#include <utils/cfcore_utils.h>
#include <utils/compression/compression_service.h>
void AMyModTools::PackageAuthoredContent(const TArray<FString>& InFiles,
const FString& InOutputZipFile) {
cfcore::ICompressionService* compression =
cfcore::CFCoreContext::GetInstance()->Utils()->Compression();
ZipFuture = compression->Zip(
MakeShared<TArray<FString>>(InFiles), InOutputZipFile,
cfcore::ICompressionService::FProgressDelegate::CreateLambda(
[](const FCompressionProgress& progress) {
UE_LOG(LogTemp, Display, TEXT("Zipping %s (%d%%)"),
*progress.file, progress.progress);
}))
.Next([InOutputZipFile](ECompressionError err) {
if (err != ECompressionError::None) {
UE_LOG(LogTemp, Error, TEXT("Zip failed with code %d"), (int32)err);
return;
}
if (!FPaths::FileExists(InOutputZipFile)) {
UE_LOG(LogTemp, Warning, TEXT("Zip succeeded, no archive at %s"),
*InOutputZipFile);
}
});
}
Blueprint: Zip Paths on CFCoreSubsystem, under cfcore|Utils|Compression. Pins are InPathsToZip (string array), InOutputZipFile (string), OnProgress (an FCompressionProgress output), OnSuccess (no payload) and OnError (an ECompressionError output), so you do not compare against None yourself. Unlike the API nodes it is declared without AutoCreateRefTerm, so no delegate pin is auto-created: wire at least OnSuccess and OnError.
The SDK exposes UtilsCompressionZipPaths (Zip Paths) but no unzip node, even though ICompressionService::Unzip exists in C++. A Blueprint-only project that needs to extract an archive needs a C++ wrapper UFUNCTION, or should rely on the library install path, which extracts mod archives for you. Request parity through cfforstudios@overwolf.com, naming ICompressionService::Unzip. An in-flight unzip cannot be cancelled in the current release. Do not offer a cancel button you cannot honour.