Merge branch '1.19' into 1.19.3

# Conflicts:
#	gradle.properties
#	src/main/java/org/betterx/bclib/BCLib.java
#	src/main/java/org/betterx/bclib/api/v2/generator/BCLChunkGenerator.java
#	src/main/java/org/betterx/worlds/together/mixin/common/WorldPresetsBootstrapMixin.java
#	src/main/java/org/betterx/worlds/together/worldPreset/WorldPresets.java
#	src/main/resources/bclib.accesswidener
This commit is contained in:
Frank 2022-11-29 09:41:10 +01:00
commit 0dcb7809b8
41 changed files with 1586 additions and 75 deletions

View file

@ -15,6 +15,7 @@ import org.betterx.bclib.api.v2.levelgen.surface.rules.Conditions;
import org.betterx.bclib.api.v2.poi.PoiManager;
import org.betterx.bclib.api.v3.levelgen.features.blockpredicates.BlockPredicates;
import org.betterx.bclib.api.v3.levelgen.features.placement.PlacementModifiers;
import org.betterx.bclib.api.v3.tag.BCLBlockTags;
import org.betterx.bclib.commands.CommandRegistry;
import org.betterx.bclib.config.Configs;
import org.betterx.bclib.networking.VersionChecker;
@ -139,6 +140,7 @@ public class BCLib implements ModInitializer {
AnvilRecipe.register();
Conditions.registerAll();
CommandRegistry.register();
BCLBlockTags.ensureStaticallyLoaded();
PoiManager.registerAll();
DataExchangeAPI.registerDescriptors(List.of(

View file

@ -25,6 +25,7 @@ public class BonemealAPI {
private static final Set<Block> TERRAIN_TO_SPREAD = Sets.newHashSet();
private static final Set<Block> TERRAIN = Sets.newHashSet();
@Deprecated(forRemoval = true)
public static void addSpreadableBlock(Block spreadableBlock, Block surfaceForSpread) {
SPREADABLE_BLOCKS.put(spreadableBlock, surfaceForSpread);
TERRAIN_TO_SPREAD.add(surfaceForSpread);
@ -35,38 +36,53 @@ public class BonemealAPI {
return TERRAIN.contains(block);
}
@Deprecated(forRemoval = true)
public static boolean isSpreadableTerrain(Block block) {
return TERRAIN_TO_SPREAD.contains(block);
}
@Deprecated(forRemoval = true)
public static Block getSpreadable(Block block) {
return SPREADABLE_BLOCKS.get(block);
}
@Deprecated(forRemoval = true)
public static void addLandGrass(Block plant, Block... terrain) {
addLandGrass(makeConsumer(plant), terrain);
}
@Deprecated(forRemoval = true)
public static void addLandGrass(BiConsumer<Level, BlockPos> plant, Block... terrain) {
for (Block block : terrain) {
addLandGrass(plant, block, 1F);
}
}
/**
* This API is deprecated, please use the new {@link org.betterx.bclib.api.v3.bonemeal.BonemealAPI}.
*
* @param biome
* @param plant
* @param terrain
*/
@Deprecated(forRemoval = true)
public static void addLandGrass(ResourceLocation biome, Block plant, Block... terrain) {
addLandGrass(biome, makeConsumer(plant), terrain);
}
@Deprecated(forRemoval = true)
public static void addLandGrass(ResourceLocation biome, BiConsumer<Level, BlockPos> plant, Block... terrain) {
for (Block block : terrain) {
addLandGrass(biome, plant, block, 1F);
}
}
@Deprecated(forRemoval = true)
public static void addLandGrass(Block plant, Block terrain, float chance) {
addLandGrass(makeConsumer(plant), terrain, chance);
}
@Deprecated(forRemoval = true)
public static void addLandGrass(BiConsumer<Level, BlockPos> plant, Block terrain, float chance) {
WeightedList<BiConsumer<Level, BlockPos>> list = LAND_GRASS_TYPES.get(terrain);
if (list == null) {
@ -77,10 +93,12 @@ public class BonemealAPI {
list.add(plant, chance);
}
@Deprecated(forRemoval = true)
public static void addLandGrass(ResourceLocation biome, Block plant, Block terrain, float chance) {
addLandGrass(biome, makeConsumer(plant), terrain, chance);
}
@Deprecated(forRemoval = true)
public static void addLandGrass(
ResourceLocation biome,
BiConsumer<Level, BlockPos> plant,
@ -159,6 +177,7 @@ public class BonemealAPI {
list.add(plant, chance);
}
@Deprecated(forRemoval = true)
public static BiConsumer<Level, BlockPos> getLandGrass(
ResourceLocation biomeID,
Block terrain,

View file

@ -17,6 +17,8 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.data.worldgen.SurfaceRuleData;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
@ -24,12 +26,11 @@ import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeGenerationSettings;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.biome.FeatureSorter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.*;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.synth.NormalNoise;
@ -62,6 +63,12 @@ public class BCLChunkGenerator extends NoiseBasedChunkGenerator implements Resto
.and(builderInstance.group(noiseGetter, biomeSourceCodec, settingsCodec))
.apply(builderInstance, builderInstance.stable(BCLChunkGenerator::new));
});
protected static final NoiseSettings NETHER_NOISE_SETTINGS_AMPLIFIED = NoiseSettings.create(0, 256, 1, 4);
public static final ResourceKey<NoiseGeneratorSettings> AMPLIFIED_NETHER = ResourceKey.create(
Registry.NOISE_GENERATOR_SETTINGS_REGISTRY,
BCLib.makeID("amplified_nether")
);
public final BiomeSource initialBiomeSource;
public BCLChunkGenerator(
@ -225,4 +232,24 @@ public class BCLChunkGenerator extends NoiseBasedChunkGenerator implements Resto
//
// return Holder.direct(noise);
}
public static NoiseGeneratorSettings amplifiedNether() {
return new NoiseGeneratorSettings(
NETHER_NOISE_SETTINGS_AMPLIFIED,
Blocks.NETHERRACK.defaultBlockState(),
Blocks.LAVA.defaultBlockState(),
NoiseRouterData.noNewCaves(
BuiltinRegistries.DENSITY_FUNCTION,
NoiseRouterData.slideNetherLike(BuiltinRegistries.DENSITY_FUNCTION, 0, 256)
),
SurfaceRuleData.nether(),
List.of(),
32,
false,
false,
false,
true
);
}
}

View file

@ -45,6 +45,28 @@ public class BCLEndBiomeSourceConfig implements BiomeSourceConfig<BCLibEndBiomeS
MINECRAFT_17.landBiomesSize,
MINECRAFT_17.barrensBiomesSize
);
public static final BCLEndBiomeSourceConfig MINECRAFT_18_LARGE = new BCLEndBiomeSourceConfig(
EndBiomeMapType.HEX,
BCLib.RUNS_NULLSCAPE ? EndBiomeGeneratorType.VANILLA : EndBiomeGeneratorType.PAULEVS,
BCLib.RUNS_NULLSCAPE ? false : true,
MINECRAFT_18.innerVoidRadiusSquared,
MINECRAFT_18.centerBiomesSize,
MINECRAFT_18.voidBiomesSize * 2,
MINECRAFT_18.landBiomesSize * 4,
MINECRAFT_18.barrensBiomesSize * 2
);
public static final BCLEndBiomeSourceConfig MINECRAFT_18_AMPLIFIED = new BCLEndBiomeSourceConfig(
EndBiomeMapType.HEX,
EndBiomeGeneratorType.PAULEVS,
true,
MINECRAFT_18.innerVoidRadiusSquared,
MINECRAFT_18.centerBiomesSize,
MINECRAFT_18.voidBiomesSize,
MINECRAFT_18.landBiomesSize,
MINECRAFT_18.barrensBiomesSize
);
public static final BCLEndBiomeSourceConfig DEFAULT = MINECRAFT_18;
public static final Codec<BCLEndBiomeSourceConfig> CODEC = RecordCodecBuilder.create(instance -> instance

View file

@ -18,20 +18,40 @@ public class BCLNetherBiomeSourceConfig implements BiomeSourceConfig<BCLibNether
NetherBiomeMapType.VANILLA,
256,
86,
false,
false
);
public static final BCLNetherBiomeSourceConfig MINECRAFT_17 = new BCLNetherBiomeSourceConfig(
NetherBiomeMapType.SQUARE,
256,
86,
true
true,
false
);
public static final BCLNetherBiomeSourceConfig MINECRAFT_18 = new BCLNetherBiomeSourceConfig(
NetherBiomeMapType.HEX,
MINECRAFT_17.biomeSize,
MINECRAFT_17.biomeSizeVertical,
MINECRAFT_17.useVerticalBiomes
MINECRAFT_17.useVerticalBiomes,
MINECRAFT_17.amplified
);
public static final BCLNetherBiomeSourceConfig MINECRAFT_18_LARGE = new BCLNetherBiomeSourceConfig(
NetherBiomeMapType.HEX,
MINECRAFT_18.biomeSize * 4,
MINECRAFT_18.biomeSizeVertical * 2,
MINECRAFT_18.useVerticalBiomes,
MINECRAFT_17.amplified
);
public static final BCLNetherBiomeSourceConfig MINECRAFT_18_AMPLIFIED = new BCLNetherBiomeSourceConfig(
NetherBiomeMapType.HEX,
MINECRAFT_18.biomeSize,
128,
true,
true
);
public static final BCLNetherBiomeSourceConfig DEFAULT = MINECRAFT_18;
public static final Codec<BCLNetherBiomeSourceConfig> CODEC = RecordCodecBuilder.create(instance -> instance
@ -46,7 +66,10 @@ public class BCLNetherBiomeSourceConfig implements BiomeSourceConfig<BCLibNether
.forGetter(o -> o.biomeSizeVertical),
Codec.BOOL.fieldOf("use_vertical_biomes")
.orElse(DEFAULT.useVerticalBiomes)
.forGetter(o -> o.useVerticalBiomes)
.forGetter(o -> o.useVerticalBiomes),
Codec.BOOL.fieldOf("amplified")
.orElse(DEFAULT.amplified)
.forGetter(o -> o.amplified)
)
.apply(instance, BCLNetherBiomeSourceConfig::new));
public final @NotNull NetherBiomeMapType mapVersion;
@ -54,23 +77,30 @@ public class BCLNetherBiomeSourceConfig implements BiomeSourceConfig<BCLibNether
public final int biomeSizeVertical;
public final boolean useVerticalBiomes;
public final boolean amplified;
public BCLNetherBiomeSourceConfig(
@NotNull NetherBiomeMapType mapVersion,
int biomeSize,
int biomeSizeVertical,
boolean useVerticalBiomes
boolean useVerticalBiomes,
boolean amplified
) {
this.mapVersion = mapVersion;
this.biomeSize = Mth.clamp(biomeSize, 1, 8192);
this.biomeSizeVertical = Mth.clamp(biomeSizeVertical, 1, 8192);
this.useVerticalBiomes = useVerticalBiomes;
this.amplified = amplified;
}
@Override
public String toString() {
return "BCLibNetherBiomeSourceConfig{" +
"mapVersion=" + mapVersion +
", useVerticalBiomes=" + useVerticalBiomes +
", amplified=" + amplified +
", biomeSize=" + biomeSize +
", biomeSizeVertical=" + biomeSizeVertical +
'}';
}

View file

@ -0,0 +1,76 @@
package org.betterx.bclib.api.v3.bonemeal;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.util.random.SimpleWeightedRandomList;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.stateproviders.WeightedStateProvider;
import java.util.HashMap;
import java.util.Map;
abstract class BlockSpreader implements BonemealBlockSpreader {
protected abstract boolean isValidSource(BlockState state);
protected abstract boolean hasCustomBehaviour(BlockState state);
public boolean isValidBonemealSpreadTarget(
BlockGetter blockGetter,
BlockPos blockPos,
BlockState blockState,
boolean bl
) {
if (!blockGetter.getBlockState(blockPos.above()).propagatesSkylightDown(blockGetter, blockPos)) {
return false;
} else {
for (BlockPos testPos : BlockPos.betweenClosed(
blockPos.offset(-1, -1, -1),
blockPos.offset(1, 1, 1)
)) {
BlockState state = blockGetter.getBlockState(testPos);
if (isValidSource(state))
if (hasCustomBehaviour(state))
return true;
}
return false;
}
}
public boolean performBonemealSpread(
ServerLevel serverLevel,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
final Map<BlockState, Integer> sourceBlocks = new HashMap<>();
for (BlockPos testPos : BlockPos.betweenClosed(
blockPos.offset(-1, -1, -1),
blockPos.offset(1, 1, 1)
)) {
BlockState state = serverLevel.getBlockState(testPos);
if (isValidSource(state)) {
sourceBlocks.compute(state, (k, v) -> {
if (v == null) return 1;
return v + 1;
});
}
}
SimpleWeightedRandomList.Builder<BlockState> builder = new SimpleWeightedRandomList.Builder<>();
for (Map.Entry<BlockState, Integer> e : sourceBlocks.entrySet()) {
builder.add(e.getKey(), e.getValue());
}
WeightedStateProvider provider = new WeightedStateProvider(builder.build());
BlockState bl = provider.getState(randomSource, blockPos);
if (bl != null) {
serverLevel.setBlock(blockPos, bl, 3);
return true;
}
return false;
}
}

View file

@ -0,0 +1,144 @@
package org.betterx.bclib.api.v3.bonemeal;
import org.betterx.bclib.api.v3.levelgen.features.BCLConfigureFeature;
import org.betterx.bclib.api.v3.tag.BCLBlockTags;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
public class BonemealAPI {
public static BonemealAPI INSTANCE = new BonemealAPI();
private final Map<TagKey<Block>, BonemealBlockSpreader> taggedSpreaders;
private final Map<Block, FeatureSpreader> featureSpreaders;
private BonemealAPI() {
taggedSpreaders = new HashMap<>();
featureSpreaders = new HashMap<>();
addSpreadableBlocks(BCLBlockTags.BONEMEAL_TARGET_NETHERRACK, NetherrackSpreader.INSTANCE);
addSpreadableBlocks(BCLBlockTags.BONEMEAL_TARGET_END_STONE, EndStoneSpreader.INSTANCE);
}
/**
* Bonemeal can be used to spread vegetation to neighbouring blocks.
* <p>
* This method allows you to register a block (the type that was clicked with bonemeal) with
* a {@link BCLConfigureFeature} that will be placed on the bonemeald block
* <p>
* You can achieve the same behaviour by implementing {@link BonemealNyliumLike} on your custom
* BlockClass. This is mainly intended for vanilla Blocks where you need to add bonemeal
* behaviour
*
* @param target The block-type
* @param spreadableFeature the feature to place
*/
public void addSpreadableFeatures(
Block target,
@NotNull BCLConfigureFeature<? extends Feature<?>, ?> spreadableFeature
) {
featureSpreaders.put(target, new FeatureSpreader(target, spreadableFeature));
}
/**
* Bonemeal can be used to spread for example Nylium to Netherrack.
* <p>
* In this example, Netherrack is the target block which will get replaced by one of the source blocks (like
* Warped or Crimson Nylium. You can register Tag-Combinations to easily add your own behaviour for custom
* blocks.
* <p>
* When a Block with the Target-Tag
*
* @param targetTag If you click a Block with the given Tag using Bonemeal, you will replace it with
* a block from the sourceTag
* @param sourceTag Blocks with this Tag can replace the Target block if they are in a 3x3 Neighborhood
* centered around the target Block.
*/
public void addSpreadableBlocks(@NotNull TagKey<Block> targetTag, @NotNull TagKey<Block> sourceTag) {
taggedSpreaders.put(targetTag, new TaggedBonemealBlockSpreader(sourceTag));
}
/**
* See {@link #addSpreadableBlocks(TagKey, TagKey)} for Details.
*
* @param targetTag If you click a Block with the given Tag using Bonemeal, you will replace it with
* * a block from the sourceTag
* @param spreader The {@link BonemealBlockSpreader}-Object that is called when a corresponding target-Block
* is clicked with Bone-Meal
*/
public void addSpreadableBlocks(@NotNull TagKey<Block> targetTag, @NotNull BonemealBlockSpreader spreader) {
taggedSpreaders.put(targetTag, spreader);
}
/**
* When a block is clicked with Bonemeal, this method will be called with the state of the given Block.
* <p>
* If the Method returs a valid {@link BonemealBlockSpreader}-Instance, it will override the default behaviour
* for the BoneMeal, otherwise the vanilla action will be performed.
*
* @param state The {@link BlockState} you need to test
* @return A valid spreader instance, or {@code null}
*/
@ApiStatus.Internal
public BonemealBlockSpreader blockSpreaderForState(@NotNull BlockState state) {
for (var e : taggedSpreaders.entrySet()) {
if (state.is(e.getKey())) {
return e.getValue();
}
}
return null;
}
@ApiStatus.Internal
public FeatureSpreader featureSpreaderForState(@NotNull BlockState state) {
return featureSpreaders.get(state.getBlock());
}
@ApiStatus.Internal
public boolean runSpreaders(ItemStack itemStack, Level level, BlockPos blockPos) {
BlockState blockState = level.getBlockState(blockPos);
BonemealBlockSpreader spreader = org.betterx.bclib.api.v3.bonemeal.BonemealAPI
.INSTANCE
.blockSpreaderForState(blockState);
if (spreader != null) {
if (spreader.isValidBonemealSpreadTarget(level, blockPos, blockState, level.isClientSide)) {
if (level instanceof ServerLevel) {
if (spreader.performBonemealSpread((ServerLevel) level, level.random, blockPos, blockState)) {
itemStack.shrink(1);
}
}
return true;
}
}
FeatureSpreader fSpreader = org.betterx.bclib.api.v3.bonemeal.BonemealAPI
.INSTANCE
.featureSpreaderForState(blockState);
if (fSpreader != null) {
if (fSpreader.isValidBonemealTarget(level, blockPos, blockState, level.isClientSide)) {
if (level instanceof ServerLevel) {
if (fSpreader.isBonemealSuccess(level, level.random, blockPos, blockState)) {
fSpreader.performBonemeal((ServerLevel) level, level.random, blockPos, blockState);
}
itemStack.shrink(1);
}
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,20 @@
package org.betterx.bclib.api.v3.bonemeal;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.state.BlockState;
public interface BonemealBlockSpreader {
boolean isValidBonemealSpreadTarget(BlockGetter blockGetter, BlockPos blockPos, BlockState blockState, boolean bl);
boolean performBonemealSpread(
ServerLevel serverLevel,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
);
}

View file

@ -0,0 +1,103 @@
package org.betterx.bclib.api.v3.bonemeal;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.configurations.RandomPatchConfiguration;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import java.util.List;
public interface BonemealGrassLike extends BonemealableBlock {
BlockState getGrowableCoverState(); //Blocks.GRASS.defaultBlockState();
Block getHostBlock(); //this
Holder<PlacedFeature> getCoverFeature(); //VegetationPlacements.GRASS_BONEMEAL
List<ConfiguredFeature<?, ?>> getFlowerFeatures(); /*serverLevel.getBiome(currentPos)
.value()
.getGenerationSettings()
.getFlowerFeatures();*/
default boolean canGrowFlower(RandomSource random) {
return random.nextInt(8) == 0;
}
default boolean canGrowCover(RandomSource random) {
return random.nextInt(10) == 0;
}
default boolean isValidBonemealTarget(
BlockGetter blockGetter,
BlockPos blockPos,
BlockState blockState,
boolean bl
) {
return blockGetter.getBlockState(blockPos.above()).isAir();
}
default boolean isBonemealSuccess(
Level level,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
return true;
}
default void performBonemeal(ServerLevel serverLevel, RandomSource random, BlockPos pos, BlockState state) {
final BlockPos above = pos.above();
final BlockState growableState = getGrowableCoverState();
outerLoop:
for (int bonemealAttempt = 0; bonemealAttempt < 128; ++bonemealAttempt) {
BlockPos currentPos = above;
for (int j = 0; j < bonemealAttempt / 16; ++j) {
currentPos = currentPos.offset(
random.nextInt(3) - 1,
(random.nextInt(3) - 1) * random.nextInt(3) / 2,
random.nextInt(3) - 1
);
if (!serverLevel.getBlockState(currentPos.below()).is(getHostBlock())
|| serverLevel.getBlockState(currentPos)
.isCollisionShapeFullBlock(serverLevel, currentPos)) {
continue outerLoop;
}
}
BlockState currentState = serverLevel.getBlockState(currentPos);
if (currentState.is(growableState.getBlock()) && canGrowCover(random)) {
((BonemealableBlock) growableState.getBlock()).performBonemeal(
serverLevel,
random,
currentPos,
currentState
);
}
if (currentState.isAir()) {
Holder<PlacedFeature> boneFeature;
if (canGrowFlower(random)) {
List<ConfiguredFeature<?, ?>> list = getFlowerFeatures();
if (list.isEmpty()) {
continue;
}
boneFeature = ((RandomPatchConfiguration) list.get(0).config()).feature();
} else {
boneFeature = getCoverFeature();
}
boneFeature.value()
.place(serverLevel, serverLevel.getChunkSource().getGenerator(), random, currentPos);
}
}
}
}

View file

@ -0,0 +1,52 @@
package org.betterx.bclib.api.v3.bonemeal;
import org.betterx.bclib.api.v3.levelgen.features.BCLConfigureFeature;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
//adapted from NyliumBlock
public interface BonemealNyliumLike extends BonemealableBlock {
Block getHostBlock(); //this
BCLConfigureFeature<? extends Feature<?>, ?> getCoverFeature();
default boolean isValidBonemealTarget(
BlockGetter blockGetter,
BlockPos blockPos,
BlockState blockState,
boolean bl
) {
return blockGetter.getBlockState(blockPos.above()).isAir();
}
default boolean isBonemealSuccess(
Level level,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
return true;
}
default void performBonemeal(
ServerLevel serverLevel,
RandomSource randomSource,
BlockPos blockPos,
BlockState blockState
) {
final BlockState currentState = serverLevel.getBlockState(blockPos);
if (currentState.is(getHostBlock())) {
BCLConfigureFeature<? extends Feature<?>, ?> feature = getCoverFeature();
if (feature != null) {
feature.placeInWorld(serverLevel, blockPos.above(), randomSource, true);
}
}
}
}

View file

@ -0,0 +1,12 @@
package org.betterx.bclib.api.v3.bonemeal;
import org.betterx.bclib.api.v3.tag.BCLBlockTags;
public class EndStoneSpreader extends TaggedBonemealBlockSpreader {
static final EndStoneSpreader INSTANCE = new EndStoneSpreader();
protected EndStoneSpreader() {
super(BCLBlockTags.BONEMEAL_SOURCE_END_STONE);
}
}

View file

@ -0,0 +1,40 @@
package org.betterx.bclib.api.v3.bonemeal;
import org.betterx.bclib.api.v3.levelgen.features.BCLConfigureFeature;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
public class FeatureSpreader implements BonemealNyliumLike {
public final BCLConfigureFeature<? extends Feature<?>, ?> spreadableFeature;
public final Block hostBlock;
public FeatureSpreader(Block hostBlock, BCLConfigureFeature<? extends Feature<?>, ?> spreadableFeature) {
this.spreadableFeature = spreadableFeature;
this.hostBlock = hostBlock;
}
@Override
public boolean isValidBonemealTarget(
BlockGetter blockGetter,
BlockPos blockPos,
BlockState blockState,
boolean bl
) {
return spreadableFeature != null
&& BonemealNyliumLike.super.isValidBonemealTarget(blockGetter, blockPos, blockState, bl);
}
@Override
public Block getHostBlock() {
return hostBlock;
}
@Override
public BCLConfigureFeature<? extends Feature<?>, ?> getCoverFeature() {
return spreadableFeature;
}
}

View file

@ -0,0 +1,18 @@
package org.betterx.bclib.api.v3.bonemeal;
import org.betterx.bclib.api.v3.tag.BCLBlockTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
public class NetherrackSpreader extends TaggedBonemealBlockSpreader {
public static final NetherrackSpreader INSTANCE = new NetherrackSpreader();
protected NetherrackSpreader() {
super(BCLBlockTags.BONEMEAL_SOURCE_NETHERRACK);
}
protected boolean hasCustomBehaviour(BlockState state) {
return !state.is(Blocks.WARPED_NYLIUM) && !state.is(Blocks.CRIMSON_NYLIUM);
}
}

View file

@ -0,0 +1,23 @@
package org.betterx.bclib.api.v3.bonemeal;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
class TaggedBonemealBlockSpreader extends BlockSpreader {
public final TagKey<Block> blockTag;
public TaggedBonemealBlockSpreader(TagKey<Block> blockTag) {
this.blockTag = blockTag;
}
@Override
protected boolean isValidSource(BlockState state) {
return state.is(blockTag);
}
@Override
protected boolean hasCustomBehaviour(BlockState state) {
return true;
}
}

View file

@ -55,7 +55,11 @@ public class BCLConfigureFeature<F extends Feature<FC>, FC extends FeatureConfig
}
public boolean placeInWorld(ServerLevel level, BlockPos pos, RandomSource random) {
return placeInWorld(getFeature(), getConfiguration(), level, pos, random);
return placeInWorld(level, pos, random, false);
}
public boolean placeInWorld(ServerLevel level, BlockPos pos, RandomSource random, boolean unchanged) {
return placeUnboundInWorld(getFeature(), getConfiguration(), level, pos, random, unchanged);
}
private static boolean placeUnboundInWorld(
@ -63,16 +67,19 @@ public class BCLConfigureFeature<F extends Feature<FC>, FC extends FeatureConfig
FeatureConfiguration config,
ServerLevel level,
BlockPos pos,
RandomSource random
RandomSource random,
boolean asIs
) {
if (config instanceof RandomPatchConfiguration rnd) {
var configured = rnd.feature().value().feature().value();
feature = configured.feature();
config = configured.config();
}
if (!asIs) {
if (config instanceof RandomPatchConfiguration rnd) {
var configured = rnd.feature().value().feature().value();
feature = configured.feature();
config = configured.config();
}
if (feature instanceof UserGrowableFeature growable) {
return growable.grow(level, pos, random, config);
if (feature instanceof UserGrowableFeature growable) {
return growable.grow(level, pos, random, config);
}
}
FeaturePlaceContext context = new FeaturePlaceContext(
@ -92,7 +99,7 @@ public class BCLConfigureFeature<F extends Feature<FC>, FC extends FeatureConfig
BlockPos pos,
RandomSource random
) {
return placeUnboundInWorld(feature, FeatureConfiguration.NONE, level, pos, random);
return placeUnboundInWorld(feature, FeatureConfiguration.NONE, level, pos, random, true);
}
public static <FC extends FeatureConfiguration> boolean placeInWorld(
@ -102,6 +109,6 @@ public class BCLConfigureFeature<F extends Feature<FC>, FC extends FeatureConfig
BlockPos pos,
RandomSource random
) {
return placeUnboundInWorld(feature, config, level, pos, random);
return placeUnboundInWorld(feature, config, level, pos, random, true);
}
}

View file

@ -44,7 +44,7 @@ import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends FeatureConfiguration> {
private final ResourceLocation featureID;
protected final ResourceLocation featureID;
private final F feature;
private BCLFeatureBuilder(ResourceLocation featureID, F feature) {
@ -100,6 +100,22 @@ public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends Featur
);
}
public static WeightedBlockPatch startWeightedRandomPatch(
ResourceLocation featureID
) {
return new WeightedBlockPatch(
featureID,
(RandomPatchFeature) Feature.RANDOM_PATCH
);
}
public static WeightedBlockPatch startBonemealPatch(
ResourceLocation featureID
) {
return startWeightedRandomPatch(featureID).likeDefaultBonemeal();
}
public static RandomPatch startRandomPatch(
ResourceLocation featureID,
Holder<PlacedFeature> featureToPlace
@ -140,6 +156,15 @@ public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends Featur
);
}
public static NetherForrestVegetation startBonemealNetherVegetation(
ResourceLocation featureID
) {
return new NetherForrestVegetation(
featureID,
(NetherForestVegetationFeature) Feature.NETHER_FOREST_VEGETATION
).spreadHeight(1).spreadWidth(3);
}
public static WithTemplates startWithTemplates(
ResourceLocation featureID
@ -773,9 +798,86 @@ public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends Featur
}
}
public static class WeightedBlock extends BCLFeatureBuilder<SimpleBlockFeature, SimpleBlockConfiguration> {
SimpleWeightedRandomList.Builder<BlockState> stateBuilder = SimpleWeightedRandomList.builder();
public static class WeightedBlockPatch extends WeightedBaseBlock<RandomPatchFeature, RandomPatchConfiguration, WeightedBlockPatch> {
private BlockPredicate groundType = null;
private boolean isEmpty = true;
private int tries = 96;
private int xzSpread = 7;
private int ySpread = 3;
protected WeightedBlockPatch(@NotNull ResourceLocation featureID, @NotNull RandomPatchFeature feature) {
super(featureID, feature);
}
public WeightedBlockPatch isEmpty() {
return this.isEmpty(true);
}
public WeightedBlockPatch isEmpty(boolean value) {
this.isEmpty = value;
return this;
}
public WeightedBlockPatch isOn(BlockPredicate predicate) {
this.groundType = predicate;
return this;
}
public WeightedBlockPatch isEmptyAndOn(BlockPredicate predicate) {
return this.isEmpty().isOn(predicate);
}
public WeightedBlockPatch likeDefaultNetherVegetation() {
return likeDefaultNetherVegetation(8, 4);
}
public WeightedBlockPatch likeDefaultNetherVegetation(int xzSpread, int ySpread) {
this.xzSpread = xzSpread;
this.ySpread = ySpread;
tries = xzSpread * xzSpread;
return this;
}
public WeightedBlockPatch likeDefaultBonemeal() {
return this.tries(9)
.spreadXZ(3)
.spreadY(1);
}
public WeightedBlockPatch tries(int v) {
tries = v;
return this;
}
public WeightedBlockPatch spreadXZ(int v) {
xzSpread = v;
return this;
}
public WeightedBlockPatch spreadY(int v) {
ySpread = v;
return this;
}
@Override
public RandomPatchConfiguration createConfiguration() {
BCLInlinePlacedBuilder<Feature<SimpleBlockConfiguration>, SimpleBlockConfiguration> blockFeature = BCLFeatureBuilder
.start(
new ResourceLocation(featureID.getNamespace(), "tmp_" + featureID.getPath()),
Feature.SIMPLE_BLOCK
)
.configuration(new SimpleBlockConfiguration(new WeightedStateProvider(stateBuilder.build())))
.inlinePlace();
if (isEmpty) blockFeature.isEmpty();
if (groundType != null) blockFeature.isOn(groundType);
return new RandomPatchConfiguration(tries, xzSpread, ySpread, blockFeature.build());
}
}
public static class WeightedBlock extends WeightedBaseBlock<SimpleBlockFeature, SimpleBlockConfiguration, WeightedBlock> {
private WeightedBlock(
@NotNull ResourceLocation featureID,
@NotNull SimpleBlockFeature feature
@ -783,31 +885,70 @@ public abstract class BCLFeatureBuilder<F extends Feature<FC>, FC extends Featur
super(featureID, feature);
}
public WeightedBlock add(Block block, int weight) {
return add(block.defaultBlockState(), weight);
}
public WeightedBlock add(BlockState state, int weight) {
stateBuilder.add(state, weight);
return this;
}
public WeightedBlock addAllStates(Block block, int weight) {
Set<BlockState> states = BCLPoiType.getBlockStates(block);
states.forEach(s -> add(block.defaultBlockState(), Math.max(1, weight / states.size())));
return this;
}
public WeightedBlock addAllStatesFor(IntegerProperty prop, Block block, int weight) {
Collection<Integer> values = prop.getPossibleValues();
values.forEach(s -> add(block.defaultBlockState().setValue(prop, s), Math.max(1, weight / values.size())));
return this;
}
@Override
public SimpleBlockConfiguration createConfiguration() {
return new SimpleBlockConfiguration(new WeightedStateProvider(stateBuilder.build()));
}
//TODO: Remove in the next Minor Update. This method is just for backward compatibility.
@Override
public WeightedBlock add(Block block, int weight) {
return super.add(block, weight);
}
//TODO: Remove in the next Minor Update. This method is just for backward compatibility.
@Override
public WeightedBlock add(BlockState state, int weight) {
return super.add(state, weight);
}
//TODO: Remove in the next Minor Update. This method is just for backward compatibility.
@Override
public WeightedBlock addAllStates(Block block, int weight) {
return super.addAllStates(block, weight);
}
//TODO: Remove in the next Minor Update. This method is just for backward compatibility.
@Override
public WeightedBlock addAllStatesFor(IntegerProperty prop, Block block, int weight) {
return super.addAllStatesFor(prop, block, weight);
}
}
private abstract static class WeightedBaseBlock<F extends Feature<FC>, FC extends FeatureConfiguration, W extends WeightedBaseBlock> extends BCLFeatureBuilder<F, FC> {
SimpleWeightedRandomList.Builder<BlockState> stateBuilder = SimpleWeightedRandomList.builder();
protected WeightedBaseBlock(
@NotNull ResourceLocation featureID,
@NotNull F feature
) {
super(featureID, feature);
}
public W add(Block block, int weight) {
return add(block.defaultBlockState(), weight);
}
public W add(BlockState state, int weight) {
stateBuilder.add(state, weight);
return (W) this;
}
public W addAllStates(Block block, int weight) {
Set<BlockState> states = BCLPoiType.getBlockStates(block);
states.forEach(s -> add(block.defaultBlockState(), Math.max(1, weight / states.size())));
return (W) this;
}
public W addAllStatesFor(IntegerProperty prop, Block block, int weight) {
Collection<Integer> values = prop.getPossibleValues();
values.forEach(s -> add(block.defaultBlockState().setValue(prop, s), Math.max(1, weight / values.size())));
return (W) this;
}
}
public static class AsRandomSelect extends BCLFeatureBuilder<RandomSelectorFeature, RandomFeatureConfiguration> {

View file

@ -354,6 +354,15 @@ abstract class CommonPlacedFeatureBuilder<F extends Feature<FC>, FC extends Feat
return modifier(Is.below(predicate));
}
public T inBiomes(ResourceLocation... biomeID) {
return modifier(InBiome.matchingID(biomeID));
}
public T notInBiomes(ResourceLocation... biomeID) {
return modifier(InBiome.notMatchingID(biomeID));
}
public T isEmptyAndOn(BlockPredicate predicate) {
return (T) this.isEmpty().isOn(predicate);
}
@ -420,4 +429,11 @@ abstract class CommonPlacedFeatureBuilder<F extends Feature<FC>, FC extends Feat
public BCLFeatureBuilder.RandomPatch inRandomPatch(ResourceLocation id) {
return BCLFeatureBuilder.startRandomPatch(id, build());
}
public BCLFeatureBuilder.RandomPatch randomBonemealDistribution(ResourceLocation id) {
return inRandomPatch(id)
.tries(9)
.spreadXZ(3)
.spreadY(1);
}
}

View file

@ -0,0 +1,69 @@
package org.betterx.bclib.api.v3.levelgen.features.placement;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.placement.PlacementContext;
import net.minecraft.world.level.levelgen.placement.PlacementFilter;
import net.minecraft.world.level.levelgen.placement.PlacementModifierType;
import java.util.List;
import java.util.Optional;
public class InBiome extends PlacementFilter {
public static final Codec<InBiome> CODEC = RecordCodecBuilder.create((instance) -> instance
.group(
Codec.BOOL
.fieldOf("negate")
.orElse(false)
.forGetter(cfg -> cfg.negate),
Codec.list(ResourceLocation.CODEC)
.fieldOf("biomes")
.forGetter(cfg -> cfg.biomeIDs)
)
.apply(instance, InBiome::new));
public final List<ResourceLocation> biomeIDs;
public final boolean negate;
protected InBiome(boolean negate, List<ResourceLocation> biomeIDs) {
this.biomeIDs = biomeIDs;
this.negate = negate;
}
public static InBiome matchingID(ResourceLocation... id) {
return new InBiome(false, List.of(id));
}
public static InBiome matchingID(List<ResourceLocation> ids) {
return new InBiome(false, ids);
}
public static InBiome notMatchingID(ResourceLocation... id) {
return new InBiome(true, List.of(id));
}
public static InBiome notMatchingID(List<ResourceLocation> ids) {
return new InBiome(true, ids);
}
@Override
protected boolean shouldPlace(PlacementContext ctx, RandomSource random, BlockPos pos) {
Holder<Biome> holder = ctx.getLevel().getBiome(pos);
Optional<ResourceLocation> biomeLocation = holder.unwrapKey().map(key -> key.location());
if (biomeLocation.isPresent()) {
boolean contains = biomeIDs.contains(biomeLocation.get());
return negate != contains;
}
return false;
}
@Override
public PlacementModifierType<InBiome> type() {
return PlacementModifiers.IN_BIOME;
}
}

View file

@ -71,6 +71,11 @@ public class PlacementModifiers {
UnderEveryLayer.CODEC
);
public static final PlacementModifierType<InBiome> IN_BIOME = register(
"in_biome",
InBiome.CODEC
);
private static <P extends PlacementModifier> PlacementModifierType<P> register(String path, Codec<P> codec) {
return register(BCLib.makeID(path), codec);

View file

@ -0,0 +1,38 @@
package org.betterx.bclib.api.v3.tag;
import org.betterx.worlds.together.tag.v3.TagManager;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import org.jetbrains.annotations.ApiStatus;
public class BCLBlockTags {
public static final TagKey<Block> BONEMEAL_SOURCE_NETHERRACK = TagManager.BLOCKS.makeTogetherTag(
"bonemeal/source/netherrack"
);
public static final TagKey<Block> BONEMEAL_TARGET_NETHERRACK = TagManager.BLOCKS.makeTogetherTag(
"bonemeal/target/netherrack"
);
public static final TagKey<Block> BONEMEAL_SOURCE_END_STONE = TagManager.BLOCKS.makeTogetherTag(
"bonemeal/source/end_stone"
);
public static final TagKey<Block> BONEMEAL_TARGET_END_STONE = TagManager.BLOCKS.makeTogetherTag(
"bonemeal/target/end_stone"
);
public static final TagKey<Block> BONEMEAL_SOURCE_OBSIDIAN = TagManager.BLOCKS.makeTogetherTag(
"bonemeal/source/obsidian"
);
public static final TagKey<Block> BONEMEAL_TARGET_OBSIDIAN = TagManager.BLOCKS.makeTogetherTag(
"bonemeal/target/obsidian"
);
@ApiStatus.Internal
public static void ensureStaticallyLoaded() {
TagManager.BLOCKS.add(BONEMEAL_SOURCE_NETHERRACK, Blocks.WARPED_NYLIUM, Blocks.CRIMSON_NYLIUM);
TagManager.BLOCKS.add(BONEMEAL_TARGET_NETHERRACK, Blocks.NETHERRACK);
TagManager.BLOCKS.add(BONEMEAL_TARGET_END_STONE, Blocks.END_STONE);
TagManager.BLOCKS.add(BONEMEAL_TARGET_OBSIDIAN, Blocks.OBSIDIAN);
}
}

View file

@ -109,8 +109,37 @@ public class FeatureSaplingBlock<F extends Feature<FC>, FC extends FeatureConfig
@Override
public void advanceTree(ServerLevel world, BlockPos pos, BlockState blockState, RandomSource random) {
BCLConfigureFeature<F, FC> conf = getConfiguredFeature(blockState);
conf.placeInWorld(world, pos, random);
if (blockState.getValue(STAGE) == 0) {
world.setBlock(pos, blockState.cycle(STAGE), 4);
} else {
BCLConfigureFeature<F, FC> conf = getConfiguredFeature(blockState);
growFeature(conf, world, pos, blockState, random);
}
}
protected boolean growFeature(
BCLConfigureFeature<F, FC> feature,
ServerLevel serverLevel,
BlockPos blockPos,
BlockState originalBlockState,
RandomSource randomSource
) {
if (feature == null) {
return false;
} else {
BlockState emptyState = serverLevel.getFluidState(blockPos).createLegacyBlock();
serverLevel.setBlock(blockPos, emptyState, 4);
if (feature.placeInWorld(serverLevel, blockPos, randomSource)) {
if (serverLevel.getBlockState(blockPos) == emptyState) {
serverLevel.sendBlockUpdated(blockPos, originalBlockState, emptyState, 2);
}
return true;
} else {
serverLevel.setBlock(blockPos, originalBlockState, 4);
return false;
}
}
}
@Override

View file

@ -81,8 +81,47 @@ public class UpdatesScreen extends BCLibLayoutScreen {
row.addText(fit(), fit(), Component.literal(" -> "));
row.addText(fit(), fit(), Component.literal(updated)).setColor(ColorHelper.GREEN);
row.addFiller();
if (nfo != null && nfo.getMetadata().getContact().get("homepage").isPresent()) {
row.addButton(fit(), fit(), Component.translatable("bclib.updates.curseforge_link"))
boolean createdDownloadLink = false;
if (nfo != null
&& nfo.getMetadata().getCustomValue("bclib") != null
&& nfo.getMetadata().getCustomValue("bclib").getAsObject().get("downloads") != null) {
CustomValue.CvObject downloadLinks = nfo.getMetadata()
.getCustomValue("bclib")
.getAsObject()
.get("downloads")
.getAsObject();
String link = null;
Component name = null;
if (Configs.CLIENT_CONFIG.preferModrinthForUpdates() && downloadLinks.get("modrinth") != null) {
link = downloadLinks.get("modrinth").getAsString();
name = Component.translatable("bclib.updates.modrinth_link");
// row.addButton(fit(), fit(), Component.translatable("bclib.updates.modrinth_link"))
// .onPress((bt) -> {
// this.openLink(downloadLinks.get("modrinth").getAsString());
// }).centerVertical();
// createdDownloadLink = true;
} else if (downloadLinks.get("curseforge") != null) {
link = downloadLinks.get("curseforge").getAsString();
name = Component.translatable("bclib.updates.curseforge_link");
// row.addButton(fit(), fit(), Component.translatable("bclib.updates.curseforge_link"))
// .onPress((bt) -> {
// this.openLink(downloadLinks.get("curseforge").getAsString());
// }).centerVertical();
// createdDownloadLink = true;
}
if (link != null) {
createdDownloadLink = true;
final String finalLink = link;
row.addButton(fit(), fit(), name)
.onPress((bt) -> {
this.openLink(finalLink);
}).centerVertical();
}
}
if (!createdDownloadLink && nfo != null && nfo.getMetadata().getContact().get("homepage").isPresent()) {
row.addButton(fit(), fit(), Component.translatable("bclib.updates.download_link"))
.onPress((bt) -> {
this.openLink(nfo.getMetadata().getContact().get("homepage").get());
}).centerVertical();

View file

@ -41,6 +41,8 @@ import org.jetbrains.annotations.Nullable;
public class WorldSetupScreen extends LayoutScreen {
private final WorldCreationContext context;
private final CreateWorldScreen createWorldScreen;
private Range<Integer> netherBiomeSize;
private Range<Integer> netherVerticalBiomeSize;
private Range<Integer> landBiomeSize;
private Range<Integer> voidBiomeSize;
private Range<Integer> centerBiomeSize;
@ -60,6 +62,8 @@ public class WorldSetupScreen extends LayoutScreen {
Checkbox endCustomTerrain;
Checkbox generateEndVoid;
Checkbox netherLegacy;
Checkbox netherVertical;
Checkbox netherAmplified;
public LayoutComponent<?, ?> netherPage(BCLNetherBiomeSourceConfig netherConfig) {
VerticalStack content = new VerticalStack(fill(), fit()).centerHorizontal();
@ -77,9 +81,51 @@ public class WorldSetupScreen extends LayoutScreen {
netherConfig.mapVersion == BCLNetherBiomeSourceConfig.NetherBiomeMapType.SQUARE
);
netherAmplified = content.indent(20).addCheckbox(
fit(), fit(),
Component.translatable("title.screen.bclib.worldgen.nether_amplified"),
netherConfig.amplified
);
netherVertical = content.indent(20).addCheckbox(
fit(), fit(),
Component.translatable("title.screen.bclib.worldgen.nether_vertical"),
netherConfig.useVerticalBiomes
);
content.addSpacer(12);
content.addText(fit(), fit(), Component.translatable("title.screen.bclib.worldgen.avg_biome_size"))
.centerHorizontal();
content.addHorizontalSeparator(8).alignTop();
netherBiomeSize = content.addRange(
fixed(200),
fit(),
Component.translatable("title.screen.bclib.worldgen.nether_biome_size"),
1,
512,
netherConfig.biomeSize / 16
);
netherVerticalBiomeSize = content.addRange(
fixed(200),
fit(),
Component.translatable("title.screen.bclib.worldgen.nether_vertical_biome_size"),
1,
32,
netherConfig.biomeSizeVertical / 16
);
bclibNether.onChange((cb, state) -> {
netherLegacy.setEnabled(state);
netherAmplified.setEnabled(state);
netherVertical.setEnabled(state);
netherBiomeSize.setEnabled(state);
netherVerticalBiomeSize.setEnabled(state && netherVertical.isChecked());
});
netherVertical.onChange((cb, state) -> {
netherVerticalBiomeSize.setEnabled(state && bclibNether.isChecked());
});
content.addSpacer(8);
@ -198,6 +244,8 @@ public class WorldSetupScreen extends LayoutScreen {
private void updateSettings() {
Map<ResourceKey<LevelStem>, ChunkGenerator> betterxDimensions = TogetherWorldPreset.getDimensionsMap(
PresetsRegistry.BCL_WORLD);
Map<ResourceKey<LevelStem>, ChunkGenerator> betterxAmplifiedDimensions = TogetherWorldPreset.getDimensionsMap(
PresetsRegistry.BCL_WORLD_AMPLIFIED);
Map<ResourceKey<LevelStem>, ChunkGenerator> vanillaDimensions = TogetherWorldPreset.getDimensionsMap(
WorldPresets.NORMAL);
BCLEndBiomeSourceConfig.EndBiomeMapType endVersion = BCLEndBiomeSourceConfig.DEFAULT.mapVersion;
@ -233,12 +281,17 @@ public class WorldSetupScreen extends LayoutScreen {
netherLegacy.isChecked()
? BCLNetherBiomeSourceConfig.NetherBiomeMapType.SQUARE
: BCLNetherBiomeSourceConfig.NetherBiomeMapType.HEX,
BCLNetherBiomeSourceConfig.DEFAULT.biomeSize,
BCLNetherBiomeSourceConfig.DEFAULT.biomeSizeVertical,
BCLNetherBiomeSourceConfig.DEFAULT.useVerticalBiomes
netherBiomeSize.getValue() * 16,
netherVerticalBiomeSize.getValue() * 16,
netherVertical.isChecked(),
netherAmplified.isChecked()
);
ChunkGenerator netherGenerator = betterxDimensions.get(LevelStem.NETHER);
ChunkGenerator netherGenerator = (
netherAmplified.isChecked()
? betterxAmplifiedDimensions
: betterxDimensions
).get(LevelStem.NETHER);
((BCLibNetherBiomeSource) netherGenerator.getBiomeSource()).setTogetherConfig(netherConfig);
updateConfiguration(LevelStem.NETHER, BuiltinDimensionTypes.NETHER, netherGenerator);

View file

@ -11,7 +11,6 @@ public class ClientConfig extends NamedPathConfig {
"didShowWelcome",
"version"
);
@ConfigUI(topPadding = 12)
public static final ConfigToken<Boolean> CHECK_VERSIONS = ConfigToken.Boolean(
true,
"check",
@ -24,12 +23,20 @@ public class ClientConfig extends NamedPathConfig {
"ui"
);
@ConfigUI(leftPadding = 8)
public static final ConfigToken<Boolean> PREFER_MODRINTH_FOR_UPDATES = ConfigToken.Boolean(
true,
"preferModrinthForUpdates",
"ui"
);
@ConfigUI(hide = true)
public static final ConfigToken<Boolean> FORCE_BETTERX_PRESET = ConfigToken.Boolean(
true,
"forceBetterXPreset",
"ui"
);
@ConfigUI(topPadding = 12)
public static final ConfigToken<Boolean> SUPPRESS_EXPERIMENTAL_DIALOG = ConfigToken.Boolean(
false,
"suppressExperimentalDialogOnLoad",
@ -107,6 +114,8 @@ public class ClientConfig extends NamedPathConfig {
"rendering",
(config) -> config.get(CUSTOM_FOG_RENDERING)
);
@ConfigUI(topPadding = 12)
public static final ConfigToken<Boolean> SURVIES_ON_HINT = ConfigToken.Boolean(
true,
"survives_on_hint",
@ -194,8 +203,12 @@ public class ClientConfig extends NamedPathConfig {
public void setForceBetterXPreset(boolean v) {
set(FORCE_BETTERX_PRESET, v);
}
public boolean survivesOnHint() {
return get(ClientConfig.SURVIES_ON_HINT);
}
public boolean preferModrinthForUpdates() {
return get(ClientConfig.PREFER_MODRINTH_FOR_UPDATES);
}
}

View file

@ -3,11 +3,7 @@ package org.betterx.bclib.config;
import org.betterx.bclib.BCLib;
public class MainConfig extends NamedPathConfig {
public static final ConfigToken<Boolean> VERBOSE_LOGGING = ConfigToken.Boolean(
true,
"verbose",
Configs.MAIN_INFO_CATEGORY
);
public static final ConfigToken<Boolean> APPLY_PATCHES = ConfigToken.Boolean(
true,
@ -15,7 +11,7 @@ public class MainConfig extends NamedPathConfig {
Configs.MAIN_PATCH_CATEGORY
);
@ConfigUI(leftPadding = 8, topPadding = 8)
@ConfigUI(leftPadding = 8)
public static final ConfigToken<Boolean> REPAIR_BIOMES = DependendConfigToken.Boolean(
false,
"repairBiomesOnLoad",
@ -24,6 +20,13 @@ public class MainConfig extends NamedPathConfig {
APPLY_PATCHES)
);
@ConfigUI(topPadding = 8)
public static final ConfigToken<Boolean> VERBOSE_LOGGING = ConfigToken.Boolean(
true,
"verbose",
Configs.MAIN_INFO_CATEGORY
);
public MainConfig() {
super(BCLib.MOD_ID, "main", true, true);

View file

@ -9,9 +9,147 @@ import net.minecraft.world.level.ItemLike;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
public abstract class EquipmentSet {
public static class AttackDamage {
public static SetValues WOOD_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, 3)
.add(EquipmentSet.SHOVEL_SLOT, 1.5f)
.add(EquipmentSet.PICKAXE_SLOT, 1)
.add(EquipmentSet.AXE_SLOT, 6)
.add(EquipmentSet.HOE_SLOT, 0);
public static SetValues STONE_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, 3)
.add(EquipmentSet.SHOVEL_SLOT, 1.5f)
.add(EquipmentSet.PICKAXE_SLOT, 1)
.add(EquipmentSet.AXE_SLOT, 7)
.add(EquipmentSet.HOE_SLOT, -1);
public static SetValues GOLDEN_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, 3)
.add(EquipmentSet.SHOVEL_SLOT, 1.5f)
.add(EquipmentSet.PICKAXE_SLOT, 1)
.add(EquipmentSet.AXE_SLOT, 6)
.add(EquipmentSet.HOE_SLOT, 0);
public static SetValues IRON_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, 3)
.add(EquipmentSet.SHOVEL_SLOT, 1.5f)
.add(EquipmentSet.PICKAXE_SLOT, 1)
.add(EquipmentSet.AXE_SLOT, 6)
.add(EquipmentSet.HOE_SLOT, -2);
public static SetValues DIAMOND_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, 3)
.add(EquipmentSet.SHOVEL_SLOT, 1.5f)
.add(EquipmentSet.PICKAXE_SLOT, 1)
.add(EquipmentSet.AXE_SLOT, 5)
.add(EquipmentSet.HOE_SLOT, -3);
public static SetValues NETHERITE_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, 3)
.add(EquipmentSet.SHOVEL_SLOT, 1.5f)
.add(EquipmentSet.PICKAXE_SLOT, 1)
.add(EquipmentSet.AXE_SLOT, 5)
.add(EquipmentSet.HOE_SLOT, -4);
}
public static class AttackSpeed {
public static SetValues WOOD_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, -2.4f)
.add(EquipmentSet.SHOVEL_SLOT, -3.0f)
.add(EquipmentSet.PICKAXE_SLOT, -2.8f)
.add(EquipmentSet.AXE_SLOT, -3.2f)
.add(EquipmentSet.HOE_SLOT, -3.0f);
public static SetValues STONE_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, -2.4f)
.add(EquipmentSet.SHOVEL_SLOT, -3.0f)
.add(EquipmentSet.PICKAXE_SLOT, -2.8f)
.add(EquipmentSet.AXE_SLOT, -3.2f)
.add(EquipmentSet.HOE_SLOT, -2.0f);
public static SetValues GOLDEN_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, -2.4f)
.add(EquipmentSet.SHOVEL_SLOT, -3.0f)
.add(EquipmentSet.PICKAXE_SLOT, -2.8f)
.add(EquipmentSet.AXE_SLOT, -3.0f)
.add(EquipmentSet.HOE_SLOT, -3.0f);
public static SetValues IRON_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, -2.4f)
.add(EquipmentSet.SHOVEL_SLOT, -3.0f)
.add(EquipmentSet.PICKAXE_SLOT, -2.8f)
.add(EquipmentSet.AXE_SLOT, -3.1f)
.add(EquipmentSet.HOE_SLOT, -1.0f);
public static SetValues DIAMOND_LEVEL = EquipmentSet.SetValues
.create()
.add(EquipmentSet.SWORD_SLOT, -2.4f)
.add(EquipmentSet.SHOVEL_SLOT, -3.0f)
.add(EquipmentSet.PICKAXE_SLOT, -2.8f)
.add(EquipmentSet.AXE_SLOT, -3.0f)
.add(EquipmentSet.HOE_SLOT, 0.0f);
public static SetValues NETHERITE_LEVEL = DIAMOND_LEVEL;
}
public interface ItemDescriptorCreator<I extends Item> {
EquipmentDescription<I> build(Item base, Function<Tier, I> creator);
}
public interface DescriptorCreator<I extends Item> {
EquipmentDescription<I> build(Function<Tier, I> creator);
}
public interface ItemCreator<I extends Item> {
I build(Tier t, float attackDamage, float attackSpeed);
}
public static class SetValues {
private final Map<String, Float> values;
private SetValues() {
values = new HashMap<>();
}
public static SetValues create() {
return new SetValues();
}
public static SetValues copy(SetValues source, float offset) {
SetValues v = create();
for (var e : source.values.entrySet())
v.add(e.getKey(), e.getValue() + offset);
return v;
}
public SetValues add(String slot, float value) {
values.put(slot, value);
return this;
}
public SetValues offset(String slot, float offset) {
values.put(slot, get(slot) + offset);
return this;
}
public float get(String slot) {
return values.getOrDefault(slot, 0.0f);
}
}
public final Tier material;
public final String baseName;
public final String modID;
@ -28,19 +166,62 @@ public abstract class EquipmentSet {
public static final String LEGGINGS_SLOT = "leggings";
public static final String BOOTS_SLOT = "boots";
public final SetValues attackDamage;
public final SetValues attackSpeed;
private final Map<String, EquipmentDescription<?>> descriptions = new HashMap<>();
public EquipmentSet(Tier material, String modID, String baseName, ItemLike stick) {
@Deprecated(forRemoval = true)
public EquipmentSet(
Tier material,
String modID,
String baseName,
ItemLike stick
) {
this(material, modID, baseName, stick, AttackDamage.IRON_LEVEL, AttackSpeed.IRON_LEVEL);
}
public EquipmentSet(
Tier material,
String modID,
String baseName,
ItemLike stick,
SetValues attackDamage,
SetValues attackSpeed
) {
this.material = material;
this.baseName = baseName;
this.modID = modID;
this.stick = stick;
this.attackDamage = attackDamage;
this.attackSpeed = attackSpeed;
}
protected <I extends Item> void add(String slot, EquipmentDescription<I> desc) {
descriptions.put(slot, desc);
}
protected <I extends Item> void add(
String slot,
EquipmentSet baseSet,
ItemDescriptorCreator<I> descriptor,
ItemCreator<I> item
) {
EquipmentDescription<I> desc = descriptor.build(
baseSet.getSlot(slot),
(tier) -> item.build(tier, this.attackDamage.get(slot), this.attackSpeed.get(slot))
);
descriptions.put(slot, desc);
}
protected <I extends Item> void add(String slot, DescriptorCreator<I> descriptor, ItemCreator<I> item) {
EquipmentDescription<I> desc = descriptor.build(
(tier) -> item.build(tier, this.attackDamage.get(slot), this.attackSpeed.get(slot))
);
descriptions.put(slot, desc);
}
public EquipmentSet init(ItemRegistry itemsRegistry) {
for (var desc : descriptions.entrySet()) {
desc.getValue()

View file

@ -10,6 +10,7 @@ import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.BoneMealItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
@ -33,7 +34,7 @@ public class BoneMealItemMixin {
@Inject(method = "useOn", at = @At("HEAD"), cancellable = true)
private void bclib_onUse(UseOnContext context, CallbackInfoReturnable<InteractionResult> info) {
Level world = context.getLevel();
BlockPos blockPos = context.getClickedPos();
final BlockPos blockPos = context.getClickedPos();
if (!world.isClientSide()) {
if (BonemealAPI.isTerrain(world.getBlockState(blockPos).getBlock())) {
boolean consume = false;
@ -65,6 +66,21 @@ public class BoneMealItemMixin {
}
}
@Inject(method = "growCrop", at = @At("HEAD"), cancellable = true)
private static void bcl_growCrop(
ItemStack itemStack,
Level level,
BlockPos blockPos,
CallbackInfoReturnable<Boolean> cir
) {
if (org.betterx.bclib.api.v3.bonemeal.BonemealAPI
.INSTANCE
.runSpreaders(itemStack, level, blockPos)
) {
cir.setReturnValue(true);
}
}
@Unique
private boolean bclib_growLandGrass(Level level, BlockPos pos) {
int y1 = pos.getY() + 3;

View file

@ -0,0 +1,36 @@
package org.betterx.bclib.mixin.common;
import org.betterx.bclib.api.v2.generator.BCLChunkGenerator;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(NoiseGeneratorSettings.class)
public abstract class NoiseGeneratorSettingsMixin {
@Shadow
static protected Holder<NoiseGeneratorSettings> register(
Registry<NoiseGeneratorSettings> registry,
ResourceKey<NoiseGeneratorSettings> resourceKey,
NoiseGeneratorSettings noiseGeneratorSettings
) {
return null;
}
;
@Inject(method = "bootstrap", at = @At("HEAD"))
private static void bcl_addNoiseGenerators(
Registry<NoiseGeneratorSettings> registry,
CallbackInfoReturnable<Holder<NoiseGeneratorSettings>> cir
) {
register(registry, BCLChunkGenerator.AMPLIFIED_NETHER, BCLChunkGenerator.amplifiedNether());
}
}

View file

@ -131,8 +131,8 @@ public class VersionChecker implements Runnable {
}
if (mod.n != null && mod.v != null && KNOWN_MODS.contains(mod.n)) {
String installedVersion = ModUtil.getModVersion(mod.n);
boolean isNew = ModUtil.isLargerVersion(mod.v, installedVersion) && !installedVersion.equals(
"0.0.0");
boolean isNew = ModUtil.isLargerVersion(mod.v, installedVersion)
&& !installedVersion.equals("0.0.0");
BCLib.LOGGER.info(" - " + mod.n + ":" + mod.v + (isNew ? " (update available)" : ""));
if (isNew)
NEW_VERSIONS.add(mod);

View file

@ -1,6 +1,7 @@
package org.betterx.bclib.registry;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.v2.generator.BCLChunkGenerator;
import org.betterx.bclib.api.v2.generator.config.BCLEndBiomeSourceConfig;
import org.betterx.bclib.api.v2.generator.config.BCLNetherBiomeSourceConfig;
import org.betterx.bclib.api.v2.levelgen.LevelGenUtil;
@ -10,33 +11,81 @@ import org.betterx.worlds.together.levelgen.WorldGenUtil;
import org.betterx.worlds.together.worldPreset.TogetherWorldPreset;
import org.betterx.worlds.together.worldPreset.WorldPresets;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.levelgen.presets.WorldPreset;
import java.util.Map;
public class PresetsRegistry implements WorldPresetBootstrap {
public static ResourceKey<WorldPreset> BCL_WORLD;
public static ResourceKey<WorldPreset> BCL_WORLD_LARGE;
public static ResourceKey<WorldPreset> BCL_WORLD_AMPLIFIED;
public static ResourceKey<WorldPreset> BCL_WORLD_17;
public void bootstrapWorldPresets() {
BCL_WORLD =
WorldPresets.register(
BCLib.makeID("normal"),
(overworldStem, netherContext, endContext) ->
(overworldStem, netherContext, endContext, noiseSettings, noiseBasedOverworld) ->
buildPreset(
overworldStem,
netherContext,
BCLNetherBiomeSourceConfig.DEFAULT, endContext,
BCLEndBiomeSourceConfig.DEFAULT
netherContext, BCLNetherBiomeSourceConfig.DEFAULT,
endContext, BCLEndBiomeSourceConfig.DEFAULT
),
true
);
BCL_WORLD_LARGE =
WorldPresets.register(
BCLib.makeID("large"),
(overworldStem, netherContext, endContext, noiseSettings, noiseBasedOverworld) -> {
Holder<NoiseGeneratorSettings> largeBiomeGenerator = noiseSettings
.getOrCreateHolderOrThrow(NoiseGeneratorSettings.LARGE_BIOMES);
return buildPreset(
noiseBasedOverworld.make(
overworldStem.generator().getBiomeSource(),
largeBiomeGenerator
),
netherContext, BCLNetherBiomeSourceConfig.MINECRAFT_18_LARGE,
endContext, BCLEndBiomeSourceConfig.MINECRAFT_18_LARGE
);
},
true
);
BCL_WORLD_AMPLIFIED = WorldPresets.register(
BCLib.makeID("amplified"),
(overworldStem, netherContext, endContext, noiseSettings, noiseBasedOverworld) -> {
Holder<NoiseGeneratorSettings> amplifiedBiomeGenerator = noiseSettings
.getOrCreateHolderOrThrow(NoiseGeneratorSettings.AMPLIFIED);
WorldGenUtil.Context amplifiedNetherContext = new WorldGenUtil.Context(
netherContext.biomes,
netherContext.dimension,
netherContext.structureSets,
netherContext.noiseParameters,
Holder.direct(BCLChunkGenerator.amplifiedNether())
);
return buildPreset(
noiseBasedOverworld.make(
overworldStem.generator().getBiomeSource(),
amplifiedBiomeGenerator
),
amplifiedNetherContext, BCLNetherBiomeSourceConfig.MINECRAFT_18_AMPLIFIED,
endContext, BCLEndBiomeSourceConfig.MINECRAFT_18_AMPLIFIED
);
},
true
);
BCL_WORLD_17 = WorldPresets.register(
BCLib.makeID("legacy_17"),
(overworldStem, netherContext, endContext) ->
(overworldStem, netherContext, endContext, noiseSettings, noiseBasedOverworld) ->
buildPreset(
overworldStem,
netherContext,

View file

@ -12,5 +12,19 @@ public class PresetsRegistryClient {
WorldPresetsClient.registerCustomizeUI(PresetsRegistry.BCL_WORLD, (createWorldScreen, worldCreationContext) -> {
return new WorldSetupScreen(createWorldScreen, worldCreationContext);
});
WorldPresetsClient.registerCustomizeUI(
PresetsRegistry.BCL_WORLD_LARGE,
(createWorldScreen, worldCreationContext) -> {
return new WorldSetupScreen(createWorldScreen, worldCreationContext);
}
);
WorldPresetsClient.registerCustomizeUI(
PresetsRegistry.BCL_WORLD_AMPLIFIED,
(createWorldScreen, worldCreationContext) -> {
return new WorldSetupScreen(createWorldScreen, worldCreationContext);
}
);
}
}