NBT Feature Handling
This commit is contained in:
parent
155d3663df
commit
95434107ec
11 changed files with 532 additions and 69 deletions
|
@ -49,12 +49,13 @@ public class BCLib implements ModInitializer {
|
|||
AnvilRecipe.register();
|
||||
|
||||
DataExchangeAPI.registerDescriptors(List.of(
|
||||
HelloClient.DESCRIPTOR,
|
||||
HelloServer.DESCRIPTOR,
|
||||
RequestFiles.DESCRIPTOR,
|
||||
SendFiles.DESCRIPTOR,
|
||||
Chunker.DESCRIPTOR
|
||||
));
|
||||
HelloClient.DESCRIPTOR,
|
||||
HelloServer.DESCRIPTOR,
|
||||
RequestFiles.DESCRIPTOR,
|
||||
SendFiles.DESCRIPTOR,
|
||||
Chunker.DESCRIPTOR
|
||||
)
|
||||
);
|
||||
|
||||
BCLibPatch.register();
|
||||
Configs.save();
|
||||
|
|
|
@ -15,6 +15,7 @@ import net.minecraft.world.level.Level;
|
|||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
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.Biomes;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;
|
||||
|
@ -38,6 +39,7 @@ import com.google.common.base.Suppliers;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
import org.betterx.bclib.BCLib;
|
||||
import org.betterx.bclib.api.tag.CommonBiomeTags;
|
||||
|
@ -129,11 +131,11 @@ public class BiomeAPI {
|
|||
|
||||
public static final BCLBiome THE_END = registerEndLandBiome(getFromRegistry(Biomes.THE_END));
|
||||
public static final BCLBiome END_MIDLANDS = registerSubBiome(THE_END,
|
||||
getFromRegistry(Biomes.END_MIDLANDS).value(),
|
||||
0.5F);
|
||||
getFromRegistry(Biomes.END_MIDLANDS).value(),
|
||||
0.5F);
|
||||
public static final BCLBiome END_HIGHLANDS = registerSubBiome(THE_END,
|
||||
getFromRegistry(Biomes.END_HIGHLANDS).value(),
|
||||
0.5F);
|
||||
getFromRegistry(Biomes.END_HIGHLANDS).value(),
|
||||
0.5F);
|
||||
|
||||
public static final BCLBiome END_BARRENS = registerEndVoidBiome(getFromRegistry(new ResourceLocation("end_barrens")));
|
||||
public static final BCLBiome SMALL_END_ISLANDS = registerEndVoidBiome(getFromRegistry(new ResourceLocation(
|
||||
|
@ -216,15 +218,15 @@ public class BiomeAPI {
|
|||
|
||||
public static BCLBiome registerSubBiome(BCLBiome parent, BCLBiome subBiome) {
|
||||
return registerSubBiome(parent,
|
||||
subBiome,
|
||||
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
|
||||
subBiome,
|
||||
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
|
||||
}
|
||||
|
||||
public static BCLBiome registerSubBiome(BCLBiome parent, Biome subBiome, float genChance) {
|
||||
return registerSubBiome(parent,
|
||||
subBiome,
|
||||
genChance,
|
||||
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
|
||||
subBiome,
|
||||
genChance,
|
||||
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE));
|
||||
}
|
||||
|
||||
public static BCLBiome registerSubBiome(BCLBiome parent, BCLBiome subBiome, BiomeType dim) {
|
||||
|
@ -312,7 +314,7 @@ public class BiomeAPI {
|
|||
*/
|
||||
public static BCLBiome registerEndLandBiome(Holder<Biome> biome, float genChance) {
|
||||
BCLBiome bclBiome = new BCLBiome(biome.value(),
|
||||
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
|
||||
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
|
||||
|
||||
registerBiome(bclBiome, BiomeType.OTHER_END_LAND);
|
||||
return bclBiome;
|
||||
|
@ -360,7 +362,7 @@ public class BiomeAPI {
|
|||
*/
|
||||
public static BCLBiome registerEndVoidBiome(Holder<Biome> biome, float genChance) {
|
||||
BCLBiome bclBiome = new BCLBiome(biome.value(),
|
||||
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
|
||||
VanillaBiomeSettings.createVanilla().setGenChance(genChance).build());
|
||||
|
||||
registerBiome(bclBiome, BiomeType.END_VOID);
|
||||
return bclBiome;
|
||||
|
@ -601,7 +603,7 @@ public class BiomeAPI {
|
|||
public static void registerBiomeModification(ResourceKey<LevelStem> dimensionID,
|
||||
BiConsumer<ResourceLocation, Holder<Biome>> modification) {
|
||||
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = MODIFICATIONS.computeIfAbsent(dimensionID,
|
||||
k -> Lists.newArrayList());
|
||||
k -> Lists.newArrayList());
|
||||
modifications.add(modification);
|
||||
}
|
||||
|
||||
|
@ -635,7 +637,7 @@ public class BiomeAPI {
|
|||
/**
|
||||
* For internal use only
|
||||
*/
|
||||
public static void _runTagAdders() {
|
||||
public static void _runBiomeTagAdders() {
|
||||
for (var mod : TAG_ADDERS.entrySet()) {
|
||||
Stream<ResourceLocation> s = null;
|
||||
if (mod.getKey() == Level.NETHER) s = BiomeType.BIOME_TYPE_MAP.entrySet()
|
||||
|
@ -667,7 +669,7 @@ public class BiomeAPI {
|
|||
public static void onFinishingBiomeTags(ResourceKey dimensionID,
|
||||
BiConsumer<ResourceLocation, Holder<Biome>> modification) {
|
||||
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = TAG_ADDERS.computeIfAbsent(dimensionID,
|
||||
k -> Lists.newArrayList());
|
||||
k -> Lists.newArrayList());
|
||||
modifications.add(modification);
|
||||
}
|
||||
|
||||
|
@ -718,9 +720,9 @@ public class BiomeAPI {
|
|||
}
|
||||
|
||||
List<BiConsumer<ResourceLocation, Holder<Biome>>> modifications = MODIFICATIONS.get(level
|
||||
.dimensionTypeRegistration()
|
||||
.unwrapKey()
|
||||
.orElseThrow());
|
||||
.dimensionTypeRegistration()
|
||||
.unwrapKey()
|
||||
.orElseThrow());
|
||||
for (Holder<Biome> biomeHolder : biomes) {
|
||||
if (biomeHolder.isBound()) {
|
||||
applyModificationsAndUpdateFeatures(modifications, biomeHolder);
|
||||
|
@ -773,16 +775,16 @@ public class BiomeAPI {
|
|||
* @param biome The {@link Biome} to sort the features for
|
||||
*/
|
||||
public static void sortBiomeFeatures(Holder<Biome> biome) {
|
||||
// BiomeGenerationSettings settings = biome.value().getGenerationSettings();
|
||||
// BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings;
|
||||
// List<HolderSet<PlacedFeature>> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures());
|
||||
// final int size = featureList.size();
|
||||
// for (int i = 0; i < size; i++) {
|
||||
// List<Holder<PlacedFeature>> features = getFeaturesListCopy(featureList, i);
|
||||
// sortFeatures(features);
|
||||
// featureList.set(i, HolderSet.direct(features));
|
||||
// }
|
||||
// accessor.bclib_setFeatures(featureList);
|
||||
BiomeGenerationSettings settings = biome.value().getGenerationSettings();
|
||||
BiomeGenerationSettingsAccessor accessor = (BiomeGenerationSettingsAccessor) settings;
|
||||
List<HolderSet<PlacedFeature>> featureList = CollectionsUtil.getMutable(accessor.bclib_getFeatures());
|
||||
final int size = featureList.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
List<Holder<PlacedFeature>> features = getFeaturesListCopy(featureList, i);
|
||||
sortFeatures(features);
|
||||
featureList.set(i, HolderSet.direct(features));
|
||||
}
|
||||
accessor.bclib_setFeatures(featureList);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -930,25 +932,25 @@ public class BiomeAPI {
|
|||
}
|
||||
|
||||
private static void sortFeatures(List<Holder<PlacedFeature>> features) {
|
||||
// initFeatureOrder();
|
||||
//
|
||||
// Set<Holder<PlacedFeature>> featuresWithoutDuplicates = Sets.newHashSet();
|
||||
// features.forEach(holder -> featuresWithoutDuplicates.add(holder));
|
||||
//
|
||||
// if (featuresWithoutDuplicates.size() != features.size()) {
|
||||
// features.clear();
|
||||
// featuresWithoutDuplicates.forEach(feature -> features.add(feature));
|
||||
// }
|
||||
//
|
||||
// features.forEach(feature -> {
|
||||
// FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement());
|
||||
// });
|
||||
//
|
||||
// features.sort((f1, f2) -> {
|
||||
// int v1 = FEATURE_ORDER.getOrDefault(f1, 70000);
|
||||
// int v2 = FEATURE_ORDER.getOrDefault(f2, 70000);
|
||||
// return Integer.compare(v1, v2);
|
||||
// });
|
||||
initFeatureOrder();
|
||||
|
||||
Set<Holder<PlacedFeature>> featuresWithoutDuplicates = Sets.newHashSet();
|
||||
features.forEach(holder -> featuresWithoutDuplicates.add(holder));
|
||||
|
||||
if (featuresWithoutDuplicates.size() != features.size()) {
|
||||
features.clear();
|
||||
featuresWithoutDuplicates.forEach(feature -> features.add(feature));
|
||||
}
|
||||
|
||||
features.forEach(feature -> {
|
||||
FEATURE_ORDER.computeIfAbsent(feature, f -> FEATURE_ORDER_ID.getAndIncrement());
|
||||
});
|
||||
|
||||
features.sort((f1, f2) -> {
|
||||
int v1 = FEATURE_ORDER.getOrDefault(f1, 70000);
|
||||
int v2 = FEATURE_ORDER.getOrDefault(f2, 70000);
|
||||
return Integer.compare(v1, v2);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import net.minecraft.world.level.block.Mirror;
|
|||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.Property;
|
||||
import net.minecraft.world.level.material.LavaFluid;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
|
@ -204,10 +205,10 @@ public class BlocksHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static Optional<BlockPos> findSurface(LevelAccessor level,
|
||||
BlockPos startPos,
|
||||
int minY,
|
||||
Predicate<BlockState> surface) {
|
||||
public static Optional<BlockPos> findSurfaceBelow(LevelAccessor level,
|
||||
BlockPos startPos,
|
||||
int minY,
|
||||
Predicate<BlockState> surface) {
|
||||
final MutableBlockPos POS = new MutableBlockPos(startPos.getX(), startPos.getY(), startPos.getZ());
|
||||
for (int y = startPos.getY(); y >= minY; y--) {
|
||||
POS.setY(y);
|
||||
|
@ -215,4 +216,36 @@ public class BlocksHelper {
|
|||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public static boolean findSurface(LevelAccessor level,
|
||||
MutableBlockPos startPos,
|
||||
Direction dir,
|
||||
int length,
|
||||
Predicate<BlockState> surface) {
|
||||
for (int len = 0; len < length; len++) {
|
||||
if (surface.test(level.getBlockState(startPos))) return true;
|
||||
startPos.move(dir, 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isFreeSpace(LevelAccessor level,
|
||||
BlockPos startPos,
|
||||
Direction dir,
|
||||
int length,
|
||||
Predicate<BlockState> freeSurface) {
|
||||
MutableBlockPos POS = startPos.mutable();
|
||||
for (int len = 1; len < length; len++) {
|
||||
POS.move(dir, 1);
|
||||
if (!freeSurface.test(level.getBlockState(POS))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isLava(BlockState state) {
|
||||
return state.getFluidState().getType() instanceof LavaFluid;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,18 +55,20 @@ public class BCLFeature {
|
|||
Holder<ConfiguredFeature<?, ?>> configuredFeature;
|
||||
if (!BuiltinRegistries.CONFIGURED_FEATURE.containsKey(id)) {
|
||||
configuredFeature = (Holder<ConfiguredFeature<?, ?>>) (Object) FeatureUtils.register(id.toString(),
|
||||
feature,
|
||||
configuration);
|
||||
feature,
|
||||
configuration);
|
||||
} else {
|
||||
configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(),
|
||||
id)).orElseThrow();
|
||||
configuredFeature = BuiltinRegistries.CONFIGURED_FEATURE
|
||||
.getHolder(ResourceKey.create(BuiltinRegistries.CONFIGURED_FEATURE.key(),
|
||||
id))
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
if (!BuiltinRegistries.PLACED_FEATURE.containsKey(id)) {
|
||||
return PlacementUtils.register(id.toString(), configuredFeature, modifiers);
|
||||
} else {
|
||||
return BuiltinRegistries.PLACED_FEATURE.getHolder(ResourceKey.create(BuiltinRegistries.PLACED_FEATURE.key(),
|
||||
id)).orElseThrow();
|
||||
id)).orElseThrow();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +81,10 @@ public class BCLFeature {
|
|||
return optional.isPresent();
|
||||
}
|
||||
|
||||
public static <C extends FeatureConfiguration, F extends Feature<C>> F register(String string, F feature) {
|
||||
return Registry.register(Registry.FEATURE, string, feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw feature.
|
||||
*
|
||||
|
|
|
@ -32,14 +32,14 @@ public abstract class SurfaceFeature<T extends FeatureConfiguration> extends Fea
|
|||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<T> ctx) {
|
||||
Optional<BlockPos> pos = BlocksHelper.findSurface(ctx.level(),
|
||||
Optional<BlockPos> pos = BlocksHelper.findSurfaceBelow(ctx.level(),
|
||||
ctx.origin(),
|
||||
minHeight(ctx),
|
||||
this::isValidSurface);
|
||||
if (pos.isPresent()) {
|
||||
int y2 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE_WG, ctx.origin().getX(), ctx.origin().getZ());
|
||||
int y3 = ctx.level().getHeight(Heightmap.Types.WORLD_SURFACE, ctx.origin().getX(), ctx.origin().getZ());
|
||||
System.out.println("Surfaces:" + pos.get().getY() + ", " + y2 + ", " + y3);
|
||||
System.out.println("Surfaces:" + pos.get().getY() + ", " + y2 + ", " + y3 + ", " + ctx.origin().getY());
|
||||
|
||||
generate(pos.get(), ctx);
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package org.betterx.bclib.world.features;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep;
|
||||
import net.minecraft.world.level.levelgen.blockpredicates.BlockPredicate;
|
||||
import net.minecraft.world.level.levelgen.feature.Feature;
|
||||
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
|
||||
import net.minecraft.world.level.levelgen.placement.BiomeFilter;
|
||||
import net.minecraft.world.level.levelgen.placement.EnvironmentScanPlacement;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import org.betterx.bclib.api.features.BCLFeatureBuilder;
|
||||
|
||||
public class TemplateFeature<FC extends TemplateFeatureConfig> extends Feature<FC> {
|
||||
public static final Feature<TemplateFeatureConfig> INSTANCE = BCLFeature.register("template",
|
||||
new TemplateFeature(TemplateFeatureConfig.CODEC));
|
||||
|
||||
public static <T extends TemplateFeatureConfig> BCLFeature createAndRegister(TemplateFeatureConfig configuration,
|
||||
int onveEveryChunk) {
|
||||
return BCLFeatureBuilder
|
||||
.start(new ResourceLocation(configuration.structure.location.getNamespace(),
|
||||
"feature_" + configuration.structure.location.getPath()), INSTANCE)
|
||||
.decoration(GenerationStep.Decoration.SURFACE_STRUCTURES)
|
||||
.oncePerChunks(onveEveryChunk)
|
||||
.squarePlacement()
|
||||
.distanceToTopAndBottom10()
|
||||
.modifier(EnvironmentScanPlacement.scanningFor(Direction.DOWN,
|
||||
BlockPredicate.solid(),
|
||||
BlockPredicate.matchesBlocks(Blocks.AIR, Blocks.WATER, Blocks.LAVA),
|
||||
12))
|
||||
.modifier(BiomeFilter.biome())
|
||||
.buildAndRegister(configuration);
|
||||
}
|
||||
|
||||
public TemplateFeature(Codec<FC> codec) {
|
||||
super(codec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean place(FeaturePlaceContext<FC> ctx) {
|
||||
return ctx.config().structure.generate(ctx.level(), ctx.origin(), ctx.random());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package org.betterx.bclib.world.features;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import org.betterx.bclib.world.structures.StructurePlacementType;
|
||||
import org.betterx.bclib.world.structures.StructureWorldNBT;
|
||||
|
||||
public class TemplateFeatureConfig implements FeatureConfiguration {
|
||||
public static final Codec<TemplateFeatureConfig> CODEC = RecordCodecBuilder.create((instance) -> instance
|
||||
.group(ResourceLocation.CODEC
|
||||
.fieldOf("location")
|
||||
.forGetter((TemplateFeatureConfig cfg) -> cfg.structure.location),
|
||||
|
||||
Codec
|
||||
.INT
|
||||
.fieldOf("offset_y")
|
||||
.orElse(0)
|
||||
.forGetter((TemplateFeatureConfig cfg) -> cfg.structure.offsetY),
|
||||
|
||||
StructurePlacementType.CODEC
|
||||
.fieldOf("placement")
|
||||
.orElse(StructurePlacementType.FLOOR)
|
||||
.forGetter((TemplateFeatureConfig cfg) -> cfg.structure.type)
|
||||
)
|
||||
.apply(instance, TemplateFeatureConfig::new)
|
||||
);
|
||||
|
||||
public final StructureWorldNBT structure;
|
||||
|
||||
public TemplateFeatureConfig(ResourceLocation location, int offsetY, StructurePlacementType type) {
|
||||
structure = new StructureWorldNBT(location, offsetY, type);
|
||||
}
|
||||
}
|
|
@ -59,7 +59,8 @@ public class BCLStructure<S extends Structure> {
|
|||
return structure(tagKey, Map.of(), decoration, terrainAdjustment);
|
||||
}
|
||||
|
||||
private static <S extends Structure> StructureType<S> registerStructureType(ResourceLocation id, Codec<S> codec) {
|
||||
private static <S extends Structure> StructureType<S> registerStructureType(ResourceLocation id,
|
||||
Codec<S> codec) {
|
||||
return Registry.register(Registry.STRUCTURE_TYPES, id, () -> codec);
|
||||
}
|
||||
|
||||
|
@ -93,9 +94,9 @@ public class BCLStructure<S extends Structure> {
|
|||
//
|
||||
|
||||
this.spreadConfig = new RandomSpreadStructurePlacement(spacing,
|
||||
separation,
|
||||
RandomSpreadType.LINEAR,
|
||||
RANDOM.nextInt(8192));
|
||||
separation,
|
||||
RandomSpreadType.LINEAR,
|
||||
RANDOM.nextInt(8192));
|
||||
this.structureKey = ResourceKey.create(Registry.STRUCTURE_REGISTRY, id);
|
||||
this.structureSetKey = ResourceKey.create(Registry.STRUCTURE_SET_REGISTRY, id);
|
||||
this.structureType = registerStructureType(id, STRUCTURE_CODEC);
|
||||
|
@ -136,8 +137,8 @@ public class BCLStructure<S extends Structure> {
|
|||
QuartPos.fromBlock(blockPos.getY()),
|
||||
QuartPos.fromBlock(blockPos.getZ()),
|
||||
context.randomState().sampler()
|
||||
)
|
||||
);
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public Holder<Structure> getStructure() {
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
package org.betterx.bclib.world.structures;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.BlockPos.MutableBlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||
|
||||
import org.betterx.bclib.BCLib;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class StructureNBT {
|
||||
public final ResourceLocation location;
|
||||
protected StructureTemplate structure;
|
||||
protected Mirror mirror = Mirror.NONE;
|
||||
protected Rotation rotation = Rotation.NONE;
|
||||
|
||||
public StructureNBT(ResourceLocation location) {
|
||||
this.location = location;
|
||||
this.structure = readStructureFromJar(location);
|
||||
}
|
||||
|
||||
protected StructureNBT(ResourceLocation location, StructureTemplate structure) {
|
||||
this.location = location;
|
||||
this.structure = structure;
|
||||
}
|
||||
|
||||
public StructureNBT setRotation(Rotation rotation) {
|
||||
this.rotation = rotation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Mirror getMirror() {
|
||||
return mirror;
|
||||
}
|
||||
|
||||
public StructureNBT setMirror(Mirror mirror) {
|
||||
this.mirror = mirror;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void randomRM(RandomSource random) {
|
||||
rotation = Rotation.values()[random.nextInt(4)];
|
||||
mirror = Mirror.values()[random.nextInt(3)];
|
||||
}
|
||||
|
||||
public boolean generateCentered(ServerLevelAccessor world, BlockPos pos) {
|
||||
if (structure == null) {
|
||||
BCLib.LOGGER.error("No structure: " + location.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
MutableBlockPos blockpos2 = new MutableBlockPos().set(structure.getSize());
|
||||
if (this.mirror == Mirror.FRONT_BACK)
|
||||
blockpos2.setX(-blockpos2.getX());
|
||||
if (this.mirror == Mirror.LEFT_RIGHT)
|
||||
blockpos2.setZ(-blockpos2.getZ());
|
||||
blockpos2.set(blockpos2.rotate(rotation));
|
||||
StructurePlaceSettings data = new StructurePlaceSettings().setRotation(this.rotation).setMirror(this.mirror);
|
||||
BlockPos newPos = pos.offset(-blockpos2.getX() >> 1, 0, -blockpos2.getZ() >> 1);
|
||||
structure.placeInWorld(
|
||||
world,
|
||||
newPos,
|
||||
newPos,
|
||||
data,
|
||||
world.getRandom(),
|
||||
Block.UPDATE_CLIENTS
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static StructureTemplate readStructureFromJar(ResourceLocation resource) {
|
||||
String ns = resource.getNamespace();
|
||||
String nm = resource.getPath();
|
||||
|
||||
try {
|
||||
InputStream inputstream = MinecraftServer.class.getResourceAsStream("/data/" + ns + "/structures/" + nm + ".nbt");
|
||||
return readStructureFromStream(inputstream);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static StructureTemplate readStructureFromStream(InputStream stream) throws IOException {
|
||||
CompoundTag nbttagcompound = NbtIo.readCompressed(stream);
|
||||
|
||||
StructureTemplate template = new StructureTemplate();
|
||||
template.load(nbttagcompound);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
public BlockPos getSize() {
|
||||
if (rotation == Rotation.NONE || rotation == Rotation.CLOCKWISE_180)
|
||||
return new BlockPos(structure.getSize());
|
||||
else {
|
||||
Vec3i size = structure.getSize();
|
||||
int x = size.getX();
|
||||
int z = size.getZ();
|
||||
return new BlockPos(z, size.getY(), x);
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return location.getPath();
|
||||
}
|
||||
|
||||
public BoundingBox getBoundingBox(BlockPos pos) {
|
||||
return structure.getBoundingBox(new StructurePlaceSettings().setRotation(this.rotation).setMirror(mirror), pos);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package org.betterx.bclib.world.structures;
|
||||
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
|
||||
public enum StructurePlacementType implements StringRepresentable {
|
||||
FLOOR, WALL, CEIL, LAVA, UNDER;
|
||||
|
||||
public static final Codec<StructurePlacementType> CODEC = StringRepresentable.fromEnum(StructurePlacementType::values);
|
||||
|
||||
public String getName() {
|
||||
return this.getSerializedName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerializedName() {
|
||||
return this.name().toLowerCase();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package org.betterx.bclib.world.structures;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.BlockPos.MutableBlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
|
||||
import org.betterx.bclib.util.BlocksHelper;
|
||||
|
||||
public class StructureWorldNBT extends StructureNBT {
|
||||
public final StructurePlacementType type;
|
||||
public final int offsetY;
|
||||
|
||||
public StructureWorldNBT(ResourceLocation location, int offsetY, StructurePlacementType type) {
|
||||
super(location);
|
||||
this.offsetY = offsetY;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
public boolean generate(ServerLevelAccessor level, BlockPos pos, RandomSource random) {
|
||||
randomRM(random);
|
||||
if (canGenerate(level, pos)) {
|
||||
return generateCentered(level, pos.above(offsetY));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean canGenerate(LevelAccessor world, BlockPos pos) {
|
||||
if (type == StructurePlacementType.FLOOR)
|
||||
return canGenerateFloor(world, pos);
|
||||
else if (type == StructurePlacementType.LAVA)
|
||||
return canGenerateLava(world, pos);
|
||||
else if (type == StructurePlacementType.UNDER)
|
||||
return canGenerateUnder(world, pos);
|
||||
else if (type == StructurePlacementType.CEIL)
|
||||
return canGenerateCeil(world, pos);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean containsBedrock(LevelAccessor level, BlockPos startPos) {
|
||||
for (int i = 0; i < this.structure.getSize().getY(); i += 2) {
|
||||
if (level.getBlockState(startPos.above(i)).is(Blocks.BEDROCK)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean canGenerateFloor(LevelAccessor world, BlockPos pos) {
|
||||
if (containsBedrock(world, pos)) return false;
|
||||
|
||||
return getAirFraction(world, pos) > 0.6 && getAirFractionFoundation(world, pos) < 0.5;
|
||||
}
|
||||
|
||||
protected boolean canGenerateLava(LevelAccessor world, BlockPos pos) {
|
||||
if (containsBedrock(world, pos)) return false;
|
||||
|
||||
return getLavaFractionFoundation(world, pos) > 0.9 && getAirFraction(world, pos) > 0.9;
|
||||
}
|
||||
|
||||
protected boolean canGenerateUnder(LevelAccessor world, BlockPos pos) {
|
||||
if (containsBedrock(world, pos)) return false;
|
||||
|
||||
return getAirFraction(world, pos) < 0.2;
|
||||
}
|
||||
|
||||
protected boolean canGenerateCeil(LevelAccessor world, BlockPos pos) {
|
||||
if (containsBedrock(world, pos)) return false;
|
||||
|
||||
return getAirFractionBottom(world, pos) > 0.8 && getAirFraction(world, pos) < 0.6;
|
||||
}
|
||||
|
||||
protected float getAirFraction(LevelAccessor world, BlockPos pos) {
|
||||
final MutableBlockPos POS = new MutableBlockPos();
|
||||
int airCount = 0;
|
||||
|
||||
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
|
||||
size.setX(Math.abs(size.getX()));
|
||||
size.setZ(Math.abs(size.getZ()));
|
||||
|
||||
BlockPos start = pos.offset(-(size.getX() >> 1), 0, -(size.getZ() >> 1));
|
||||
BlockPos end = pos.offset(size.getX() >> 1, size.getY() + offsetY, size.getZ() >> 1);
|
||||
int count = 0;
|
||||
|
||||
for (int x = start.getX(); x <= end.getX(); x++) {
|
||||
POS.setX(x);
|
||||
for (int y = start.getY(); y <= end.getY(); y++) {
|
||||
POS.setY(y);
|
||||
for (int z = start.getZ(); z <= end.getZ(); z++) {
|
||||
POS.setZ(z);
|
||||
if (world.isEmptyBlock(POS))
|
||||
airCount++;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (float) airCount / count;
|
||||
}
|
||||
|
||||
private float getLavaFractionFoundation(LevelAccessor world, BlockPos pos) {
|
||||
final MutableBlockPos POS = new MutableBlockPos();
|
||||
int lavaCount = 0;
|
||||
|
||||
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
|
||||
size.setX(Math.abs(size.getX()));
|
||||
size.setZ(Math.abs(size.getZ()));
|
||||
|
||||
BlockPos start = pos.offset(-(size.getX() >> 1), 0, -(size.getZ() >> 1));
|
||||
BlockPos end = pos.offset(size.getX() >> 1, 0, size.getZ() >> 1);
|
||||
int count = 0;
|
||||
|
||||
POS.setY(pos.getY() - 1);
|
||||
for (int x = start.getX(); x <= end.getX(); x++) {
|
||||
POS.setX(x);
|
||||
for (int z = start.getZ(); z <= end.getZ(); z++) {
|
||||
POS.setZ(z);
|
||||
|
||||
if (BlocksHelper.isLava(world.getBlockState(POS)))
|
||||
lavaCount++;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return (float) lavaCount / count;
|
||||
}
|
||||
|
||||
private float getAirFractionFoundation(LevelAccessor world, BlockPos pos) {
|
||||
final MutableBlockPos POS = new MutableBlockPos();
|
||||
int airCount = 0;
|
||||
|
||||
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
|
||||
size.setX(Math.abs(size.getX()));
|
||||
size.setZ(Math.abs(size.getZ()));
|
||||
|
||||
BlockPos start = pos.offset(-(size.getX() >> 1), -1, -(size.getZ() >> 1));
|
||||
BlockPos end = pos.offset(size.getX() >> 1, 0, size.getZ() >> 1);
|
||||
int count = 0;
|
||||
|
||||
for (int x = start.getX(); x <= end.getX(); x++) {
|
||||
POS.setX(x);
|
||||
for (int y = start.getY(); y <= end.getY(); y++) {
|
||||
POS.setY(y);
|
||||
for (int z = start.getZ(); z <= end.getZ(); z++) {
|
||||
POS.setZ(z);
|
||||
if (world.getBlockState(POS).getMaterial().isReplaceable())
|
||||
airCount++;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (float) airCount / count;
|
||||
}
|
||||
|
||||
private float getAirFractionBottom(LevelAccessor world, BlockPos pos) {
|
||||
final MutableBlockPos POS = new MutableBlockPos();
|
||||
int airCount = 0;
|
||||
|
||||
MutableBlockPos size = new MutableBlockPos().set(new BlockPos(structure.getSize()).rotate(rotation));
|
||||
size.setX(Math.abs(size.getX()));
|
||||
size.setZ(Math.abs(size.getZ()));
|
||||
|
||||
float y1 = Math.min(offsetY, 0);
|
||||
float y2 = Math.max(offsetY, 0);
|
||||
BlockPos start = pos.offset(-(size.getX() >> 1), y1, -(size.getZ() >> 1));
|
||||
BlockPos end = pos.offset(size.getX() >> 1, y2, size.getZ() >> 1);
|
||||
int count = 0;
|
||||
|
||||
for (int x = start.getX(); x <= end.getX(); x++) {
|
||||
POS.setX(x);
|
||||
for (int y = start.getY(); y <= end.getY(); y++) {
|
||||
POS.setY(y);
|
||||
for (int z = start.getZ(); z <= end.getZ(); z++) {
|
||||
POS.setZ(z);
|
||||
if (world.getBlockState(POS).getMaterial().isReplaceable())
|
||||
airCount++;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (float) airCount / count;
|
||||
}
|
||||
|
||||
public boolean loaded() {
|
||||
return structure != null;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue