Skip to content

Biome Modification API - #1097

Merged
modmuss50 merged 9 commits into
FabricMC:1.16from
shartte:biome-modification-api
Oct 30, 2020
Merged

Biome Modification API#1097
modmuss50 merged 9 commits into
FabricMC:1.16from
shartte:biome-modification-api

Conversation

@shartte

@shartte shartte commented Sep 23, 2020

Copy link
Copy Markdown
Contributor

This builds on the previousy biome API PR to add a way for modifying existing Biomes. This is needed since 1.16.2 made Biomes immutable, and introduced several copy-steps which make it harder to simply modify the Biomes in BuiltInBiomes and be done with it.

This API aims to add a more generic way of allowing ordered modifications, while building some heavily used convenience methods on top of that more generic API (i.e. adding features, and structure should be easy, while still allowing more wide-spread mods).

Implementation Notes

  • This will modify only after datapacks are loaded, so no modifications for the demo world or server.properties parsing (I don't think anyone cares)
  • Any method using RegistryKey parameters will work with purely JSON defined worldgen objects too, the test cases test this fully (different PR for the Unit Tests). This means JSON configured features can be added to vanilla biomes
  • Biomes overridden or defined in data packs will run through this system too
  • Any method that's called addBuiltIn*/removeBuiltIn* will use the BuiltInRegistries to resolve a registry key and continue with that, this means that built-in worldgen objects can easily be added to any biome (be it JSON or built-in)
  • Ordering of modifiers is based around both the given order (first), then alphabetically based on the modifier ID (given to BiomeModifications.create), to guarantee consistent feature ordering regardless of mod-loading order
  • The IDs have the additional benefit of allowing other mods like KubeJS or WorldGenDebug to specifically target biome modifiers by ID to disable or modify their order (great for modpacks), or print before/after reports while attributing changes to the actual modifiers that caused them
  • The convenience methods found on BiomeModifications that quickly add modifiers for common use cases will automatically use the ID of the added worldgen objects as the ID of the modifiers they add, which works well for mod-defined worldgen objects (i.e. the example below where "ae2:quartz_ore" is added will use that as the ID for the modifier)

Most important classes:

  • BiomeModifications is the entry point for any form of modification
  • BiomeSelectors contains commonly used selectors such as all biomes in the overworld, all biomes in the nether, all biomes in a given list, etc. (selectors can be combined like standard Java predicates)
  • BiomeModificationContext is the object given to the actual modifier to add/remove/change the biome as needed

Example usage (for the simple ore generation case, with some extra config bonus):

boolean isQuartzOreWorldgenEnabled = /* from mod config file */;
boolean isChargedQuartzOreWorldgenEnabled = /* from mod config file */;
Set<RegistryKey<Biome>> quartzOreBiomeBlacklist = /* from mod config file */;

if (isQuartzOreWorldgenEnabled) {
    Predicate<BiomeSelectionContext> biomeSelector = BiomeSelectors.foundInOverworld()
            .and(BiomeSelectors.excludeByKey(quartzOreBiomeBlacklist)));
    BiomeModifications.addFeature(biomeSelector, GenerationStep.Feature.UNDERGROUND_ORES, WorldGenKeys.QUARTZ_ORE);

    if (isChargedQuartzOreWorldgenEnabled) {
        BiomeModifications.addFeature(biomeSelector, GenerationStep.Feature.UNDERGROUND_DECORATION, WorldGenKeys.CHARGED_QUARTZ_ORE);
    }
}

Example usage (for the simple custom structure case, with some extra config bonus):

boolean isMeteoriteWorldgenEnabled = /* from mod config file */;
Set<RegistryKey<Biome>> meteoriteBiomeBlacklist = /* from mod config file */;

if (isMeteoriteWorldgenEnabled) {
    Predicate<BiomeSelectionContext> biomeSelector = BiomeSelectors.foundInOverworld()
            .and(BiomeSelectors.excludeByKey(meteoriteBiomeBlacklist)));
    BiomeModifications.addStructure(biomeSelector, WorldGenKeys.METEORITE);
}

Example usage (biome-specific mineshafts using biome-based selectors and multiple modifiers):

BiomeModifications.create(new Identifier("mymod:biome_mineshafts"))
        .add(BiomeModifications.ORDER_REPLACEMENTS,
                BiomeSelectors.includeByKey(BiomeKeys.PLAINS, BiomeKeys.SUNFLOWER_PLAINS),
                context -> {
                    if (context.getGenerationSettings().removeStructure(StructureFeature.MINESHAFT)) {
                        context.getGenerationSettings().addStructure(WorldGenKeys.PLAINS_MINESHAFT);
                    }
                })
        .add(BiomeModifications.ORDER_REPLACEMENTS,
                BiomeSelectors.includeByKey(BiomeKeys.JUNGLE, BiomeKeys.JUNGLE_HILLS, BiomeKeys.JUNGLE_EDGE/*, ... more jungles */),
                context -> {
                    if (context.getGenerationSettings().removeStructure(StructureFeature.MINESHAFT)) {
                        context.getGenerationSettings().addStructure(WorldGenKeys.JUNGLE_MINESHAFT);
                    }
                });

Example usage (biome-specific mineshafts using context-sensitive modifier):

BiomeModifications.create(new Identifier("mymod:biome_mineshafts"))
        .add(BiomeModifications.ORDER_REPLACEMENTS,
                context -> context.getBiome().getGenerationSettings().hasStructureFeature(StructureFeature.MINESHAFT),
                (selectionContext, context) -> {
                    RegistryKey<ConfiguredStructureFeature<?, ?>> replacement = REPLACEMENT_BY_BIOME.get(selectionContext.getBiomeKey());
                    
                    if (replacement != null) {
                        context.getGenerationSettings().removeStructure(StructureFeature.MINESHAFT);
                        context.getGenerationSettings().addStructure(replacement);
                    }
                });

Example usage (making an entity spawn in all biomes that also spawn sheep):

BiomeModifications.addSpawn(
        BiomeSelectors.spawnsOneOf(EntityType.SHEEP),
        SpawnGroup.CREATURE,
        MyEntityTypes.SUPER_SHEEP,
        1 /* weight */,
        2 /* minGroupSize */,
        3 /* maxGroupSize */
);

Example usage (replace sheep spawns with custom sheep in all overworld biomes below a given temperature as long as they're not oceans, and inherit their spawn settings - requires a custom accessor to get all properties of existing spawn entries):

BiomeModifications.create(new Identifier("mymod:extra_wooly_sheeps"))
    .add(BiomeModifications.ORDER_REPLACEMENTS,
        BiomeSelectors.foundInOverworld().and(context -> {
            Biome biome = context.getBiome();
            return biome.getCategory() != Biome.Category.OCEAN && biome.getTemperature() < 0.2f
        }),
        (selection, context) -> {
            List<SpawnSettings.SpawnEntry> oldSpawns = selection.getBiome().getSpawnSettings().getSpawnEntry(SpawnGroup.CREATURE);
            for (SpawnSettings.SpawnEntry spawnEntry : oldSpawns) {
                if (spawnEntry.type == EntityType.SHEEP) {
                    context.getSpawnSettings().addSpawn(SpawnGroup.CREATURE, new SpawnSettings.SpawnEntry(
                            MyEntityTypes.EXTRA_WOOLY_SHEEP, /* TODO: get weight from spawn entry */, spawnEntry.minGroupSize,spawnEntry.maxGroupSize
                    ));
                }
            }
            context.getSpawnSettings().removeSpawnsOfEntityType(EntityType.SHEEP);
        });
    }
}

For the curious, the tests are currently here: b02d6a7

@shartte
shartte marked this pull request as ready for review September 25, 2020 21:34
@shartte
shartte requested a review from a team September 25, 2020 23:17
@i509VCB i509VCB added enhancement New feature or request reviews needed This PR needs more reviews labels Sep 25, 2020
@i509VCB i509VCB mentioned this pull request Sep 25, 2020
7 tasks

@ghost ghost left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in terms of actual code it looks go to me. It might be benefited from some additional comments and documentation in some of the mixins, but in the grand scheme of things that's pretty minor.

@shartte

shartte commented Sep 26, 2020

Copy link
Copy Markdown
Contributor Author

@vaerian I tried to address the docs on RegistryOpsMixin (which is arguably the most important one) and on the one that adds the modified biome tracker. The accessors I'd leave undocumented since they're just added getter/setter soup.

@modmuss50
modmuss50 self-requested a review September 27, 2020 20:08

OverworldBiomes.addContinentalBiome(BiomeKeys.END_HIGHLANDS, OverworldClimate.DRY, 0.5);

BiomeModifications.create(new Identifier("fabric:test_mod"))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expand the tests a bit, I would atleast add an ore or something as well.

@shartte shartte Oct 1, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see the unit tests I linked in the PR description, I don't want to duplicate those in the test mod until it's clear whether they'll go through or not.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are awesome, they would be really great to have in. They required a loader change didnt they?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit contains more a "proof of concept" for how to do a loader-change, by placing a class in the loader-package to get around the package-visible accessibility of some loader classes. It obviously can't stay like that. Maybe we can convince player that it is worth supporting, then I'd try to make a loader PR (it shouldn't really be too hard, essentially we'd need a third KnotLauncher that doesnt invoke any entrypoints and uses more or less a dummy GameProvider, which is what that proof of concept does).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do still think there should be few more examples/tests here, atleast add an ore or something.

@modmuss50 modmuss50 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking really solid, would be nice to have a few more tests in the test mod nothing great is required. We can look into getting the full test suite in later?


OverworldBiomes.addContinentalBiome(BiomeKeys.END_HIGHLANDS, OverworldClimate.DRY, 0.5);

BiomeModifications.create(new Identifier("fabric:test_mod"))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do still think there should be few more examples/tests here, atleast add an ore or something.

@shartte

shartte commented Oct 4, 2020

Copy link
Copy Markdown
Contributor Author

Removed the exposed integer-based ordering and instead used phases to represent the constants that were on the api class before.

@shartte
shartte requested a review from modmuss50 October 7, 2020 18:23
@Stuff-Stuffs

Copy link
Copy Markdown
Contributor

Does not work for StructureFeature, as they need to have a StructureConfig added to their ChunkGenerators StructuresConfig, else the call to structuresConfig.getForType returns null and the StructureFeature is skipped.

@TelepathicGrunt

Copy link
Copy Markdown
Contributor

@Stuff-Stuffs Did you register you structures using fabric api's structure api? It should add the structure configs to a special map that all chunk generators should then inherit from

@Stuff-Stuffs

Copy link
Copy Markdown
Contributor

I did, however my problems have dissipated since invalidating caches, problem fixed. I guess the caching messed something up.

@modmuss50 modmuss50 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good now, still wondering if it would be a good idea to have a BiomeSelector that uses Biome.Category ?

florensie added a commit to florensie/artifacts-fabric that referenced this pull request Oct 23, 2020
@MarcusElg

Copy link
Copy Markdown

I really hope this can be merged soon...

@TelepathicGrunt

Copy link
Copy Markdown
Contributor

Agreed MCrafterzz. I really want to drop the current system I have for adding to biomes. And having this PR means I can make a structure tutorial for Fabric without needing hacks that could cause issues or have downsides

@Player3324 Player3324 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks! I believe the biome selector thing is worth doing after all..

@Player3324 Player3324 added status: last call If you care, make yourself heard right away! and removed reviews needed This PR needs more reviews labels Oct 26, 2020
@modmuss50
modmuss50 merged commit f5a9be8 into FabricMC:1.16 Oct 30, 2020
ThalusA pushed a commit to ThalusA/fabric that referenced this pull request May 31, 2022
* Biome Modification API

* Improved docs on Mixins.

* Added convenience methods to select biomes by mob-spawns, and added a top-level convenience method to add new spawns.

* Checkstyle fixes.

* Replace a reference to DRM with DynamicRegistryManager

* Replaced integer order with phase-based ordering.

* Changed to @deprecated

* Checkstyle fix

* Added category selector.
@SuperPlantVoiderOriginal

Copy link
Copy Markdown

BUT HOW DO WE ACTUALLY USE IT

@TelepathicGrunt

Copy link
Copy Markdown
Contributor

@SuperPlantVoiderOriginal Fabric's APIs generally have test mods on the GitHub repo that shows an example usage of the API. For the Biome Modification API, here's the test mod for that which you can look at for an example. Note, this API is only available through code so there is no way to access this modification through JSON files only unlike a few other APIs.
https://github.com/FabricMC/fabric/blob/3f301502811f0ab410f045d065b6f98d5b630537/fabric-biome-api-v1/src/testmod/java/net/fabricmc/fabric/test/biome/FabricBiomeTest.java#L77-L98

If you have further questions, please go onto the Fabric Discord and ask there as someone will be willing to help you out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request status: last call If you care, make yourself heard right away!

Projects

None yet

Development

Successfully merging this pull request may close these issues.