Registering your UpgradeHandler - PrimeSonic/PrimeSonicSubnauticaMods GitHub Wiki

Originally introduced in the 3.0 update and later refined in the 4.0 overhaul, MoreCyclopsUpgrades can now be used as a public API, allowing other mods to integrate their own cyclops upgrade modules and have them be fully compatible with the Auxiliary Upgrade Console.


Registering your UpgradeHandler

Assuming you've read about UpgradeHandler and how to create your own, here you'll learn how to send it over to the MoreCyclopsUpgrades API.

You may have noticed that the UpgradeHandler constructor requires a reference to the Cyclops SubRoot.

public UpgradeHandler(TechType techType, SubRoot cyclops)

You might be asking yourself: "How do I instantiate an UpgradeHandler if I don't have the SubRoot reference?"
The answer to that question is: "You don't. MoreCyclopsUpgrades will instantiate it for you when the Cyclops is ready."

The API isn't asking you to provide your actual UpgradeHandler, but rather to provide code that it can run to create your UpgradeHandler. This is known as an Abstract Factory Pattern.

Everything starts with a call into MCUServices.Register.CyclopsUpgradeHandler. You'll notice that the parameter for this method isn't an UpgradeHandler instance, but instead it's expecting something that can return a new UpgradeHandler when called.

The only rule is that this registration call must happen within Patch time.
Once a Cyclops has been loaded into the game, it's too late.

The API offers a few different ways you can do this, so you can choose whichever style you find most comfortable.

/// <summary>
/// Registers a <see cref="CreateUpgradeHandler"/> method that creates a new <see cref="UpgradeHandler"/> on demand.<para/>
/// This method will be invoked only once for each Cyclops sub in the game world.
/// </summary>
/// <param name="createEvent">A method that takes no parameters a returns a new instance of an <see cref="UpgradeHandler"/>.</param>
void CyclopsUpgradeHandler(CreateUpgradeHandler createEvent);

/// <summary>
/// Registers a <see cref="CreateUpgradeHandler"/> class can create a new <see cref="UpgradeHandler"/> on demand.<para/>
/// This method will be invoked only once for each Cyclops sub in the game world.
/// </summary>
/// <param name="handlerCreator">A class that implements this <see cref="IUpgradeHandlerCreator.CreateUpgradeHandler(SubRoot)"/> method.</param>
void CyclopsUpgradeHandler(IUpgradeHandlerCreator handlerCreator);

Option 1 - Using a method that returns your UpgradeHandler

This form takes in a CreateUpgradeHandler delegate method as its one parameter.
The CreateUpgradeHandler delegate defines this pattern: UpgradeHandler CreateUpgradeHandler(SubRoot);
This means that method to be used here.

  1. Must take in a SubRoot cyclops as its only parameter
  2. Must return an object that is or inherits from UpgradeHandler.

It is expected that you will pass the SubRoot cyclops reference to the UpgradeHandler constructor.

public static class QPatch
{
    private static TechType myCyclopsUpgrade;
    public static void Patch()
    {
        // Your patching code
        ....
        // In this pattern, you can pass the method by name
        MCUServices.Register.CyclopsUpgradeHandler(CreateMyUpgradeHandlerMethod);
    }

    private static CreateMyUpgradeHandlerMethod(SubRoot cyclops)
    {
        return new UpgradeHandler(myCyclopsUpgrade, cyclops)
        {
            // You could set up your UpgradeHandler right here
            ....
        };
    }
}

This can also be done using anonymous methods.
As long as the anonymous method follows the same requirements, it doesn't have to have a name.

public static class QPatch
{
    private static TechType myCyclopsUpgrade;
    public static void Patch()
    {
        // Your patching code
        ....
        // In this pattern, you can pass the method by name
        MCUServices.Register.CyclopsUpgradeHandler((SubRoot cyclops) =>
        {
            return new UpgradeHandler(myCyclopsUpgrade, cyclops)
            {
                // You could set up your UpgradeHandler right here
                ....
            };
        });
    }

Option 2 - Using a class that implements the IUpgradeHandlerCreator interface

If passing methods as a parameter feels a little too weird to you, this alternative might serve you better.
This interface requires a class to implement a method named CreateUpgradeHandler.
Just as before, this CreateUpgradeHandler must

  1. Take in a SubRoot cyclops as its only parameter
  2. return an object that is or inherits from UpgradeHandler.

It is expected that you will pass the SubRoot cyclops reference to the UpgradeHandler constructor.

public static class QPatch
{
    private static TechType myCyclopsUpgrade;
    public static void Patch()
    {
        // Your patching code
        ....        
        var myMaker = new MyUpgradeHandlerMaker()
        MCUServices.Register.CyclopsUpgradeHandler(myMaker);
    }
}

internal class MyUpgradeHandlerMaker : IUpgradeHandlerCreator
{
    public UpgradeHandler CreateUpgradeHandler(SubRoot cyclops)
    {
        return new MyUpgradeHandler(cyclops);
    }
}

internal class MyUpgradeHandler : UpgradeHandler
{
    // You could also create your UpgradeHandler as a new class
    internal MyUpgradeHandler(SubRoot cyclops) 
         : base(QPatch.myCyclopsUpgrade, cyclop)
    {
    }
    ....
}

There are many different ways to set up your UpgradeHandler,
but registering it with MCUServices boils down to either passing in a method that conforms to the CreateUpgradeHandler delegate -or- a class that implements IUpgradeHandlerCreator interface.

Either way, what you are doing is providing the API with a way to create your UpgradeHandler when the Cyclops is being set up.

Now that all of the original upgrade modules previously added directly by MoreCyclopsUpgrades have been split off into their own optional mods, you will find no shortage of examples to follow.

⚠️ **GitHub.com Fallback** ⚠️