Welcome to the mod.io Unity Engine plugin repository!
mod.io enables game developers of all sizes to integrate user-generated content directly into their games quickly and easily. This includes hosting, user profiles and subscriptions, moderation tools, file delivery, and more:
The mod.io Unity Engine plugin is the simplest and fastest way to integrate UGC into your Unity 2020.3+ game. It handles all of the common tasks, allowing game developers to quickly and easily implement a solution that enables players to access and discover user-generated content for their games.
A custom built ready-made UI for mod discovery is included, along with installation and collection management, and a full-featured C# interface which connects to the mod.io REST API.
[!WARNING] The Browser UI is scheduled for deprecation, and may not receive updates. This is to be replaced with the Component UI / Template UI
To access console platforms and documentation, see Supporting Console Platforms.
[!WARNING] To enable Mobile In-App Purchasing you need to define the
MODIO_MOBILE_IAP
in Project Settings > Player > Script Compilation > Scripting Define Symbols This will also require the Unity.Purchasing package to be installed.
Platform | Support |
---|---|
Windows | ✓ |
macOS | ✓ |
Linux | ✓ |
Xbox One | ✓ |
Xbox Series X | ✓ |
PlayStation 4 | ✓ |
PlayStation 5 | ✓ |
Nintendo Switch | ✓ |
iOS | ✓ |
Android | ✓ |
If you need assistance with first-party approval, or require a private, white-label UGC solution. Contact us!
Our Unity plugin is public and open source. Game developers are welcome to utilize it as-is or fork it for their game's specific requirements.
Want to make changes to our plugin? Submit a pull request, and we'll review your recommended changes! Our goal at mod.io is an open modding API, and you're encouraged to view, fork and contribute to all of our codebases!
[!WARNING]
If you have a previous version of the plugin installed, it is highly recommended to delete it before updating to a later version.
.unitypackage
directly from the Releases page.Assets/Plugins
directory.[!NOTE]
If you receive errors due to conflicting libraries after installing the plugin, remove any duplicates fromAssets/Plugins/mod.io/ThirdParty
.
The first thing you'll need to do is create a game profile on mod.io (or our private test environment).
[!IMPORTANT]
You'll need yourgame ID
andAPI key
for the following steps.
Tools -> mod.io -> Edit Settings
.game ID
and API key
.server URL
depending on where you created your game profile earlier.[!WARNING]
Deselect the config file before entering Play mode. A known Unity bug can cause the Editor to crash in Unity 2019-2021.
Your setup is now complete. The following sections will guide you through getting your mod.io integration up and running quickly.
If you have any questions or need some help join our Discord server.
The mod.io Unity Engine plugin comes with a prebuilt UI, a drop-in, instant solution for browsing and installing your game's mods.
If you want to skip implementing your own UI, head to the Browser UI section for setup and usage instructions. However, we recommend following the guide below to better understand how the plugin works.
[!WARNING] The Browser UI is scheduled for deprecation, and may not receive updates. This is to be replaced with the Component UI / Template UI
In the following section, we will walk through implementing some of the most common functions of the mod.io Unity Engine plugin. We recommend reading this step-by-step guide to ensure you understand how everything works, but you can find the resulting class for reference here.
If you prefer a video tutorial, click the image below to view it on YouTube:
First, let's create a new MonoBehaviour
called ModIOExample.cs
that will contain all of our example functionality:
using UnityEngine;
public class ModIOExample : MonoBehaviour
{
// TODO: Keep reading the Getting Started guide
}
Once you've created the above class:
Scene
.Empty Game Object
(name it anything you'd like).ModIOExample
component to your GameObject
.[!IMPORTANT]
The plugin relies on the config file that is configured during the setup instructions above. Please ensure you have completed all of those steps before proceeding.
Before the plugin can be used, it needs to be initialized for the current player. This usually only needs to happen once, so let's implement Unity's Start
method in our ModIOExample.cs
file:
using ModIO; // Add this to the top of your class
void Start()
{
Result result = ModIOUnity.InitializeForUser("default");
if (!result.Succeeded())
return;
Debug.Log("ModIO plugin initialized!");
}
The value passed into InitializeForUser
is important. We've used "default" here, however, if your game allows for multiple players on the same installation, you should instead use a unique identifier per player. This allows the plugin to cache authentication and mod-subscriptions on a per-player basis.
Now, return to your scene in Unity, enter Play mode and you should see the logged success message.
[!NOTE]
This guide usesModIOUnityAsync
wherever possible. However, you can find callback equivalents to every method inModIOUnity
if you prefer.
Most of the API’s functionality requires player authentication. The plugin offers a large range of SSO (single-sign on) authentication options, including Steam, Xbox, Google, PlayStation, and more. We strongly recommend using these options as they provide a frictionless user experience and don't require multiple steps.
For now, let's start with a simple email authentication to allow us full access.
[!NOTE]
While creating the UI layout referenced below is outside the scope of this guide, there are great Unity UI tutorials available. You can, however, use the image below as a guide for the elements required to achieve the same functionality:
With your UI created, let's add our authentication functionality:
using UnityEngine.UI; // Add this to the top of your class
[SerializeField] InputField authInput;
[SerializeField] Button authRequest;
[SerializeField] Button authSubmit;
void Start()
{
// Initialization ...
OnInit();
}
async void OnInit()
{
Result result = await ModIOUnityAsync.IsAuthenticated();
if (result.Succeeded())
{
OnAuth();
return;
}
// You can assign these using the Inspector if you prefer
authRequest.onClick.AddListener(RequestAuthCode);
authSubmit.onClick.AddListener(SubmitAuthCode);
}
async void RequestAuthCode()
{
Result result = await ModIOUnityAsync.RequestAuthenticationEmail(authInput.text);
if (!result.Succeeded())
{
Debug.LogError($"RequestAuthenticationEmail failed: {result.message}");
return;
}
Debug.Log($"Authentication email sent to: {authInput.text}");
authInput.text = string.Empty;
}
async void SubmitAuthCode()
{
Result result = await ModIOUnityAsync.SubmitEmailSecurityCode(authInput.text);
if (!result.Succeeded())
{
Debug.LogError($"SubmitEmailSecurityCode failed: {result.message}");
return;
}
OnAuth();
}
async void OnAuth()
{
ResultAnd<UserProfile> result = await ModIOUnityAsync.GetCurrentUser();
if (!result.result.Succeeded())
{
Debug.LogError($"GetCurrentUser failed: {result.result.message}");
}
Debug.Log($"Authenticated user: {result.value.username}");
}
[!IMPORTANT]
Don't forget to assign the fields in the Inspector!
If you've implemented the above correctly, you should now be able to:
authRequest
buttonauthSubmit
button[!NOTE] If there is no mod.io account associated with the provided email address, one will automatically be created.
There is something worth highlighting: if you restart Play mode, you'll see the logged authentication message again almost immediately. This is the result of two separate factors:
InitializeForUser
during the initialization section needs to have been used when a user successfully authenticated.OnInit()
, we check to see if we are already authenticated, and if so move straight to OnAuth()
.If you change the initialization value (currently "default"), you will no longer receive the authenticated log. This functionality can enable support for multiple players; separately tracking authentication states and mod-subscriptions. However, as mentioned previously, most games can pass a constant value if they only ever expect one player on the device.
[!NOTE]
If your email provider supports it, you can use plus-addressing to test multiple users with a single email address:john.smith+test1@gmail.com john.smith+test2@gmail.com john.smith+test3@gmail.com
[!NOTE]
Among a range of other functionality, players can use the mod.io website for creating, modifying, and removing mods for your game.In this section, we're going to add mods using the plugin and API. Feel free to skip this section if you'd prefer to use the web interface.
Before we can interact with your game's mods via the API, we're going to need to create some test mods. We’ll start by adding some functionality that checks to see if your game has any mods. If it doesn't then we'll upload some using the API:
using System.Threading.Tasks; // Add this to the top of your class
async void OnAuth()
{
// Authenticated ...
await AddModsIfNone();
}
async Task AddModsIfNone()
{
// This section ensures we only upload our mods once. Don't worry too much
// about the specifics for now, we will introduce SearchFilters and GetMods
// properly later on.
ResultAnd<ModPage> resultAnd = await ModIOUnityAsync.GetMods(new SearchFilter());
if (!resultAnd.result.Succeeded())
{
Debug.LogError($"GetMods failed: {resultAnd.result.message}");
return;
}
if (resultAnd.value.modProfiles.Length != 0)
{
Debug.Log($"{resultAnd.value.modProfiles.Length} mods found. Not adding mods");
return;
}
// TODO: Keep reading the Getting Started guide
}
[!NOTE]
This section is going to generate some dummy mods for use throughout the rest of this guide. If you already have mods or test files ready to upload, you can skip to the uploading mods section.
Uploading mods is a two-step process:
CreateModProfile
), which will return a mod id
.mod id
.Let's add a method that handles both steps:
[!NOTE]
The following code takes advantage ofModIOUnity.GetCurrentUploadHandle
, which can be used for obtaining the current upload progress. This isn't required, and you can useawait ModIOUnityAsync.UploadModFile(details)
instead if you prefer.
async Task UploadMod(string name, string summary, Texture2D logo, string path)
{
Debug.Log($"Starting upload: {name}");
ModProfileDetails details = new ModProfileDetails
{
name = name,
summary = summary,
logo = logo,
};
ResultAnd<ModId> resultCreate = await ModIOUnityAsync.CreateModProfile(ModIOUnity.GenerateCreationToken(), details);
if (!resultCreate.result.Succeeded())
{
Debug.LogError($"CreateModProfile failed: {resultCreate.result.message}");
return;
}
ModfileDetails modFile = new ModfileDetails
{
modId = resultCreate.value,
directory = path,
};
float progress = 0f;
Task<Result> taskUpload = ModIOUnityAsync.UploadModfile(modFile);
while (!taskUpload.IsCompleted)
{
ProgressHandle progressHandle = ModIOUnity.GetCurrentUploadHandle();
if (!Mathf.Approximately(progressHandle.Progress, progress))
{
progress = progressHandle.Progress;
Debug.Log($"Uploading: {name} ({Mathf.RoundToInt(progress * 100)}%)");
}
await Task.Delay(1000);
}
if (!taskUpload.Result.Succeeded())
{
Debug.LogError($"UploadModfile failed: {taskUpload.Result.message}");
return;
}
Debug.Log($"Finished upload: {name}");
}
All that's left now is to feed some mods to our brand new UploadMod
method. After we test to see if any mods exist in AddModsIfNone
, we will iterate our list of mods and upload them:
[!IMPORTANT]
If you didn't generate dummy mods in the previous section, modify the below to suit your mod files.
async Task AddModsIfNone()
{
// Return if any mods exist ...
DummyModData[] mods =
{
// ...
};
foreach (DummyModData mod in mods)
{
await UploadMod(mod.name, mod.summary, mod.logo, mod.path);
// Directory.Delete(mod.path, true); // Uncomment if you generated dummy mods
}
}
[!NOTE]
When uploading a mod, the plugin expects a directory (for each mod) that it will compress before uploading. You do not need to zip your files before uploading.
That’s it! Enter Play mode now and, after authentication, the Unity console should come to life with the upload progress of your mods. If you're eager, you can view the mods as soon as they're uploaded by going to your game's mod.io page and using the web interface.
Searching for mods is a simple task — we've actually seen it already in the adding mods section. To get a list of all1 available mods you can use the GetMods
method:
using System; // Add this to the top of your class
async Task<ModProfile[]> GetAllMods()
{
ResultAnd<ModPage> resultAnd = await ModIOUnityAsync.GetMods(new SearchFilter());
if (!resultAnd.result.Succeeded())
{
Debug.LogError($"GetMods failed: {resultAnd.result.message}");
return Array.Empty<ModProfile>();
}
return resultAnd.value.modProfiles;
}
[!IMPORTANT]
A Mod Profile is a read-only snapshot of the state of a mod. It is not a unique or dynamic class. CompareModProfile.id
if you want to determine whether two Mod Profiles represent the same mod.
If we add the above to our example class, and then head back up to our OnAuth
method we can quickly log a list of all1 of our available mods:
using System.Linq; // Add this to the top of your class
ModProfile[] allMods;
async void OnAuth()
{
// ...
allMods = await GetAllMods();
Debug.Log($"Available mods:\n{string.Join("\n", allMods.Select(mod => $"{mod.name} (id: {mod.id.id})"))}");
}
We write all1 because while using the default Search Filter settings will return all of your mods, this is only because you don't have many. This brings us to Search Filters.
The maximum number of results returned can be set in the Search Filter using its SetPageSize()
method. However, the default value of 100 is also the limit. In order to return later results, you can use the Search Filter's SetPageIndex()
method.
This is fairly simple in practice and is explained best with the following snippet:
var searchFilter = new SearchFilter();
searchFilter.SetPageSize(10);
searchFilter.SetPageIndex(0); // Will return results 1-10
searchFilter.SetPageIndex(1); // Will return results 11-20
searchFilter.SetPageIndex(2); // Will return results 21-30
// You can also set pageIndex and pageSize using the constructor
new SearchFilter(0, 10); // Will return results 1-10
new SearchFilter(1, 10); // Will return results 11-20
new SearchFilter(2, 10); // Will return results 21-30
[!NOTE]
Search Filters have a number of options for filtering and ordering your results. See the documentation (or use code completion in your IDE) for its available options.
More specifically: downloading a Mod Profile's images. We'll cover subscribing to and installing mods soon.
A common feature when listing mods is to display an image along with its name and summary. Metadata images such as logos, screenshots, and avatars don't require subscribing to a mod to view them, and can be downloaded separately from a mod's in-game files.
As we know, all mods have a logo. So let's write a short method that selects a random mod, downloads its logo and displays it alongside its name:
[!NOTE]
Below is a screenshot of the UI we're using to utilize the method. You can use this as a guide for your own or display the result however you'd like!
[SerializeField] Text randomName;
[SerializeField] Image randomLogo;
async void SetRandomMod()
{
ModProfile modProfile = allMods[UnityEngine.Random.Range(0, allMods.Length)];
randomName.text = modProfile.name;
ResultAnd<Texture2D> resultAnd = await ModIOUnityAsync.DownloadTexture(modProfile.logoImage_320x180);
if (!resultAnd.result.Succeeded())
{
Debug.LogError($"DownloadTexture failed: {resultAnd.result.message}");
return;
}
Texture2D logo = resultAnd.value;
randomLogo.sprite = Sprite.Create(logo, new Rect(0, 0, logo.width, logo.height), Vector2.zero);
}
[!WARNING]
The code above relies onallMods
, which is set in the first searching for mods section. Ensure thatallMods
has been set before running this method.
This method is downloading the smallest version of the logo, logoImage_320x180
. However, Mod Profiles have a number of sizes for each image. See the documentation (or use code completion in your IDE) to view available options.
We're going to cover mod subscriptions in what will seem like a backward way. First, we'll learn how to get a list of our subscribed mods, then we'll learn how to subscribe to a mod.
The reason we do it this way is because subscribed mods are cached locally, so we avoid redundant API calls if we're already subscribed.
With that in mind, the rest is quite straightforward:
static ModProfile[] GetSubscribedMods()
{
SubscribedMod[] subscribed = ModIOUnity.GetSubscribedMods(out Result result);
if (!result.Succeeded())
{
Debug.LogError($"GetSubscribedMods failed: {result.message}");
return Array.Empty<ModProfile>();
}
return subscribed.Select(mod => mod.modProfile).ToArray();
}
Getting the user's subscribed mods first requires a call to FetchUpdates()
. This method synchronises the cached local state with mod.io (subscriptions, ratings, etc.). This is an expensive method and usually only needs to be called on authentication, as local subscription changes are reflected automatically. However if, for example, you change subscriptions using the web interface, they won't be reflected in your game until FetchUpdates()
has been called.
We'll make use of the above from our OnAuth()
method, after we log all available mods. First, we call FetchUpdates()
to synchronize our local state, and then we can get an array of our subscribed mods:
async void OnAuth()
{
// ...
Result resultUpdate = await ModIOUnityAsync.FetchUpdates(); // Synchronize our local state
if (!resultUpdate.Succeeded())
{
Debug.LogError($"FetchUpdates failed: {resultUpdate.message}");
}
ModProfile[] subscribedMods = GetSubscribedMods();
Debug.Log($"Subscribed mods:\n{(subscribedMods.Length > 0 ? string.Join("\n", subscribedMods.Select(mod => $"{mod.name} (id: {mod.id.id})")) : "None")}");
}
[!IMPORTANT]
A Mod Profile is a read-only snapshot of the state of a mod. It is not a unique or dynamic class. CompareModProfile.id
if you want to determine whether two Mod Profiles represent the same mod.
For now, you should see "Subscribed mods: None" in the log if you enter Play mode. But we're going to rectify that very soon.
[!NOTE]
The web interface at your game's mod.io page can also be used to subscribe to mods. However, you'll need to exit and enter Play mode to see the changes, asFetchUpdates()
needs to be run to synchronise the local state.
Subscribing to mods is very simple. The following code adds a check to see if we're already subscribed and then logs the name of the mod once the subscription is successful. The only actual requirement is the call to SubscribeToMod()
:
async Task SubscribeToMod(ModId modId, ModProfile[] subscribed)
{
if (subscribed.Any(mod => mod.id == modId))
return;
Result result = await ModIOUnityAsync.SubscribeToMod(modId);
if (!result.Succeeded())
{
Debug.LogError($"SubscribeToMod failed: {result.message}");
return;
}
Debug.Log($"Subscribed to mod: {allMods.First(mod => mod.id == modId).name}");
}
[!NOTE]
Inefficient code is used here to reduce the sample's complexity. Translating subscriptions to aList<ModId>
and keeping aDictionary<ModId, ModProfile>
would be more efficient solutions for these look-ups.
To test it out:
In your OnAuth()
method, after we log all subscribed mods add the following line (replace YOUR_MOD_ID
with the id from step 1):
async void OnAuth()
{
// ...
await SubscribeToMod(new ModId(YOUR_MOD_ID), subscribedMods);
}
Now, the moment we've all been waiting for. Downloading, installing, updating, and deleting mods are all handled automatically by the plugin. However, it requires both an authenticated user and to opt-in to the behaviour. The latter is done via a simple EnableModManagement()
call. This method requires a delegate argument that exposes us to the various events that mod management emits.
In the following code we're going to enable mod management and also log the download progress when a mod is being downloaded:
string downloadName = "";
float downloadProgress;
void EnableModManagement()
{
void HandleModManagementEvent(ModManagementEventType eventType, ModId modId, Result eventResult)
{
switch (eventType)
{
case ModManagementEventType.DownloadStarted:
downloadName = allMods.First(mod => mod.id == modId).name;
Debug.Log($"Downloading {downloadName}");
break;
case ModManagementEventType.Downloaded:
Debug.Log($"Downloaded {downloadName}");
downloadName = string.Empty;
break;
case ModManagementEventType.DownloadFailed:
Debug.Log($"Download failed {downloadName}");
downloadName = string.Empty;
break;
}
}
ModIOUnity.EnableModManagement(HandleModManagementEvent);
}
void Update()
{
if (downloadName.Length == 0)
return;
ProgressHandle progress = ModIOUnity.GetCurrentModManagementOperation();
if (Mathf.Approximately(progress.Progress, downloadProgress))
return;
downloadProgress = progress.Progress;
Debug.Log($"Downloading {downloadName} ({Mathf.RoundToInt(downloadProgress * 100)}%)");
}
In a real implementation, you'll likely track the modId
's download and install progress separately to display in your UI. But, this should give you an idea of what's possible with the mod management feature.
[!NOTE]
There are a number of mod management events available. See the documentation (or use code completion in your IDE) for a complete list.
We’re nearing the end now. You've initialized. You've authenticated. You've uploaded. You've searched. You've subscribed. You've installed. It's all led to this single question:
"How do I find installed mods?"
The answer is very straight forward: GetInstalledModsForUser()
. Using this is as simple as expected:
void LogInstalledModsForUser()
{
UserInstalledMod[] installedMods = ModIOUnity.GetInstalledModsForUser(out Result result);
if (!result.Succeeded())
{
Debug.LogError($"GetInstalledModsForUser failed: {result.message}");
return;
}
Debug.Log($"Installed mods:\n{(installedMods.Length > 0 ? string.Join("\n", installedMods.Select(mod => $"{mod.modProfile.name} ({mod.directory})")) : "None")}");
}
We're currently logging each installed mod and the path to its files (UserInstalledMod.directory
). However, you are only limited by how you want to utilize user-generated content. A mod's installation directory is exactly the same as when we uploaded it: uncompressed and ready for action.
That’s it, we’re done. The time has come to build a bridge to your creator community using mod.io.
Please join us on our Discord server if you have any questions or need some help.
[!NOTE]
You can also find the following class (along with an example scene) inAssets/Plugins/mod.io/Example
.
[!WARNING] The Browser UI is scheduled for deprecation, and may not receive updates. This is to be replaced with the Component UI / Template UI
[!IMPORTANT]
The Browser UI relies on the config file that is configured during the setup instructions above. Ensure you have completed all of those steps before proceeding.
The Browser UI is incredibly simple to set up, and completely avoids the complexity that can come with building a full-featured mod browser:
Assets/Plugins/mod.io/UI/Examples/ModIOBrowser
into your scene.ModIOBrowser.Browser.Open()
method to show the browser in your scene.If you want a fuller understanding of the plugin and its features, we recommend following the getting started guide above.
When a user authenticates, the Browser UI will localize mod.io's terms of use based on the language code set in your config file (Tools > mod.io > Edit Settings
, or Settings.server.languageCode
).
To avoid the plugin conflicting with an existing solution, in the case of right-to-left languages you will need to apply your current implementation for mixed RTL and LTR text to the terms text-elements in the browser.
The Component UI is an experimental module that allows for a more easily integrated and customised Mod Browsing UI.
This can be accessed via the modio-ui.unitypackage
provided in the plugin in the experimental
folder.
A Readme is provided in the package.
[!IMPORTANT] This will be replacing the functionality provided by the Browser UI, once the Browser UI is deprecated.
The mod.io SDK supports full monetization features, allowing you to sell a per-game virtual currency to your players that they can use to purchase mods, with a share of the revenue split between creators and your studio. Every platform requires specific setup for monetization features to work, with regards to the virtual currency configuration and API calls.
The following documentation walks you through the setup process and gives example usages. The mod.io monetization features are enabled as part of the onboarding process on your game profile. Once that is setup, there is nothing further you need to do for initialization in the SDK.
The first thing you will need to do is enable the marketplace in the mod.io portal for your game under Admin->Monetization->Settings->Enable Marketplace.
Returns the current user's token balance.
[!NOTE] This function creates a wallet for the user the first time it is called so this must be called before any sync entitlements calls.
async void GetUserWalletBalanceExample() { var response = await ModIOUnityAsync.GetUserWalletBalance(); if (response.result.Succeeded()) Debug.Log($"User has a balance of {response.value.balance} tokens."); else Debug.Log("failed to get balance"); }
Purchases a mod using tokens.
async void PurchaseItemExample()
{
ModId modId = new ModId(1234); // Mod to purchase
int displayedAmount = 12; // Price displayed to the player (must match mod price)
string idempotent = $"aUniqueKey"; // Unique key used to prevent duplicate purchases
var response = await ModIOUnityAsync.PurchaseItem(modId, displayedAmount, idempotent);
if (response.result.Succeeded())
Debug.Log("Completed Purchase");
else
Debug.Log("failed to complete purchase");
}
Returns the current user's purchased mods.
async void GetUserPurchases()
{
ModProfile[] purchased = ModIOUnity.GetPurchasedMods(out Result result);
if (result.Succeeded())
foreach (ModProfile mod in purchased)
Debug.Log($"User owns mod with id: {mod.id}");
else
Debug.Log("Failed to get purchases");
}
[!NOTE] Setup token pack SKUs from your game's mod.io website dashboard by navigating to
Admin -> Monetization -> Manage SKUs
.[!NOTE]
The GetUserWalletBalanceExample function creates a wallet for the user the first time it is called so this must be called before any sync entitlements calls.
Once you have setup SKUs for your users to purchase tokens through Steam, you can sync these purchases with the mod.io server using the SyncEntitlments()
method.
After a user purchases a token pack on Steam, calling SyncEntitlements()
will consume the purchased item, and add those tokens to the user's wallet. Below is a very simple example of how to use the method:
[!WARNING]
It is highly recommended that you callSyncEntitlements()
after any successful external purchase.
async void SyncEntitlements()
{
Result result = await ModIOUnityAsync.SyncEntitlements();
if (response.result.Succeeded())
Debug.Log("Entitlements are synced");
else
Debug.Log("Failed to sync entitlements");
}
[!NOTE]
SyncEntitlements()
is automatically run duringModIOUnity.FetchUpdates()
.[!NOTE]
SyncEntitlements()
can also be used for consuming purchases on console platforms.
To facilitate HTTP requests to mod.io's Service To Service (S2S) API's, your backend server must first authenticate and generate credentials that your backend service will use. Credentials required by S2S API's are separate from mod.io's public API endpoints and cannot be used interchangeably.
Some service-to-service endpoints require user context to be able to make requests on behalf of a user, such as creating a transaction. To facilitate this, mod.io hosts a public endpoint which can be called by an authenticated user with their bearer token which returns what we call a User Delegation Token. This token should then be sent to your secure backend server from your game client, where you can then use it for specific endpoints in conjunction with a valid service token.
[!NOTE] User must me authenticated to request a User Delegation Token.
async void Example()
{
ResultAnd<UserDelegationToken> response = await ModIOUnityImplementation.RequestUserDelegationToken();
if (response.result.Succeeded())
{
Debug.Log("successful.");
//TODO: Send response.value.token to server
}
else
{
Debug.Log("failed.");
}
}
Temp Mod sets allow users to download mods that they are not subscribed to. This can be helpful in multiplayer situations, when a player might join a game that requires specific mods to be downloaded. The intended flow in this situation would be to Create a temp mod set when a player joins the game and Delete the temp mod set when the game is over. The following documentation walks you through the setup process and gives example usages.
Creating a temp mod set starts to download mods in the set in a temporary location unassociated with their subscribed mods.
[!NOTE] Mods that the user is subscribed to will be not be re-downloaded and will remain in the installed mods location for that user.
ModId[] modIds;
void Example()
{
Result result = await ModIOUnityAsync.CreateTempModSet(modIds);
if (result.Succeeded())
{
Debug.Log("Successful");
}
else
{
Debug.Log("Failed");
}
}
Destroying a temp mod set removes the temporary installed mods in that set and allows for uninstallation of temporary mods.
void Example()
{
Result result = await ModIOUnityAsync.ModIOUnity.DeleteTempModSet();
if (result.Succeeded())
{
Debug.Log("Successful");
}
else
{
Debug.Log("Failed");
}
}
Adds mods to an existing Temp mod set and downloads/installs them if needed.
[!NOTE] Mods that the user is subscribed to will be not be re-downloaded and will remain in the installed mods location for that user.
ModId[] modIds;
void Example()
{
Result result = await ModIOUnityAsync.AddModToTempModSet(modIds);
if (result.Succeeded())
{
Debug.Log("Successful");
}
else
{
Debug.Log("Failed");
}
}
Removes mods from an existing Temporary Mod Set. This removes them from the list in the set but does not uninstall them, that is done when the set is destroyed.
ModId[] modIds;
void Example()
{
Result result = await ModIOUnityAsync.RemoveModsFromTempModSet(modIds);
if (result.Succeeded())
{
Debug.Log("Successful");
}
else
{
Debug.Log("Failed");
}
}
Gets an array of temp mods that are installed on the current device.
[!NOTE] These will not be subscribed by the current user. If you wish to get all the current user's installed mods use ModIOUnity.GetSubscribedMods() and check the SubscribedMod.status equals SubscribedModStatus.Installed.
void Example() { InstalledMod[] mods = await ModIOUnityAsync.GetTempSystemInstalledMods(out Result result); if (result.Succeeded()) { Debug.Log("found " + mods.Length.ToString() + " temp mods installed"); } else { Debug.Log("failed to get temp installed mods"); } }
The mod.io SDK supports analytic features, allowing you to view your players' mod activity.
The following documentation walks you through the setup process and gives example usages.
When you want to start tracking analytic data you can call StartAnalyticsSession()
. This function requires a unique sessionId
, an array of modIds
being used in the session,
and the autoStartHeartbeat
boolean that represents automation of the analytics heartbeat.
[!NOTE] You will need to cache your session id as it will be needed later.
[!NOTE] It is recommended that you allow automation of the heartbeat function by passing in
true
.
string[] modIds;
string sessionId;
void Example()
{
var r = await ModIOUnityAsync.StartAnalyticsSession(sessionId, modIds, true);
if (r.result.Succeeded())
{
//Store the returned session id to end the session later.
sessionId = r.value;
Debug.Log("Successfully sent start analytics session request");
}
else
{
Debug.Log("Failed to send start analytics session request");
}
}
If you decide that you want more control over when the heartbeat function is called, you can manually call SendAnalyticsHeartbeat()
. This function tells our backend that the session is still active. The only parameter required is the sessionId
that you used to start the analytics session.
[!NOTE] Each call to
SendAnalyticsHeartbeat()
should be at least 5 minutes apart.string sessionId;
async void Example() { Result r = await ModIOUnityAsync.SendAnalyticsHeartbeat(sessionId);
if (r.result.Succeeded())
{
Debug.Log("Successfully sent analytics heartbeat request");
}
else
{
Debug.Log("Failed to send analytics heartbeat request");
}
}
When you are ready to end the session, you can call ```EndAnalyticsSession()``` by passing in the ```sessionId``` that you used to start the analytics session.
```csharp
string sessionId;
async void Example()
{
Result result = await ModIOUnityAsync.EndAnalyticsSession(sessionId);
if (result.Succeeded())
{
Debug.Log("Successfully sent end analytics request");
}
else
{
Debug.Log("Failed to send end analytics request");
}
}