Jump to content

Recommended Posts

Posted

Hi, I recently released the sprinklers mod (https://mods.vintagestory.at/show/mod/33016). A question I get asked a lot, if I could make this mod compatible with feature X from another mod. The most requested one is allowing to make sprinklers interact with fluid pipes from other mods. Now I could go, add the mod into my csproj file and start coding, using the other mods' API to make it interact with a pipe.

But that's exactly the issue. I believe that when I go and do that, the sprinklers mod will require mod B as a dependency, lowering the ease of installing, as I want this mod to work as a standalone as well. Some ideas I had:

  • Make a parallel mod, using the first ones codebase to specifically separate the dependencies. The issue with this one is, it would require maintenance for each supported mod and make development in the future a mess.
  • Add some sort of conditional "patching", which would run some code to modify classes in case one of the other mods are detected. This seems pretty difficult and out of my personal C# dev knowledge.

As I am not the most experienced C# dev, I'd be happy to hear about your ideas to make this work or at least guide me to the right direction to be able to continue developing my mod.

Thanks in advance!

Posted (edited)
18 hours ago, Jan5676 said:

But that's exactly the issue. I believe that when I go and do that, the sprinklers mod will require mod B as a dependency, lowering the ease of installing, as I want this mod to work as a standalone as well. Some ideas I had:

This depends on how you do it, as long as you make sure to only access the code from the other mod when it is actually present then it will work just fine. Keep in mind however that this doesn't always behave as you might expect when looking at the uncompiled code, variables for instance are always declared at the start of the method when compiled, so doing this would fail if the mod is not present:

public void DoStuff(ICoreAPI api)
{
    if(api.ModLoader.IsModEnabled("ModB"))
    {
        var modBModSystem = api.ModLoader.GetModSystem<ModBModSystem>();
        modBModSystem.PerformAction();
    }
}

But if you extracted the content of the `if` statement to a new method then it would work.

In your scenario you would probably want to make create a collection of delegates and only register the method for each different mod if they are present, which would look roughly like this:

//Returns wether the provider is able to provide water and how much water was provided
public delegate bool WaterProviderDelegate(ICoreAPI api, BlockPos pos, Block block, BlockEntity blockEntity, int requestedAmount, out int waterRetrieved);

public class SprinklerModSystem : ModSystem
{
    internal static readonly HashSet<WaterProviderDelegate> WaterProviders = [];

    public override void Start(ICoreAPI api)
    {
        if (api.ModLoader.IsModEnabled("hydrateordiedrate"))
        {
            WaterProviders.Add(WaterProvider_HoD);
        }
    }

    private static bool WaterProvider_HoD(ICoreAPI api, BlockPos pos, Block block, BlockEntity blockEntity, int requestedAmount, out int waterRetrieved)
    {
        waterRetrieved = 0;

        if (blockEntity is BlockEntityPipe)
        {
            if (FluidSearch.TryFindWellSpring(api.World, pos, out var foundWellSpring, maxVisited: 4096))
            {
                int delta = foundWellSpring.TryChangeVolume(-requestedAmount);
                waterRetrieved = -delta;
            }
            return true;
        }

        return false;
    }

    public override void Dispose()
    {
        base.Dispose();
        WaterProviders.Clear();
    }
}

public class BlockEntitySprinkler : BlockEntity
{
    public int WaterCapacity { get; protected set; } = 1000;

    public int CurrentWaterAmount { get; set; } = 0;

    public override void ToTreeAttributes(ITreeAttribute tree)
    {
        base.ToTreeAttributes(tree);
        tree.SetInt("currentWaterAmount", CurrentWaterAmount);
    }

    public override void FromTreeAttributes(ITreeAttribute tree, IWorldAccessor worldAccessForResolve)
    {
        base.FromTreeAttributes(tree, worldAccessForResolve);
        CurrentWaterAmount = tree.GetInt("currentWaterAmount", 0);
    }

    public override void Initialize(ICoreAPI api)
    {
        base.Initialize(api);
        if (api.Side != EnumAppSide.Server) return;
        RegisterGameTickListener(OnServerTick, 1000);
    }

    private void OnServerTick(float timePassedInMs)
    {
        CollectWaterFromProviders();

        //Actually do something with the water
    }

    public void CollectWaterFromProviders()
    {
        if (SprinklerModSystem.WaterProviders.Count == 0) return;

        int remainingCapicity = WaterCapacity - CurrentWaterAmount;
        if (remainingCapicity <= 0) return;

        var downPos = Pos.DownCopy();
        var block = Api.World.BlockAccessor.GetBlock(downPos);
        var blockEntity = Api.World.BlockAccessor.GetBlockEntity(downPos);

        foreach (var provider in SprinklerModSystem.WaterProviders)
        {
            if (provider(Api, downPos, block, blockEntity, remainingCapicity, out int waterCollected))
            {
                CurrentWaterAmount += waterCollected;
                break;
            }
        }
    }
}


 

Edited by The Insanity God
Posted
4 minutes ago, The Insanity God said:
internal static readonly HashSet<WaterProviderDelegate> WaterProviders = [];

Actually should probably make this public or add a public add method. That way support can also be added the other way around (as in the other mod could register a method of their own to your mod if present)

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.