1.18.2-pre1 Biome API ()

* Fix BiomeSelectors.tag

* Update fabric-biome-api-v1/src/main/java/net/fabricmc/fabric/api/biome/v1/BiomeSelectionContext.java

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>

* Checkstyle

* Port Biome API

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>
This commit is contained in:
modmuss50 2022-02-21 11:58:35 +00:00 committed by GitHub
parent d1027f7d5e
commit 58afd26713
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 38 additions and 279 deletions

View file

@ -21,7 +21,6 @@ import java.util.function.BiPredicate;
import org.jetbrains.annotations.NotNull;
import net.minecraft.world.gen.feature.PlacedFeature;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.sound.BiomeAdditionsSound;
@ -36,8 +35,7 @@ import net.minecraft.world.biome.BiomeParticleConfig;
import net.minecraft.world.biome.SpawnSettings;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.carver.ConfiguredCarver;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.StructureFeature;
import net.minecraft.world.gen.feature.PlacedFeature;
import net.fabricmc.fabric.impl.biome.modification.BuiltInRegistryKeys;
@ -393,67 +391,6 @@ public interface BiomeModificationContext {
default boolean removeBuiltInCarver(GenerationStep.Carver step, ConfiguredCarver<?> configuredCarver) {
return removeCarver(step, BuiltInRegistryKeys.get(configuredCarver));
}
/**
* Allows a configured structure to start in this biome.
*
* <p>Structures added this way may start in this biome, with respect to the {@link net.minecraft.world.gen.chunk.StructureConfig}
* set in the {@link net.minecraft.world.gen.chunk.ChunkGenerator}, but structure pieces can always generate in chunks adjacent
* to a started structure, regardless of biome.
*
* <p>Configured structures that have the same underlying {@link StructureFeature} as the given structure will be removed before
* adding the new structure, since only one of them could actually generate.
*
* @see net.minecraft.world.biome.GenerationSettings.Builder#feature(GenerationStep.Feature, PlacedFeature)
*/
void addStructure(RegistryKey<ConfiguredStructureFeature<?, ?>> configuredStructureKey);
/**
* Allows a configured structure from {@link BuiltinRegistries#CONFIGURED_STRUCTURE_FEATURE} to start in this biome.
*
* <p>This method is intended for use with the configured structures found in {@link net.minecraft.world.gen.feature.ConfiguredStructureFeatures}.
*
* <p><b>NOTE:</b> In case the configured structure is overridden using a datapack, the definition from the datapack
* will be added to the biome.
*
* <p>Structures added this way may start in this biome, with respect to the {@link net.minecraft.world.gen.chunk.StructureConfig}
* set in the {@link net.minecraft.world.gen.chunk.ChunkGenerator}, but structure pieces can always generate in chunks adjacent
* to a started structure, regardless of biome.
*
* <p>Configured structures that have the same underlying {@link StructureFeature} as the given structure will be removed before
* adding the new structure, since only one of them could actually generate.
*/
default void addBuiltInStructure(ConfiguredStructureFeature<?, ?> configuredStructure) {
addStructure(BuiltInRegistryKeys.get(configuredStructure));
}
/**
* Removes a configured structure from the structures that are allowed to start in this biome.
*
* <p>Please see the note on {@link #addStructure(RegistryKey)} about structures pieces still generating
* if adjacent biomes allow the structure to start.
*
* @return True if any structures were removed.
*/
boolean removeStructure(RegistryKey<ConfiguredStructureFeature<?, ?>> configuredStructureKey);
/**
* Removes a structure from the structures that are allowed to start in this biome.
*
* <p>This will remove all configured variations of the given structure from this biome.
*
* <p>This can be used with modded structures or Vanilla structures from {@link net.minecraft.world.gen.feature.StructureFeature}.
*
* @return True if any structures were removed.
*/
boolean removeStructure(StructureFeature<?> structure);
/**
* {@link #removeStructure(RegistryKey)} for built-in structures (see {@link #addBuiltInStructure(ConfiguredStructureFeature)}).
*/
default boolean removeBuiltInStructure(ConfiguredStructureFeature<?, ?> configuredStructure) {
return removeStructure(BuiltInRegistryKeys.get(configuredStructure));
}
}
interface SpawnSettingsContext {

View file

@ -28,7 +28,6 @@ import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.SpawnSettings;
import net.minecraft.world.gen.GenerationStep;
import net.minecraft.world.gen.carver.ConfiguredCarver;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.PlacedFeature;
/**
@ -51,17 +50,6 @@ public final class BiomeModifications {
});
}
/**
* Convenience method to add a structure to one or more biomes.
*
* @see BiomeSelectors
*/
public static void addStructure(Predicate<BiomeSelectionContext> biomeSelector, RegistryKey<ConfiguredStructureFeature<?, ?>> configuredStructureKey) {
create(configuredStructureKey.getValue()).add(ModificationPhase.ADDITIONS, biomeSelector, context -> {
context.getGenerationSettings().addStructure(configuredStructureKey);
});
}
/**
* Convenience method to add a carver to one or more biomes.
*

View file

@ -19,6 +19,7 @@ package net.fabricmc.fabric.api.biome.v1;
import java.util.List;
import java.util.Optional;
import net.minecraft.tag.TagKey;
import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.util.registry.RegistryEntryList;
import net.minecraft.util.registry.RegistryKey;
@ -41,6 +42,8 @@ public interface BiomeSelectionContext {
*/
Biome getBiome();
RegistryEntry<Biome> getBiomeRegistryEntry();
/**
* Returns true if this biome has the given configured feature, which must be registered
* in the {@link net.minecraft.util.registry.BuiltinRegistries}.
@ -118,16 +121,16 @@ public interface BiomeSelectionContext {
*
* <p>This method is intended for use with the Vanilla configured structures found in {@link net.minecraft.world.gen.feature.ConfiguredStructureFeatures}.
*/
default boolean hasBuiltInStructure(ConfiguredStructureFeature<?, ?> configuredStructure) {
default boolean validForBuiltInStructure(ConfiguredStructureFeature<?, ?> configuredStructure) {
RegistryKey<ConfiguredStructureFeature<?, ?>> key = BuiltInRegistryKeys.get(configuredStructure);
return hasStructure(key);
return validForStructure(key);
}
/**
* Returns true if the configured structure with the given key can start in this biome in any chunk generator
* used by the current world-save.
*/
boolean hasStructure(RegistryKey<ConfiguredStructureFeature<?, ?>> key);
boolean validForStructure(RegistryKey<ConfiguredStructureFeature<?, ?>> key);
/**
* Tries to retrieve the registry key for the given configured feature, which should be from this biomes
@ -143,4 +146,9 @@ public interface BiomeSelectionContext {
* <p>If no dimension options exist for the given dimension key, <code>false</code> is returned.
*/
boolean canGenerateIn(RegistryKey<DimensionOptions> dimensionKey);
/**
* {@return true if this biome is in the given {@link TagKey}}.
*/
boolean hasTag(TagKey<Biome> tag);
}

View file

@ -99,7 +99,7 @@ public final class BiomeSelectors {
* @see net.fabricmc.fabric.api.tag.TagFactory#BIOME
*/
public static Predicate<BiomeSelectionContext> tag(TagKey<Biome> tag) {
return context -> BuiltinRegistries.BIOME.containsTag(tag);
return context -> context.hasTag(tag);
}
/**

View file

@ -60,7 +60,7 @@ public final class NetherBiomeData {
if (NETHER_BIOMES.isEmpty()) {
MultiNoiseBiomeSource source = MultiNoiseBiomeSource.Preset.NETHER.getBiomeSource(BuiltinRegistries.BIOME);
for (RegistryEntry<Biome> entry : source.getBiomes().toList()) {
for (RegistryEntry<Biome> entry : source.getBiomes()) {
BuiltinRegistries.BIOME.getKey(entry.value()).ifPresent(NETHER_BIOMES::add);
}
}

View file

@ -55,7 +55,6 @@ import net.minecraft.world.gen.carver.ConfiguredCarver;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.PlacedFeature;
import net.minecraft.world.gen.feature.StructureFeature;
import net.fabricmc.fabric.api.biome.v1.BiomeModificationContext;
@ -322,25 +321,6 @@ public class BiomeModificationContextImpl implements BiomeModificationContext {
return false;
}
@Override
public void addStructure(RegistryKey<ConfiguredStructureFeature<?, ?>> configuredStructureKey) {
ConfiguredStructureFeature<?, ?> configuredStructure = structures.getOrThrow(configuredStructureKey);
BiomeStructureStartsImpl.addStart(registries, configuredStructure, biomeKey);
}
@Override
public boolean removeStructure(RegistryKey<ConfiguredStructureFeature<?, ?>> configuredStructureKey) {
ConfiguredStructureFeature<?, ?> configuredStructure = structures.getOrThrow(configuredStructureKey);
return BiomeStructureStartsImpl.removeStart(registries, configuredStructure, biomeKey);
}
@Override
public boolean removeStructure(StructureFeature<?> structure) {
return BiomeStructureStartsImpl.removeStructureStarts(registries, structure, biomeKey);
}
private <T> RegistryEntryList<T> plus(RegistryEntryList<T> values, RegistryEntry<T> entry) {
List<RegistryEntry<T>> list = new ArrayList<>(values.stream().toList());
list.add(entry);

View file

@ -120,7 +120,7 @@ public class BiomeModificationImpl {
// Build a list of all biome keys in ascending order of their raw-id to get a consistent result in case
// someone does something stupid.
List<RegistryKey<Biome>> keys = biomes.getEntries().stream()
List<RegistryKey<Biome>> keys = biomes.getEntrySet().stream()
.map(Map.Entry::getKey)
.sorted(Comparator.comparingInt(key -> biomes.getRawId(biomes.getOrThrow(key))))
.toList();
@ -170,7 +170,7 @@ public class BiomeModificationImpl {
BiomeSource biomeSource = dimension.getChunkGenerator().getBiomeSource();
// Replace the Supplier to force it to rebuild on next call
biomeSource.field_34469 = Suppliers.memoize(() -> {
biomeSource.indexedFeaturesSupplier = Suppliers.memoize(() -> {
return biomeSource.method_39525(biomeSource.biomes.stream().map(RegistryEntry::value).toList(), true);
});
}

View file

@ -20,12 +20,13 @@ import java.util.Optional;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.tag.TagKey;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryEntry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.gen.chunk.placement.StructuresConfig;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.PlacedFeature;
@ -39,12 +40,14 @@ public class BiomeSelectionContextImpl implements BiomeSelectionContext {
private final LevelProperties levelProperties;
private final RegistryKey<Biome> key;
private final Biome biome;
private final RegistryEntry<Biome> entry;
public BiomeSelectionContextImpl(DynamicRegistryManager dynamicRegistries, LevelProperties levelProperties, RegistryKey<Biome> key, Biome biome) {
this.dynamicRegistries = dynamicRegistries;
this.levelProperties = levelProperties;
this.key = key;
this.biome = biome;
this.entry = dynamicRegistries.get(Registry.BIOME_KEY).getEntry(this.key).orElseThrow();
}
@Override
@ -57,6 +60,11 @@ public class BiomeSelectionContextImpl implements BiomeSelectionContext {
return biome;
}
@Override
public RegistryEntry<Biome> getBiomeRegistryEntry() {
return entry;
}
@Override
public Optional<RegistryKey<ConfiguredFeature<?, ?>>> getFeatureKey(ConfiguredFeature<?, ?> configuredFeature) {
Registry<ConfiguredFeature<?, ?>> registry = dynamicRegistries.get(Registry.CONFIGURED_FEATURE_KEY);
@ -70,24 +78,14 @@ public class BiomeSelectionContextImpl implements BiomeSelectionContext {
}
@Override
public boolean hasStructure(RegistryKey<ConfiguredStructureFeature<?, ?>> key) {
public boolean validForStructure(RegistryKey<ConfiguredStructureFeature<?, ?>> key) {
ConfiguredStructureFeature<?, ?> instance = dynamicRegistries.get(Registry.CONFIGURED_STRUCTURE_FEATURE_KEY).get(key);
if (instance == null) {
return false;
}
// Since the biome->structure mapping is now stored in the chunk generator configuration, we check every
// chunk generator used by the current world-save.
for (DimensionOptions dimension : levelProperties.getGeneratorOptions().getDimensions()) {
StructuresConfig structuresConfig = dimension.getChunkGenerator().getStructuresConfig();
if (structuresConfig.getConfiguredStructureFeature(instance.feature).get(key).contains(getBiomeKey())) {
return true;
}
}
return false;
return instance.getBiomes().contains(getBiomeRegistryEntry());
}
@Override
@ -104,6 +102,12 @@ public class BiomeSelectionContextImpl implements BiomeSelectionContext {
return false;
}
return dimension.getChunkGenerator().getBiomeSource().getBiomes().anyMatch(entry -> entry.value() == biome);
return dimension.getChunkGenerator().getBiomeSource().getBiomes().stream().anyMatch(entry -> entry.value() == biome);
}
@Override
public boolean hasTag(TagKey<Biome> tag) {
Registry<Biome> biomeRegistry = dynamicRegistries.get(Registry.BIOME_KEY);
return biomeRegistry.entryOf(getBiomeKey()).isIn(tag);
}
}

View file

@ -1,150 +0,0 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.biome.modification;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.util.registry.BuiltinRegistries;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.StructureFeature;
/**
* Helps with modifying the structure start information stored in {@link ChunkGeneratorSettings}.
*/
@ApiStatus.Internal
public final class BiomeStructureStartsImpl {
private BiomeStructureStartsImpl() {
}
public static void addStart(
DynamicRegistryManager registries,
ConfiguredStructureFeature<?, ?> configuredStructure,
RegistryKey<Biome> biome
) {
changeStructureStarts(registries, structureMap -> {
Multimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>> configuredMap = structureMap.computeIfAbsent(configuredStructure.feature, k -> HashMultimap.create());
// This is tricky, the keys might be either from builtin (Vanilla) or dynamic registries (modded)
RegistryKey<ConfiguredStructureFeature<?, ?>> configuredStructureKey = registries.get(Registry.CONFIGURED_STRUCTURE_FEATURE_KEY).getKey(configuredStructure).orElseThrow();
RegistryKey<ConfiguredStructureFeature<?, ?>> mapKey = registries.get(Registry.CONFIGURED_STRUCTURE_FEATURE_KEY).getKey(configuredStructure)
.orElse(configuredStructureKey);
// orElse means the configured structure passed in is not a (potentially modified) Vanilla entry,
// but rather a modded one. In this case, this will create a new entry in the map.
configuredMap.put(mapKey, biome);
});
}
public static boolean removeStart(
DynamicRegistryManager registries,
ConfiguredStructureFeature<?, ?> configuredStructure,
RegistryKey<Biome> biome
) {
AtomicBoolean result = new AtomicBoolean(false);
changeStructureStarts(registries, structureMap -> {
Multimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>> configuredMap = structureMap.get(configuredStructure.feature);
if (configuredMap == null) {
return;
}
// This is tricky, the keys might be either from builtin (Vanilla) or dynamic registries (modded)
RegistryKey<ConfiguredStructureFeature<?, ?>> configuredStructureKey = registries.get(Registry.CONFIGURED_STRUCTURE_FEATURE_KEY).getKey(configuredStructure).orElseThrow();
ConfiguredStructureFeature<?, ?> mapKey = BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE.get(configuredStructureKey);
if (mapKey == null) {
// This means the configured structure passed in is not a (potentially modified) Vanilla entry,
// but rather a modded one. In this case, this will create a new entry in the map.
mapKey = configuredStructure;
}
if (configuredMap.remove(mapKey, biome)) {
result.set(true);
}
});
return result.get();
}
public static boolean removeStructureStarts(
DynamicRegistryManager registries,
StructureFeature<?> structure,
RegistryKey<Biome> biome
) {
AtomicBoolean result = new AtomicBoolean(false);
changeStructureStarts(registries, structureMap -> {
Multimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>> configuredMap = structureMap.get(structure);
if (configuredMap == null) {
return;
}
if (configuredMap.values().remove(biome)) {
result.set(true);
}
});
return result.get();
}
private static void changeStructureStarts(DynamicRegistryManager registries, Consumer<Map<StructureFeature<?>, Multimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>>>> modifier) {
Registry<ChunkGeneratorSettings> chunkGenSettingsRegistry = registries.get(Registry.CHUNK_GENERATOR_SETTINGS_KEY);
for (Map.Entry<RegistryKey<ChunkGeneratorSettings>, ChunkGeneratorSettings> entry : chunkGenSettingsRegistry.getEntries()) {
Map<StructureFeature<?>, Multimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>>> structureMap = unfreeze(entry.getValue());
modifier.accept(structureMap);
freeze(entry.getValue(), structureMap);
}
}
private static Map<StructureFeature<?>, Multimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>>> unfreeze(ChunkGeneratorSettings settings) {
ImmutableMap<StructureFeature<?>, ImmutableMultimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>>> frozenMap = settings.getStructuresConfig().configuredStructures;
Map<StructureFeature<?>, Multimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>>> result = new HashMap<>(frozenMap.size());
for (Map.Entry<StructureFeature<?>, ImmutableMultimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>>> entry : frozenMap.entrySet()) {
result.put(entry.getKey(), HashMultimap.create(entry.getValue()));
}
return result;
}
private static void freeze(ChunkGeneratorSettings settings, Map<StructureFeature<?>, Multimap<RegistryKey<ConfiguredStructureFeature<?, ?>>, RegistryKey<Biome>>> structureStarts) {
settings.getStructuresConfig().configuredStructures = structureStarts.entrySet().stream()
.collect(ImmutableMap.toImmutableMap(
Map.Entry::getKey,
e -> ImmutableMultimap.copyOf(e.getValue())
));
}
}

View file

@ -4,8 +4,8 @@ accessible class net/minecraft/world/biome/Biome$Weather
# Rebuilding biome source feature lists
accessible method net/minecraft/world/biome/source/BiomeSource method_39525 (Ljava/util/List;Z)Ljava/util/List;
accessible field net/minecraft/world/biome/source/BiomeSource biomes Ljava/util/Set;
accessible field net/minecraft/world/biome/source/BiomeSource field_34469 Ljava/util/function/Supplier;
mutable field net/minecraft/world/biome/source/BiomeSource field_34469 Ljava/util/function/Supplier;
accessible field net/minecraft/world/biome/source/BiomeSource indexedFeaturesSupplier Ljava/util/function/Supplier;
mutable field net/minecraft/world/biome/source/BiomeSource indexedFeaturesSupplier Ljava/util/function/Supplier;
# Top-Level Biome Fields Access
accessible field net/minecraft/world/biome/Biome weather Lnet/minecraft/world/biome/Biome$Weather;

View file

@ -83,14 +83,6 @@ public class FabricBiomeTest implements ModInitializer {
Registry.register(BuiltinRegistries.BIOME, TEST_END_MIDLANDS.getValue(), createEndMidlands());
Registry.register(BuiltinRegistries.BIOME, TEST_END_BARRRENS.getValue(), createEndBarrens());
BiomeModifications.create(new Identifier("fabric:end_test_structures"))
.add(ModificationPhase.ADDITIONS, BiomeSelectors.includeByKey(TEST_END_HIGHLANDS, TEST_END_MIDLANDS), ctx -> {
ctx.getGenerationSettings().addStructure(RegistryKey.of(Registry.CONFIGURED_STRUCTURE_FEATURE_KEY, new Identifier("end_city")));
})
.add(ModificationPhase.ADDITIONS, BiomeSelectors.includeByKey(BiomeKeys.PLAINS), ctx -> {
ctx.getGenerationSettings().addStructure(RegistryKey.of(Registry.CONFIGURED_STRUCTURE_FEATURE_KEY, new Identifier("end_city")));
});
// TESTING HINT: to get to the end:
// /execute in minecraft:the_end run tp @s 0 90 0
TheEndBiomes.addHighlandsBiome(TEST_END_HIGHLANDS, 5.0);
@ -117,7 +109,7 @@ public class FabricBiomeTest implements ModInitializer {
);
})
.add(ModificationPhase.ADDITIONS,
BiomeSelectors.tag(TagKey.intern(Registry.BIOME_KEY, new Identifier(MOD_ID, "tag_selector_test"))),
BiomeSelectors.tag(TagKey.of(Registry.BIOME_KEY, new Identifier(MOD_ID, "tag_selector_test"))),
context -> context.getEffects().setSkyColor(0x770000));
}

View file

@ -14,7 +14,7 @@ rootProject.name = "fabric-api"
include 'fabric-api-base'
include 'fabric-api-lookup-api-v1'
//include 'fabric-biome-api-v1'
include 'fabric-biome-api-v1'
include 'fabric-blockrenderlayer-v1'
include 'fabric-commands-v0'
include 'fabric-command-api-v1'