Developers
Prosperity exposes a stable, Fabric-style Loot Modifier API so other mods can adjust loot generation — without a hard dependency.
Soft Dependency Setup
Compile against Prosperity with modCompileOnly so it stays optional at runtime, then guard every call with a mod-loaded check.
Gradle:
dependencies {
modCompileOnly "maven.modrinth:prosperity:<version>"
}
Guard calls so your mod loads cleanly whether or not Prosperity is present:
if (FabricLoader.getInstance().isModLoaded("prosperity")) {
// safe to touch the Prosperity API here
}
Loot Modifier API
The API classes live in com.rfizzle.prosperity.api and are annotated @Stable (Prosperity's own marker, per the Concord no-shared-jar rule). LootModifierCallback.EVENT fires after distance scaling but before the loot table resolves, handing each listener a mutable LootModifierContext. Listeners fire in registration order and each sees the cumulative state, so a later listener observes earlier mutations.
import com.rfizzle.prosperity.api.LootModifierCallback;
if (FabricLoader.getInstance().isModLoaded("prosperity")) {
LootModifierCallback.EVENT.register(context -> {
// read context.player(), context.containerPos(), context.lootTable()
context.addLuck(2.0f); // bias quality
context.multiplyStacks(1.5f); // scale stack sizes
// context.customData() is a fresh CompoundTag for inter-mod data
});
}
Loot Table Injection
Datapacks can add custom items to existing vanilla loot tables, gated by a minimum distance tier and optionally by dimension or mod presence. Files live at data/prosperity/loot_injections/<name>.json and use tier names local, frontier, wilderness, outlands, depths. Injection is purely additive — an eligible container receives one weighted-random injected reward in a spare slot, and vanilla loot is never replaced. Prosperity ships a built-in Frontier-through-Depths set, so distance scaling feels meaningful out of the box.
data/prosperity/loot_injections/example.json
{
"replace": false,
"injections": [
{
"target": "minecraft:chests/simple_dungeon",
"min_tier": "frontier",
"entries": [
{
"item": "minecraft:enchanted_book",
"count": 1,
"components": {
"minecraft:stored_enchantments": { "levels": { "minecraft:sharpness": 3 } }
},
"weight": 5
}
]
},
{
"target": "minecraft:chests/bastion_treasure",
"min_tier": "outlands",
"dimensions": [ "minecraft:the_nether" ],
"entries": [ { "item": "minecraft:netherite_scrap", "weight": 2 } ]
}
]
}
target— the loot table id to inject into, or the special wildcardprosperity:all_chests, which fans out to every loot table whose path contains achests/segment.min_tier— the entry is eligible only when the container's resolved distance tier is at or above this tier.dimensions(optional) — a list of dimension ids the entry is restricted to, e.g.["minecraft:the_nether"]. Omitted or empty matches any dimension; when present it composes withmin_tierso both gates must pass.requires_mods(optional) — a list of mod ids that must all be loaded for the entry to apply, e.g.["meridian"]. Omitted or empty is unconditional. Evaluated at load time — an entry naming an absent mod is silently dropped, so one file can mix unconditional injections with ones scoped to a sibling mod.entries[].item— the item id.count(default1) andcomponents(full data-component syntax, as in/give) define the stack.entries[].enchant_randomly(optional) — an enchantment tag id (with or without a#prefix, e.g."#meridian:rarity/rare") that makes the entry generative: at draw time one enchantment is picked uniformly from the tag and stored on the item, so a whole catalog is covered without enumerating it. Mutually exclusive withcomponents. The pick uses the injection draw's deterministic per-player random, so instanced loot stays reproducible. A tag that resolves empty (mod absent, empty tag) drops the entry from the pool before weighting — the slot falls to the remaining entries instead of being wasted.entries[].level(optional, generative entries only) — the level the drawn enchantment is stored at, relative to its own range:mid(the rounded-up midpoint, ⌈max/2⌉),max(the top level), oruniform(the default — a uniform draw over the whole range, vanillaenchant_randomlysemantics).entries[].weight(default1) — the relative chance of this entry among all eligible entries for the container's table and tier.replace(defaultfalse) — whentrue, clears any earlier Prosperity injections for the targets this file touches before adding its own. Vanilla entries are untouched.
For richer cross-mod logic, a file may carry a fabric:load_conditions header — a standard Fabric resource-conditions block giving fabric:and / fabric:or / fabric:not / fabric:all_mods_loaded. It is evaluated before the file is parsed, so an unmet condition skips the whole file silently. This lets a cross-mod injection ship in-jar and self-activate only when its sibling mod is present, instead of as a separate datapack.
data/prosperity/loot_injections/meridian.json
{
"fabric:load_conditions": [
{ "condition": "fabric:all_mods_loaded", "values": [ "meridian" ] }
],
"injections": [
{
"target": "minecraft:chests/stronghold_library",
"min_tier": "outlands",
"requires_mods": [ "meridian" ],
"entries": [
{ "item": "meridian:guide_book", "weight": 1 },
{
"item": "minecraft:enchanted_book",
"enchant_randomly": "#meridian:rarity/rare",
"level": "mid",
"weight": 3
}
]
}
]
}
Injection files reload with /reload, and toggle off entirely via the enableLootInjection config option.
Party Group Provider
When partyLootMode is enabled, players who resolve to the same group key share one loot instance per container. By default the group is the player's vanilla scoreboard team; a social or party mod can override that by registering a PartyGroupProvider, so its own parties drive the sharing. Providers are consulted server-side in registration order under error isolation — the first non-null, non-blank key wins, otherwise Prosperity falls back to the scoreboard team.
import com.rfizzle.prosperity.api.ProsperityAPI;
if (FabricLoader.getInstance().isModLoaded("prosperity")) {
ProsperityAPI.registerPartyGroupProvider(player -> {
// return an opaque key shared by every member of the player's party,
// or null to defer to the next provider / the scoreboard team
return MyPartyMod.partyIdOf(player); // e.g. the party's stable id
});
}
Notes
- The API package is
com.rfizzle.prosperity.api— everything outside it is internal and may change without notice. LootModifierCallbackis a standard FabricEvent, not a custom event bus.LootModifierContextis created fresh per loot generation, populated with the player, position, loot table, and post-distance-scaling values.- Mob loot scaling fires the same event, so listeners registered for container loot affect mob loot too.
Building an integration and something's missing? Open an issue — the API surface grows based on real integration needs.