Mod proxies are a way of abstracting mod integrations away through a common interface and conditionally use different implementations based on which other mods are loaded in the environment. This enables seamless integration with third party mods without introducing hard dependencies or loader-specific code.
Mod proxies provide:
The most common use case is to create a specific implementation when a mod is present, with a fallback for when it's not:
MyInterface implementation = Balm.<MyInterface>modProxy()
.with("othermodid", "com.example.mod.compat.OtherModImplementation")
.withFallback(new DefaultImplementation())
.build();
This code will:
"othermodid" is loadedOtherModImplementation classDefaultImplementation) you provideYou can chain multiple mod integrations:
MyInterface implementation = Balm.<MyInterface>modProxy()
.with("modid-a", "com.example.mod.compat.ModAImplementation")
.with("modid-b", "com.example.mod.compat.ModBImplementation")
.withFallback(new DefaultImplementation())
.build();
By default, the first loaded mod's implementation (in order of .with() calls) will be used. If neither mod is loaded, the fallback is used.
In some cases, you might want to specially handle multiple implementations being present instead of just using the first one:
MyInterface implementation = Balm.<MyInterface>modProxy()
.with("modid-a", "com.example.mod.ModAImplementation")
.with("modid-b", "com.example.mod.ModBImplementation")
.withMultiplexer(implementations -> new CombinedImplementation(implementations))
.withFallback(new DefaultImplementation())
.build();
The multiplexer function receives a list of all loaded implementations and should return a single combined implementation.
You can delay initialization of the mod proxy until it is accessed by using buildLazily().
This is recommended if you define a proxy outside of the typical mod lifecycle, such as in a static field initializer. Mod Proxies need to access the mod list when they are built, and there's no guarantee it's available by the time your class is loaded.
Balm itself uses mod proxies for its Trinkets/Curios integration, as well as its Permissions support on Fabric.
BalmPermissions permissions = Balm.<BalmPermissions>modProxy()
.with("fabric-permissions-api-v0", "net.blay09.mods.balm.fabric.compat.FabricPermissionsAPIIntegration")
.withFallback(new CommonBalmPermissions())
.build();
This code creates a permissions system that integrates with fabric-permissions-api (third-party-mod) when available, but falls back to a standard implementation when the API isn't present.
Implementation classes loaded through the mod proxy system must:
Balm also provides other proxy types: