FancyModLoader icon indicating copy to clipboard operation
FancyModLoader copied to clipboard

Conditional mods & event bus subscribers

Open wired-tomato opened this issue 1 year ago • 12 comments

Conditional class loading described in #201

@DependsOn("modid")
@Mod("testmod")
//class loaded if modid is present
public class TestMod {
    public TestMod() { }
}
@DependsOn({"modid", "modid2"})
@EventBusSubscriber(modid = "testmod")
//class loaded if modid and modid2 are present
public class TestModEvents {
    public void configEvent(ModConfigEvent event) {}
}

wired-tomato avatar Nov 12 '24 04:11 wired-tomato

  • [ ] Publish PR to GitHub Packages

Can you use @Dependency without wrapping it in @ChainDependency? I'm quite sure most use-cases would be just depending on one single modid.

tmvkrpxl0 avatar Nov 12 '24 06:11 tmvkrpxl0

Uh oh I accidentally mentioned someone

tmvkrpxl0 avatar Nov 12 '24 06:11 tmvkrpxl0

I don't like how this is modeled. dependencies is not the right term to do conditional loading, and I think the annotation nesting soup is getting a bit out of hand... It'll be some time until I can take a proper look at the impl too, but I think I'd prefer repeatable annotations separate from the top-level annotation used.

shartte avatar Nov 12 '24 08:11 shartte

Looking at the beginning of the impl, it seems you can use dependencies = @ChainDependency(@Dependency("modid")). Though that's still too verbose in my opinion.

Gaming32 avatar Nov 12 '24 13:11 Gaming32

Would a model like this be preferable?

@DependsOn(value = "neoforge", condition = Condition.ALL_PRESENT, operator = Operator.AND)
@DependsOn(value = {"othermod", "othermod2"}, condition = Condition.AT_LEAST_ONE_PRESENT, operator = Operator.OR)
@DependsOn("othermod3")
@Mod("testmod")
public class TestMod {
    public TestMod() {
        //called when neoforge and one of othermod or othermod2 are present or when othermod3 is present
    }
}

The least verbose one dependency would be @DependsOn("dependency") This model also avoids changing the definitions of @Mod and @EventBusSubscriber

wired-tomato avatar Nov 12 '24 13:11 wired-tomato

Why do you need such a complex expression language beyond simple AND/OR which could be expressed through a repeatable annotation / value array attribute?

shartte avatar Nov 12 '24 14:11 shartte

I think that OR conditions are quite suspicious if the goal is to avoid crashes because of missing classes.

Technici4n avatar Nov 12 '24 16:11 Technici4n

The new model is comprised of AND/ORs and their respective negations ORs are included because there are cases in which multiple mods provide the same public API Negations are included because there are cases in which mod conflictions end up changing certain behaviors

@DependsOn("neoforge")
@DependsOn(value = { "othermod", "othermod2" }, condition = DependsOn.Operation.OR)
@DependsOn("othermod3")
@DependsOn.List({
        @DependsOn("neoforge"),
        @DependsOn(value = { "othermod", "othermod2" }, condition = DependsOn.Operation.OR),
        @DependsOn("othermod3")
})

Both of the preceding annotations will behave the same

Refer to the @DependsOn annotation class for further details

wired-tomato avatar Nov 12 '24 22:11 wired-tomato

The main goal of checking for dependencies in the annotation like this, is to prevent accidentally loading code that references nonexisting classes. If you add NOT or OR dependencies, that flies out of the window, and you might as well just add the following code instead of writing convoluted annotations:

if (ModList.get().isModLoaded("a") && !ModList.get().isModLoaded("b")) {
    bus.register(AButNotBEventHandler.class);
}

Technici4n avatar Nov 13 '24 09:11 Technici4n

Removed ORs and NOTs, @DependsOn now only takes an array of modids that must all be present for the class to be loaded

wired-tomato avatar Nov 14 '24 22:11 wired-tomato

@wired-tomato, this pull request has conflicts, please resolve them for this PR to move forward.