Skip to main content

Initialization

This guide describes the C++ SDK's basic event loop, initialization, and shutdown functions.

This guide covers:

SDK configuration

When you are ready to initialize the SDK for the current session, you'll need to call InitializeAsync, passing in your product's mod.io ID, your API key, the Local Profile Name, and a callback/handler so you know when the SDK is initialized correctly. Note that InitializeAsync's callback will be invoked after calling RunPendingHandlers from your project's main loop.

You should also ensure that you are targeting an appropriate Portal for mod.io to understand what storefront or app the request is originating from. This enabled additional storefront-based functionality, such as returning display name mappings for that portal.

// represents some external state so we know that the SDK is good to go
Modio::Optional<bool> SDKInitialized;

Modio::InitializeOptions Options;
Options.APIKey = Modio::ApiKey(YOUR_API_KEY);
Options.GameID = Modio::GameID(YOUR_GAME_ID);
Options.User = "LocalProfileName";
Options.GameEnvironment = Modio::Environment::Live;
Options.PortalInUse = Modio::Portal::Steam;

Modio::InitializeAsync(Options, [&SDKInitialized](Modio::ErrorCode ec) {
if (ec)
{
// SDK initialization failure
}
else
{
// SDK init OK - can now make other calls to the SDK, show mod browser UI, etc
}
});

It is worth considering that the "LocalProfileName" is used by the mod.io SDK to associate a local session to a user, as mentioned in Users and Local Profiles. It is possible to forward a user nickname as the LocalProfileName, then initialize the mod.io SDK. Any data related to a user session will be stored in its corresponding Mod Data Directory.

Notes

  • By convention you'll see these code examples pass in lambda functions as callback handlers, but you can use anything convertible to std::function with the appropriate signature.
  • The error-handling in this sample is deliberately kept brief. See Error Handling for more information on error handling.
  • You can perform calls to other functions, such as something that shows your mod browser UI implementation, directly in the callback. The primary caveat to doing much processing here is that you'll be running in the context of, and therefore blocking, the thread running the callback, which is the thread running RunPendingHandlers.

Extended initialization parameters

The ExtendedParameters field on InitializeOptions is a set of key-value pairs intended for platform-specific or special-case parameters that need to be passed to the SDK. Simply set the value before passing your initialization parameters in to InitializeAsync.

Options.ExtendedParameters["SomeParameterName"] = "SomeParameterValue";

General

The SDK supports the following general use parameters that can be set through ExtendedParameters:

ParameterDescription
PendingOnlyResultsOnly include UGC in search results that are pending moderation. For moderation and testing purposes. Set to "true" to enable. Warning: Should only be enabled when the user is a moderator or admin of the game, setting in shipping builds is not recommended.
PlatformOverrideSet the platform to be used when making requests to show UGC for that platform instead. For moderation and testing purposes. This parameter will soon be deprecated.

Storage Quota

The SDK supports the following storage quota parameters that can be set through ExtendedParameters:

ParameterDescription
ModStorageQuotaMBDefines a maximum quota of locally installed UGC for this game, in megabytes (minimum 25 MB). This includes temp mods/UGC and those installed by other users on this system.
CacheStorageQuotaMBDefines a maximum quota for cached image data such as UGC logos and gallery images, in megabytes (minimum 25 MB).

Example usage:

// Set mod storage quota to 500 MB
Options.ExtendedParameters["ModStorageQuotaMB"] = "500";

// Set cache storage quota to 250 MB
Options.ExtendedParameters["CacheStorageQuotaMB"] = "250";

By setting these values, the SDK will actively enforce the specified storage limits.

Metrics

The SDK supports the following metric parameters that can be set through ExtendedParameters:

ParameterDescription
MetricsSecretKeySet the secret key used by the metrics feature.

Event loop (RunPendingHandlers)

The SDK's internal event loop requires care and attention in the form of RunPendingHandlers.

ALL SDK work is performed during executions of RunPendingHandlers, therefore calling it as often as possible will improve performance of the SDK's I/O operations.

You can either call RunPendingHandlers on the main thread, or on a dedicated background thread or existing thread of your choice.

For optimal execution, RunPendingHandlers should be called at least once per frame.

For example, if you wish to call RunPendingHandlers on the main thread, it could be located into your project's main loop or into a tick-style function on an appropriate controller/manager object.

while(bGameIsRunning == true)
{
// other stuff
Modio::RunPendingHandlers();
// other stuff
}
note

RunPendingHandlers is not reentrant-safe. Do not call RunPendingHandlers inside a callback you give to the SDK, or your application will deadlock. Callbacks are run inside RunPendingHandlers, and your inner RunPendingHandlers call will block infinitely waiting for the enclosing scope to exit.

Multithreading

The SDK supports RunPendingHandlers being run on a secondary thread that already exists or a dedicated background thread you create specifically to peform SDK work.

Keep in mind, callbacks you provide to the SDK will run on the thread running RunPendingHandlers. If you host RunPendingHandlers on a different thread, it is your responsibility to synchronize with the main thread if you wish to pass results back to it, through callbacks you provide to the SDK.

This allows the SDK to avoid blocking the main thread of your application while performing IO.

Using an existing secondary thread

If you have an existing secondary thread which is not heavily utilized, you can call RunPendingHandlers on that thread:

while(bRunBackgroundThread == true)
{
// other stuff
Modio::RunPendingHandlers();
// other stuff
}

Just remember, that performance of the SDK is proportional to the amount of CPU cycles you give it, so if you use an existing secondary thread you'll need to ensure that that thread's loop runs at a high enough frequency.

Using a dedicated background thread

Using a dedicated background thread for RunPendingHandlers will ensure the best performance for the SDK's I/O operations by allowing SDK functionality to execute at a rate not limited by your application's main loop frequency or existing background loop frequencies.

//Assumption: bHaltBackgroundThread is a threadsafe flag or atomic boolean whose lifetime is guaranteed to be longer than that of the handler thread
HandlerThread = std::thread([&bHaltBackgroundThread]() {
while (!bHaltBackgroundThread)
{
Modio::RunPendingHandlers();
//Use one of the following if you intend to call ShutdownAsync in your program:
std::this_thread::yield();
//Change the sleep duration here as appropriate
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
});
note

ShutdownAsync takes a lock on the SDK's internal state in order to finalize the pending task queue and shut down internal services.

If you call RunPendingHandlers in a background thread, especially a dedicated background thread, and invoke ShutdownAsync to close the SDK while your application continues to run, on certain platforms you may find the mutex/lock to be unfair to the point that the main thread cannot take the lock, because the background thread does not get suspended by the OS' scheduler often enough.

If this occurs, we recommend you use either a yield or high-resolution sleep after each invocation of RunPendingHandlers to allow the main thread to take the shutdown lock. Alternatively, you can use some kind of signaling mechanism to pause the background thread calling RunPendingHandlers, call ShutdownAsync, and then allow that background thread to resume looping.

If the SDK runs for the lifetime of your application and you do not call ShutdownAsync this is not necessary.

Shutting down

To finalize and shut down the mod.io SDK is equally simple:

// State variable, stored in some valid scope
bool SDKShutdownComplete = false;

// Capture reference to the state variable in the lambda - could use shared_ptr for more safety instead
Modio::ShutdownAsync([&SDKShutdownComplete](Modio::ErrorCode ec)
{
SDKShutdownComplete = true;
});

while(!SDKShutdownComplete)
{
Modio::RunPendingHandlers();
}

note

ShutdownAsync uses a lock to ensure that global SDK state is not mutated out from underneath an invocation of RunPendingHandlers. It is not safe to call ShutdownAsync in any callback you provide to the SDK. Callbacks are executed during RunPendingHandlers execution, your application will deadlock while waiting for the enclosing RunPendingHandlers to complete. The lock is deliberately not implemented to support recursive locking, again because ShutdownAsync mutates data structures that RunPendingHandlers expects to remain unchanged for the duration of its scope.

note

You will need to continue to call RunPendingHandlers while the async shutdown is in progress to allow for intermediate handlers to finish running.

Next steps

Now that you are all set up, you may want to learn more about the SDK Structure to get a better understanding of the C++ SDK and its processes.

Or if you are eager to get things moving, you can skip directly to setting up User Authentication or Searching for UGC.

All the core setup information you need can be found in the C++ SDK Getting Started Guides, which are designed to teach you how to implement mod.io's fundamentals.