Skip to main content

Subscribing to UGC

Now you've shown the user some UGC based on a query they've submitted through your UI, the next step is for the user to pick a piece of UGC they'd like to download and trigger an installation process.

This guide covers:

Installation management and UGC filepaths

A subscription marks a piece of UGC as requiring installation, whereas an unsubscription indicates uninstallation.

So, how do you actually control when the SDK does those things? After all, you don't want UGC to be uninstalled after your main program has loaded those files into memory, locking them from deletion. Likewise, you probably don't want to be using networking or processor resources during gameplay for downloading UGC. In order to give you control over when these processes occur, without forcing you to shut down the SDK entirely, you can call EnableModManagement and DisableModManagement.

In order to display a notification to your users when UGC is finished installing or updating, EnableModManagement asks you to provide a callback. Because EnableModManagement is not an async function (ie it doesn't end with *Async), the function handler operates differently compared to other asynchronous results callbacks you use elsewhere in the SDK. A handler given to this function will be held by the SDK until a corresponding call to DisableModManagement or ShutdownAsync takes place. The handler will be invoked every time UGC is automatically installed, updated, or uninstalled by the SDK's internal event loop.

Modio::EnableModManagement([](Modio::ModManagementEvent ModEvent)
{
if (ModEvent.Status && ModEvent.Event == Modio::ModManagementEvent::EventType::Installed)
{
std::cout << "Mod with ID: " << ModEvent.ID << " is installed" << std::endl;
}
else
{
std::cout << "Mod with ID: " << ModEvent.ID << " failed to install: " << ModEvent.Status.message() << std::endl;
}
});

// Some time later: check if there's a mod being installed, or more mods that require installation pending
if (!Modio::IsModManagementBusy())
{
// This will reset any in-progress installations to pending, so we're only calling it if nothing's being processed
Modio::DisableModManagement();
}

UGC subscriptions

To build on the above, a user indicates they want to install UGC by 'subscribing' to it. The mod.io servers store these subscriptions, associating them with a particular user's mod.io account. When a user 'unsubscribes' UGC, they indicate that piece of UGC should be uninstalled from any device they're logged in on.

The API for managing subscriptions is simple and consists of a call to either SubscribeToModAsync or UnsubscribeFromModAsync with the ID of the mod in question and a callback to receive the status of the request:

note

To subscribe to UGC, EnableModManagement must be called beforehand.

When subscribing to UGC, you can also pass in a bool to indicate if you want to subscribe to all dependencies for the given UGC. If dependencies are also subscribed, the mod.io servers will also associate them with the current user.

note

Currently when dependencies are included during subscription, they will not automatically be downloaded. To ensure the latest content is downloaded, FetchExternalUpdatesAsync must be called after subscribing.

// Subscription
Modio::SubscribeToModAsync(ModID, IncludeDependencies, [](Modio::ErrorCode ec)
{
if (ec)
{
// Didn't subscribe, show a message to the user
}
else
{
// Successfully subscribed on the server
}
});

// Unsubscription
Modio::UnsubscribeFromModAsync(ModID, [](Modio::ErrorCode ec)
{
if (ec)
{
// Couldn't unsubscribe, show error
}
else
{
// Server records unsubscription to remove the user's association to this mod
}
});

External subscription changes

Remember that the mod.io service is available as a website besides the integration within your application. Users can manage their subscriptions (and therefore installations) outside of your game. Consequently, you must query the server for any external subscription changes. To do this, use FetchExternalUpdatesAsync to synchronise the server state with the SDK's local subscriptions:

Modio::FetchExternalUpdatesAsync([](Modio::ErrorCode ec)
{
if (ec)
{
// Couldn't fetch external subscription data, handle error
}
else
{
// The SDK's internal state synchronised. This is an acknowledgment of success
}
});
note

You should call FetchExternalUpdatesAsync at particular times in your application when you want to ensure that the state is up-to-date. The mod.io SDK will apply rate-limiting internally if you try to call it too often.

In case you need to prepare for changes happening beforehand, call PreviewExternalUpdatesAsync. This function retrieves a list of updates between the users local mod state, and the server-side state. It allows you to identify which piece of UGC will be modified when you call FetchExternalUpdatesAsync next in order to perform any content management (such as unloading files) that might be required. Its use is very similar:

Modio::PreviewExternalUpdatesAsync([](Modio::ErrorCode ec, std::map<Modio::ModID, Modio::UserSubscriptionList::ChangeType> ListOfChanges)
{
if (ec)
{
// Couldn't preview external subscription data, handle error
}
else
{
// Take notice of the changes brought inside variable "ListOfChanges". It serves as acknowledgment of success
}
});

Checking the user subscription list

In order to see which piece of UGC the user has subscribed to, call QueryUserSubscriptions to retrieve a collection of ModCollectionEntry objects, one for each subscribed piece of UGC. Each of these objects contains the UGC's state, profile information, ID, and other data suitable for showing users a list of their subscriptions.

note

This collection includes UGC that are still in the process of being installed. Make sure to check the result of ModCollectionEntry::GetModState before trying to load files from the UGC in this collection. Alternatively, use QueryUserInstallations as described in Retrieving mod directory paths for loading.

A distinction exists between functions QueryUserInstallations and QuerySystemInstallations. The first fetches the subset of the user's subscribed UGC that are installed and therefore ready for loading. QueryUserInstallations is more relevant for most cases to personalize the content shown to the user. On the other hand, a call to QuerySystemInstallations returns all mods installed on the system (including those the current user is subscribed to). This provides insight into UGC installed by other users.

If local space is a concern, here are some options to manage storage:

The first option focuses on the removal of any UGC the user has not interacted with, whereas the second option would actively uninstall UGC the user has previously considered and subscribed to. Consider other alternatives when designing your game to support UGC.

Retrieving UGC directory filepaths for loading

Once the user can pick UGC and subscribe (i.e. mark them for installation), mod.io SDK management can alter the filesystem and retrieve UGC. We need to know where they are on the filesystem to load them into your gameplay.

The easiest way to do this is by using QueryUserInstallations. This function allows you to specify if you want to include outdated UGC or not. It returns a collection of ModCollectionEntry objects that you can query for folder paths you can use to load files into your title.

std::vector<Modio::filesystem::path> ModPaths;

// It iterates over all the installed mods that are up-to-date
bool bIncludeOutdatedMods = false;
for (std::pair<Modio::ModID, Modio::ModCollectionEntry>& Entry : Modio::QueryUserInstallations(bIncludeOutdatedMods))
{
ModPaths.push_back(Entry.second().GetPath());
}

// You can now append whatever filenames you expect in a mod to the paths and load those in
for (Modio::filesystem::path& Path : ModPaths)
{
YourGame::FileHandle ModManifest = YourGame::OpenFile(Path / "mod_manifest.txt");
}

Next steps

Now your users can search for and subscribe to UGC, you might want to start exploring ways for users to Add UGC to your game.

If you've already done this, we recommend working your way through the C++ SDK Getting Started Guides as they will teach you how to implement the fundamentals of the C++ SDK before moving onto exploring our Features.