Heads up!
Added in Fzzy Config 0.5.3
The Result Provider system is a framework for retrieving values from a config without directly interacting with that config in-code.
A straightforward example is a loot table with a configurable drop chance for a loot pool. Traditionally, the only way to modify this would be to override the loot table with a data pack. Result Providers let us define a new loot number provider that draws its value from a config, allowing the drop chance to be altered in-game from a config GUI.
Result Provider
A result provider is an interface with one job: provide a result based on a passed config scope
. The Translation page has an overview of what scope
is in Fzzy Config's context.
ResultProvider<Boolean> myProvider = TODO();public boolean getMyResult() {return myProvider.getResult("my_mod.my_config.mySetting");}
Creating a Provider
Create an implementation of a Result Provider using the ResultApi
🗗 via ConfigApi.result()
or ConfigApiJava.result()
Using the example from above, let's now add a simple provider. This system requires a fallback (false in this case).
ResultProvider<Boolean> myProvider = ConfigApiJava.result().createSimpleResultProvider(false, Boolean.class);public boolean getMyResult() {return myProvider.getResult("my_mod.my_config.mySetting");}
Using the Provider
The above examples show a simple usage example.
Heads up!
The scope requested is dynamic; it can point to any valid boolean-producing setting in any registered config. Including configs of other mods.
This makes result providers a convenient way to interface with another mod without having to depend on it in-code. If for example, you are an addon to another mod, interfacing with a config setting this way is a change-robust way of retrieving the value you need. The parent config can add 5000 other settings, change package name, class name, and so on, and the value retrieval will still work. In the case that the scope becomes invalid, fzzy config will fail soft; throwing a log error and using your fallback approach.
//both of these are valid targets for the boolean provider created aboveboolean mySetting = false;ValidatedBoolean mySetting2 = new ValidatedBoolean(false);
Example: Loot Table with dynamic drop chance
Below is an example loot table. It includes a rare drop that is valuable for mod progression, like a wither skeleton skull.
Currently, the drop is hard-wired to exactly the same random chance as a skull.
Using result providers we can make this table dynamically linked with a config.
{"type": "minecraft:entity","pools": [{"entries": [{"type": "minecraft:item","name": "minecraft:redstone"}],"rolls": 1.0},{"conditions": [{"chance": 0.025,"condition": "minecraft:random_chance_with_looting","looting_multiplier": 0.01}],"entries": [{"type": "minecraft:item","name": "my_mod:important_progression_item"}],"rolls": 1.0}]}
Float Provider
To make this table dynamic, we will need a provider of float values to feed into our loot number provider.
Heads up!
This provider could be improved to accept any number type, but that is beyond the scope of this example
ResultProvider<Float> floatProvider = ConfigApi.result().createSimpleResultProvider(0f, Float.class);
Number Provider
Next we wire this provider into a LootNumberProvider
. Note the scope param. This is the scope passed in from the loot table definition that we will be providing from our config.
Heads up!
Methods that are unnecessary to illustrate this example are excluded. Assume that things like codec, type, and so on are implemented.
public class ConfigLootNumberProvider implements LootNumberProvider {private final String scope;public ConfigLootNumberProvider(String scope) {this.scope = scope;}@Overrridefloat nextFloat(LootContext context) {return floatProvider.getResult(scope);}//getType, registration of the type, codec, etc. are assumedprivate static final ResultProvider<Float> floatProvider = ConfigApiJava.result().createSimpleResultProvider(0f, Float.class);}
Json Integration
Let's assume our type and codec are registered under my_mod:chance_provider
and accepts one string scope
. The number provider json would look like:
{"type": "my_mod:chance_provider","scope": "my_mod.my_config.progressionDropChance"}
The Config
Our config, registered under the identifier my_mod:my_config
needs the setting(s) we want to refer to.
It might also be a good idea to include the looting multiplier. (And an even better idea to move these into a separate section/object/group for organization)
public class MyConfig extends Config {public MyConfig() {super(Identifier.of("my_mod", "my_config"));}@ValidatedFloat.Restrict(min = 0f, max = 1f)float progressionDropChance = 0.025f;@ValidatedFloat.Restrict(min = 0f, max = 1f)float progressionLootingMultiplier = 0.01f;}
Table Integration
The pieces can now be tied together; the new provider condition can be used in loot tables.
This table will now have its chance dynamically updated from the config definition. Updates to the config values will be applied right away, no resource reload needed.
{"type": "minecraft:entity","pools": [{"entries": [{"type": "minecraft:item","name": "minecraft:redstone"}],"rolls": 1.0},{"conditions": [{"chance": {"type": "my_mod:chance_provider","scope": "my_mod.my_config.progressionDropChance"},"condition": "minecraft:random_chance_with_looting","looting_multiplier": {"type": "my_mod:chance_provider","scope": "my_mod.my_config.progressionLootingMultiplier"}}],"entries": [{"type": "minecraft:item","name": "my_mod:important_progression_item"}],"rolls": 1.0}]}