Skip to main content

Multiplayer

The mod.io Unreal Engine Plugin provides a framework of features to support multiplayer servers, be they headless dedicated servers, or player hosted P2P/listen servers.

This framework does not take the place of any kind of online matchmaking, server advertising, joining, replication, communication, or any form of information transfer between clients and the server. It is platform agnostic and does not take the place of any online subsystem. It enables the authentication of the mod.io subsystems in a headless environment, as well as providing helpers for tracking the use of UGC by players on your server, and providing a single source of truth for the full list of UGC in use by the Server and all Clients.

With regards to clients (non-hosts, non-servers), whether joining a dedicated server or a listen server P2P session, their initialization and UGC acquisition methods are unchanged. A client is expected to initialize all Server UGC, as well as any UGC in use by other clients that is needed for the game experience, as a Temporary Mod Set.

Listen servers​

A listen server (or P2P multiplayer) is, from the perspective of initialization and authentication with mod.io services, no different from a normal player. All clients joining a P2P/listen hosted game session should replicate their mods to one another for initialization as a Temporary Mod Set, using the host to marshal them together. Upon leaving the session each client can simply close their temp mod set which will uninstall the relevant UGC. Listen servers are not intended to be used with the mod.io Dedicated Server framework.

An example of communication between a listen server (the Host) and its Clients would be as follows:

Dedicated servers​

In the case of Dedicated Servers, the process of authentication and initialization requires an OAuth token to be created on the mod.io website which will provide the Server the same access and entitlements as the user (generally the Server owner) creating it, within the scope of the game in question.

This OAuth token, along with a list of UGC intended to be used on the Server, needs to be passed to UModioMultiplayerSubsystem::InitializeServerAsync to perform headless authentication. Once the server is online, initialized, and has players join, there are some helper functions for registering client UGC with the server to facilitate easier replication of UGC lists with all clients.

Overview​

A general example of how the framework is intended to be used is as follows:

As you can see, our framework is intended to only be directly interacted with via the dedicated server itself; clients still authenticate and acquire mods via the conventional mod.io subsystem, and it is up to the developer to get that list of subscribed mods to the server itself. The server can then utilize our framework to manage its UGC, as well as maintain an up-to-date list of client UGC across all clients.

At certain points in the above diagram you will note "Your Validation Check". These indicate places in the execution chain where you can insert your own steps for things like validation, the accepting or refusing of UGC, and filtering based on things like tags and content. These steps allow you to have more direct control over what UGC is utilized by both clients and the server. You could even add your own step to fully download all client UGC onto the server in a temporary mod set, if you wanted.

Initialization​

Dedicated Servers require a specific initialization process to properly authenticate with the mod.io service in order to acquire UGC. Part of this initialization process is the creation of an OAuth token on the mod.io website which provides scoped-permissions for the Server to properly acquire UGC.

To do this, a user must go to their API Settings on the mod.io website and create an OAuth token for their Server to use. Once they have this token the dedicated server must pass this OAuth token, along with a number of other initialization parameters in addition to the standard initialization options:

  • ModsDirectory - the directory on the server machine that UGC files should be stored in.
  • Token - the OAuth token discussed above.
  • Mods - An array of Mod IDs indicating what mods this Server should download.

These options can be configured by the server owner however the developer wishes: config files, launch arguments, etc, and are up to the developer to implement and capture prior to initialization.

These should be passed to UModioMultiplayerSubsystem::InitializeServerAsync:

/// On the Server
void UModioServerManagerSubsystem::InitializeServer()
{

if (UModioMultiplayerSubsystem* Subsystem = GEngine->GetEngineSubsystem<UModioMultiplayerSubsystem>())
{
// Your Init Settings
FModioServerInitializeOptions InitOptions;

Subsystem->InitializeServerAsync(InitOptions, FOnErrorOnlyDelegateFast ::CreateUObject(this, &UModioServerManagerSubsystem::OnInitializationComplete));
}
}

void UModioServerManagerSubsystem::OnInitializationComplete(FModioErrorCode ErrorCode)
{
if (!ErrorCode)
{
// The Server was successfully authenticated with the mod.io service.
}
}
note
  • The error-handling in this sample has been omitted. See Error handling for more information.
  • InitializeServerAsync is an asynchronous function, therefore you must wait for the callback for confirmation that the initialization is complete.

UGC acquisition​

To actually download the mods listed in the Initialization parameters, and/or additional mods, you must call UModioMultiplayerSubsystem::InstallOrUpdateServerModsAsync to trigger the download of mods at your discretion. This function accepts an Array of ModIDs to download, which will be acquired in addition to any listed during initialization.

note

Mod Management needs to be enabled for the Server to acquire its mods, and as such the call will fail if it is not enabled.

/// On the Server
void UModioServerManagerSubsystem::GetServerMods()
{

if (UModioMultiplayerSubsystem* Subsystem = GEngine->GetEngineSubsystem<UModioMultiplayerSubsystem>())
{
// These listed mods will be acquired *in addition* to any specified during Initialization
TArray<FModioModID> Mods;
Mods.Add(12345);
Mods.Add(54321);

Subsystem->InstallOrUpdateServerModsAsync(Mods, FOnErrorOnlyDelegateFast ::CreateUObject(&UModioServerManagerSubsystem::OnGetServerModsComplete));
}
}

void UModioServerManagerSubsystem::OnGetServerModsComplete(FModioErrorCode ErrorCode)
{
if (!ErrorCode)
{
// The Server successfully got the mods.
}
}
note
  • The error-handling in this sample has been omitted. See Error handling for more information.
  • InstallOrUpdateServerModsAsync is an asynchronous function, therefore you must wait for the callback for confirmation that the mod acquisition process is complete.

Querying server UGC​

You can query the base mod.io subsystem for the list of UGC currently installed on the server. As you can add UGC after initialization with InstallOrUpdateServerModsAsync and UGC can fail (bad IDs, network issues) this can be useful to confirm what UGC is available on-disk.

/// On the Server
void UModioServerManagerSubsystem::GetServerMods()
{

// Note that this is the base Mod.io Subsystem, *not* the Mod.io Multiplayer Subsystem
if (UModioSubsystem* Subsystem = GEngine->GetEngineSubsystem<UModioSubsystem>())
{
for(const TPair<FModioModID, FModioModCollectionEntry>& Mod :Subsystem->QuerySystemInstallations())
{
Mod.Key; // The Mod ID
Mod.Value; // The Mod Collection Entry
}
}
}

Joining client UGC​

In cases where Clients who are joining a Dedicated Server have UGC of their own, it may be necessary for the Server to inform other clients of this UGC. For example, if a player has a custom hat, when they join the server all the other players will need to be able to see said hat. Similarly, if any of those players already on the server have their own UGC cosmetics, the new player will need to download those as well.

The mod.io Unreal Engine Plugin handles this via maintaining a simple set of ModIDs in-use on the server. The mod.io plugin does not download these mods on the server itself (as they are considered client-only), it only stores their IDs for easy tracking and replication. You will need to replicate a given Client's list of UGC to the Server and call UModioMultiplayerSubsystem::RegisterClientModsWithServerAsync which, upon completion, will return the complete list of ModIDs that have been added, which you will then need to replicate back to all connected clients for initializing/adding to a Temporary Mod Set.

note

Not all client UGC can remain client-only. If a mod adds content that the server must replicate, such as actors, classes or asset references, the server must also have that mod installed and loaded. The server cannot replicate IDs or asset paths for content it doesn't know about.

Which mods need to be installed on the server is game-specific and up to you to decide. Make this decision in the server-side "Your Validation Check" step shown in the Overview diagram, after RegisterClientModsWithServerAsync completes. For mods the server needs, you can download them onto the server using InstallOrUpdateServerModsAsync or a Temporary Mod Set.

/// On the Server, Mods have been replicated from the joining Client
void UModioServerManagerSubsystem::RegisterClientMods(TArray<FModioModID> JoiningClientMods)
{

if (UModioMultiplayerSubsystem* Subsystem = GEngine->GetEngineSubsystem<UModioMultiplayerSubsystem>())
{
Subsystem->RegisterClientModsWithServerAsync(JoiningClientMods, FAddClientModsDelegateFast ::CreateUObject(&UModioServerManagerSubsystem::OnRegisterClientModsComplete));
}
}

void UModioServerManagerSubsystem::OnRegisterClientModsComplete(FModioErrorCode ErrorCode, TSet<FModioModID> ModIds)
{
if (!ErrorCode)
{
// The Server successfully got the mods.

// ModIds is a TSet containing the full list of Client Mods on the Server
// They will need to be replicated to all Clients.
UYourGameState::ReplicateClientModList(ModIds);
}
}

// On all Clients
void UYourGameState::ReplicateClientModList(TSet<FModioModID> ModIds)
{
if (UModioSubsystem* Subsystem = GEngine->GetEngineSubsystem<UModioSubsystem>())
{
Subsystem->InitTempModSet(ModIds);
}
}
note
  • The error-handling in this sample has been omitted. See Error handling for more information.
  • RegisterClientModsWithServerAsync is an asynchronous function, therefore you must wait for the callback for confirmation that the mod listing is complete.

Querying client UGC​

You can query the server's full list of client UGC added via RegisterClientModsWithServerAsync by calling GetRegisteredClientMods.

/// On the Server
void UModioServerManagerSubsystem::GetClientMods()
{

if (UModioMultiplayerSubsystem* Subsystem = GEngine->GetEngineSubsystem<UModioMultiplayerSubsystem>())
{
for(const TSet<FModioModID>& Mod :Subsystem->GetRegisteredClientMods())
{
// "Mod" is an FModId
}
}
}

Clearing client UGC​

It is not intended that you would deregister a player's UGC from a Server when they leave; they may return, or another player use it in the future (or be using it when the other player left) so to minimize excessive API calls and network usage, we provide only a function to clear the registered client UGC list. It is expected that this will only be called when it is safe to do so, such as when the Server is empty or when starting a new match/round.

/// On the Server, Mods have been replicated from the joining Client
void UModioServerManagerSubsystem::ClearRegisteredClientMods()
{
if (UModioMultiplayerSubsystem* Subsystem = GEngine->GetEngineSubsystem<UModioMultiplayerSubsystem>())
{
Subsystem->ClearRegisteredClientModList();
}
}

References​