FabricMC / fabric-example-mod

Example Fabric mod
Creative Commons Zero v1.0 Universal
1.65k stars 933 forks source link

Encourage depending on a subset of the Fabric API rather than the entire thing #175

Open LoganDark opened 2 years ago

LoganDark commented 2 years ago

This project currently depends on the entire Fabric API, with no mention of fabricApi.module:

https://github.com/FabricMC/fabric-example-mod/blob/06e3e43ee0662fd1afecebb428cb04cdc271b0ab/build.gradle#L28

https://github.com/FabricMC/fabric-example-mod/blob/06e3e43ee0662fd1afecebb428cb04cdc271b0ab/src/main/resources/fabric.mod.json#L31

This encourages oversights like https://github.com/QuiltServerTools/Ledger/issues/163, which force server / modpack admins to always include all of Fabric API, even those modules which are not actually required for the mods to function.

The first question on everyone's mind whenever I bring this up is usually "But why in the world wouldn't you want to always include the entirety of Fabric API?", I'll answer that first because it also explains why the current approach isn't the most optimal.

There are API modules that only add harmless mixin hooks, but there are also modules that introduce side effects that may not be needed to support many (or even most) mods. For example, registry sync is only relevant when the registries need syncing (AKA, only when registries are modified), resource loader is only necessary when resources need loading (for example, mod icons for Mod Menu), networking is only necessary when the client needs to know what communication channels the server supports, etc.

Fabric is much, much more performant even with Fabric API than any other mod loader, especially Forge, but having all these unnecessary modules still kind of irks me. Fabric API is designed to be modular and there's already support in Loom for easily declaring support on individual API modules, so why not use it?

I propose commenting out that modImplementation line, and adding one that only depends on a singular module - say fabric-resource-loader-v0 - along with an explanation of what the module does, why include only that module, and how to find the names of other modules you may need. Additionally, in fabric.mod.json, only declare dependencies on the modules you need.

It would look something like this:

    // Declare a dependency on the "fabric-resource-loader-v0" API module.
    // This module loads mod resources and data as resource and data packs.
    // You need it to be able to add textures to blocks, or use TranslatableText.
    // Another useful module is "fabric-registry-sync-v0" if your mod modifies any vanilla registries such as biomes.
    // You can find the full list of modules at https://github.com/FabricMC/fabric - each one has its own directory.
    modImplementation(fabricApi.module("fabric-resource-loader-v0", project.fabric_version))

(That's a rough draft, the final comment would probably look significant different - I'm not great at writing.)

This would teach beginning modders (and also seasoned modders just getting started with Fabric) that Fabric API is not a monolith, and you can pick and choose what you need. This should be encouraged, even - you should be precise, and avoid being too liberal with your dependencies.

This would improve my experience as a modder, as a user, and as a server admin. It will not fix existing mods on its own, but it will set an important precedent for new mods, and serve as a good example for existing ones, encouraging them to switch. I believe this approach is more sustainable than letting every mod depend on the entire ever-growing Fabric API, as not every setup should require every API module to be present.

Plus it would reinforce the fact that Fabric can be a really lightweight mod loader - I love the idea of it just being used to tweak the game and optimize it and build around it. Requiring the entire Fabric API all the time kind of detracts from that, you know?

This may be a niche use case, but I think it's an important one to consider, not because you can expect most or even more than a few Fabric users to install individual API modules, but because, in general, you shouldn't over-depend.

Thanks for reading my super long post, feel free to AMA.

Juuxel commented 2 years ago

IMO this should only be done to the example mod once Loader has the ability to specify the dependencies more precisely, including urls.

Currently, users even get confused with where to get the mod called fabric ("I need fabric?? But I already have this Fabric mod loader installed!?"). Having mods like fabric-loot-tables-v1 etc that can't be found in any user-facing source like Curseforge (except bundled in Fabric API) would even increase this confusion further unless/until Loader's dep handling improves, so we can point users to Fabric API's CF page, for example.

LoganDark commented 2 years ago

IMO this should only be done to the example mod once Loader has the ability to specify the dependencies more precisely, including urls.

Currently, users even get confused with where to get the mod called fabric ("I need fabric?? But I already have this Fabric mod loader installed!?").

This could have been avoided by naming it fabric-api instead of fabric, but too late to fix that I guess. (sigh)

Having mods like fabric-loot-tables-v1 etc that can't be found in any user-facing source like Curseforge (except bundled in Fabric API) would even increase this confusion further unless/until Loader's dep handling improves, so we can point users to Fabric API's CF page, for example.

I do like this idea, and I do believe it should be included in the next schema version. It can be hard for users to tell the difference between first-party Fabric API modules, and third-party ones like fabric-permissions-api-v0. So knowing where to get the module might be a real issue.

I personally hope Fabric will support/encourage/have first-party support for Modrinth links rather than CF. (shedaniel and his revenue-sharing-only policy can still use CurseForge, I guess.)

Juuxel commented 2 years ago

This could have been avoided by naming it fabric-api instead of fabric, but too late to fix that I guess. (sigh)

There's an API PR for this (it can be done with Loader's support for aliases with the provides array in fabric.mod.json), but apparently it's stalled for potential future dev with the mod loading behaviour: https://github.com/FabricMC/fabric/pull/1263#issuecomment-865411305

It can be hard for users to tell the difference between first-party Fabric API modules, and third-party ones like fabric-permissions-api-v0.

I'd say the permissions api is badly named here since it copies the naming pattern (fabric-, -api and even the -vN) but isn't an actual FAPI module.

I personally hope Fabric will support/encourage/have first-party support for Modrinth links rather than CF.

(this is getting off-topic, but) IMO the best solution here would just be to support an array of URLs where the user can choose one. (So mods could provide both CF and MR for example)

dexman545 commented 2 years ago

This at the very least needs to wait for loader changes to make error messages more useful.

Module discovery for modders is also an issue that needs to be dealt with.

The granule option should be mentioned in the build.gradle, but not used as a default.

LoganDark commented 2 years ago

Module discovery for modders is also an issue that needs to be dealt with.

This is a valid concern. Usually you can tell which module something comes from based on its package name, but if you haven't brought that into your IDE yet, then you won't be able to find the classes in the first place. If you bring in all of Fabric API, then comes the issue of keeping what you actually use & what you declare in fabric.mod.json in sync. Perhaps Loom should have a feature built-in for this, like how it already has fabricApi.module built-in. I could imagine a processResources step that detects which API modules you use & automatically adds them to your mod json.