Jan5676 Posted December 31, 2025 Report Posted December 31, 2025 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!
The Insanity God Posted January 1 Report Posted January 1 (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 January 1 by The Insanity God
The Insanity God Posted January 1 Report Posted January 1 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)
Recommended Posts