Integration

uMod provides an interface or intermediate layer to bring together two separate plugins which then cooperate ensuring that both plugins function together as a single system.

Providing integrations with other plugins is one of the single best value adding propositions in the uMod ecosystem.

Modularity and interoperability

It is recommended that plugins follow SOLID object oriented design principles. Using these principles correctly often means that disparate functionality is refactored down to constituent parts, each limited to their specific scope and responsibility.

This approach is beneficial because, not only does it prevent code from becoming spaghetti (or a big mess), it also ensures each code unit is separated into easily digestable parts for other programmers to understand.

Plugin dependencies

Creating a dependency is usually the first step to creating a plugin integration.

As outlined in the dependencies documentation, there are three (3) different types of dependencies: optional, required, and hard.

A basic plugin reference:

[PluginReference]
private Plugin EpicStuff;
The name of the property (e.g. "EpicStuff") must match the class name of the plugin being referenced exactly.

Call method

After creating a dependency, a plugin may call specific methods of another plugin.

private void Loaded()
{
    if (EpicStuff != null) // check if plugin is loaded
    {
        EpicStuff.Call("SomeMethod", "argument1", "argument2");
    }
}

When return behavior is required the call method returns an object by default. The call method has an optional generic method which may be used to explicitly cast the result.

bool someResponse = EpicStuff.Call<bool>("SomeMethod", "argument1", "argument2");
if(someResponse)
{
    Puts("SomeMethod Response: True");
}        

In order for the above implementations to work, the EpicStuff plugin must have a method that matches the signatures used above.

// In EpicStuff.cs
private bool SomeMethod(string argument1, string argument2)
{
    Puts($"Do stuff: {argument1} {argument2}");
    return true;
}

Hook conflicts

Hooks that have return behavior may conflict when multiple plugins using a hook return different values. In such cases, it is often necessary for one plugin or the other to integrate and resolve the conflict.

Hook conflicts usually print a message like..

[Warning] Calling hook CanUserLogin resulted in a conflict between the following plugins: MyPlugin - True (Boolean), EpicPlugin (False (Boolean))

The solution is usually to integrate MyPlugin with EpicPlugin (or vice versa) to give one plugin's hook precedence over the other.

bool CanUserLogin (string name, string id, string ip)
{
    if (EpicPlugin != null)
    {
        var result = EpicPlugin.Call ("CanUserLogin", name, id, ip);
        if (result is bool)
        {
            return (bool)result;
        }
    }

    Puts("No conflict, do plugin stuff here");

    return true;
}

Custom hooks

Integrations do not necessarily require dependencies, sometimes simply using a custom hook is sufficient.

For example, if a plugin creates a backpack for players to store their items, it could create a custom hook called CanCreateBackpack. Other plugins could then implement the CanCreateBackpack hook, and prevent the player from using their backpack in certain situations (e.g. being in an arena).

This might be part of a backpack plugin:

bool result = Interface.Oxide.CallHook<bool>("CanCreateBackpack", player);
if (!result)
{
    return;
}

// Create backpack

Then in the integration plugin:

private object CanCreateBackpack(IPlayer player)
{
    if (IsInArena(player))
    {
        return false;
    }

    return null;
}