Backportat latest changes from master
This commit is contained in:
parent
9183293329
commit
53911dfe91
63 changed files with 1002 additions and 351 deletions
|
@ -1,13 +1,17 @@
|
|||
package ru.bclib.api;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
||||
import net.fabricmc.fabric.impl.biome.NetherBiomeData;
|
||||
import net.fabricmc.fabric.impl.biome.TheEndBiomeData;
|
||||
import net.fabricmc.fabric.mixin.biome.modification.GenerationSettingsAccessor;
|
||||
import net.fabricmc.fabric.mixin.biome.modification.SpawnSettingsAccessor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.data.BuiltinRegistries;
|
||||
|
@ -15,11 +19,16 @@ import net.minecraft.resources.ResourceKey;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.random.WeightedRandomList;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biome.ClimateParameters;
|
||||
import net.minecraft.world.biome.Biome.ClimateParameters;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;
|
||||
import net.minecraft.world.level.levelgen.GenerationStep.Decoration;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
|
||||
|
@ -31,6 +40,8 @@ import ru.bclib.world.features.BCLFeature;
|
|||
import ru.bclib.world.generator.BiomePicker;
|
||||
import ru.bclib.world.structures.BCLStructureFeature;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
@ -114,8 +125,8 @@ public class BiomeAPI {
|
|||
registerBiome(biome);
|
||||
NETHER_BIOME_PICKER.addBiome(biome);
|
||||
Random random = new Random(biome.getID().hashCode());
|
||||
//TODO: (1.18) did they add depth and scale as two new params here???
|
||||
Climate.ParameterPoint parameters = Climate.parameters(
|
||||
//TODO: (1.18) did they add depth and scale as two new params here???
|
||||
ClimateParameters parameters = new ClimateParameters(
|
||||
MHelper.randRange(-1.5F, 1.5F, random),
|
||||
MHelper.randRange(-1.5F, 1.5F, random),
|
||||
MHelper.randRange(-1.5F, 1.5F, random),
|
||||
|
@ -396,7 +407,10 @@ public class BiomeAPI {
|
|||
biomes.forEach(biome -> {
|
||||
ResourceLocation biomeID = getBiomeID(biome);
|
||||
boolean modify = isDatapackBiome(biomeID);
|
||||
if (!modify && !MODIFIED_BIOMES.contains(biomeID)) {
|
||||
if (biome != BuiltinRegistries.BIOME.get(biomeID)) {
|
||||
modify = true;
|
||||
}
|
||||
else if (!modify && !MODIFIED_BIOMES.contains(biomeID)) {
|
||||
MODIFIED_BIOMES.add(biomeID);
|
||||
modify = true;
|
||||
}
|
||||
|
@ -417,13 +431,7 @@ public class BiomeAPI {
|
|||
public static void addBiomeFeature(Biome biome, ConfiguredFeature feature, Decoration step) {
|
||||
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
|
||||
List<List<Supplier<ConfiguredFeature<?, ?>>>> biomeFeatures = getMutableList(accessor.fabric_getFeatures());
|
||||
int index = step.ordinal();
|
||||
if (biomeFeatures.size() < index) {
|
||||
for (int i = biomeFeatures.size(); i <= index; i++) {
|
||||
biomeFeatures.add(Lists.newArrayList());
|
||||
}
|
||||
}
|
||||
List<Supplier<ConfiguredFeature<?, ?>>> list = getMutableList(biomeFeatures.get(index));
|
||||
List<Supplier<ConfiguredFeature<?, ?>>> list = getList(step, biomeFeatures);
|
||||
list.add(() -> feature);
|
||||
accessor.fabric_setFeatures(biomeFeatures);
|
||||
}
|
||||
|
@ -437,18 +445,30 @@ public class BiomeAPI {
|
|||
GenerationSettingsAccessor accessor = (GenerationSettingsAccessor) biome.getGenerationSettings();
|
||||
List<List<Supplier<ConfiguredFeature<?, ?>>>> biomeFeatures = getMutableList(accessor.fabric_getFeatures());
|
||||
for (BCLFeature feature: features) {
|
||||
int index = feature.getFeatureStep().ordinal();
|
||||
if (biomeFeatures.size() < index) {
|
||||
for (int i = biomeFeatures.size(); i <= index; i++) {
|
||||
biomeFeatures.add(Lists.newArrayList());
|
||||
}
|
||||
}
|
||||
List<Supplier<ConfiguredFeature<?, ?>>> list = getMutableList(biomeFeatures.get(index));
|
||||
List<Supplier<ConfiguredFeature<?, ?>>> list = getList(feature.getFeatureStep(), biomeFeatures);
|
||||
list.add(feature::getFeatureConfigured);
|
||||
}
|
||||
accessor.fabric_setFeatures(biomeFeatures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for correct feature list from all biome feature list of lists.
|
||||
* @param step feature {@link Decoration} step.
|
||||
* @param lists biome accessor lists.
|
||||
* @return mutable {@link ConfiguredFeature} list.
|
||||
*/
|
||||
private static List<Supplier<ConfiguredFeature<?, ?>>> getList(Decoration step, List<List<Supplier<ConfiguredFeature<?, ?>>>> lists) {
|
||||
int index = step.ordinal();
|
||||
if (lists.size() <= index) {
|
||||
for (int i = lists.size(); i <= index; i++) {
|
||||
lists.add(Lists.newArrayList());
|
||||
}
|
||||
}
|
||||
List<Supplier<ConfiguredFeature<?, ?>>> list = getMutableList(lists.get(index));
|
||||
lists.set(index, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new structure feature to existing biome.
|
||||
* @param biome {@link Biome} to add structure feature in.
|
||||
|
@ -475,10 +495,41 @@ public class BiomeAPI {
|
|||
accessor.fabric_setStructureFeatures(biomeStructures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds mob spawning to specified biome.
|
||||
* @param biome {@link Biome} to add mob spawning.
|
||||
* @param entityType {@link EntityType} mob type.
|
||||
* @param weight spawn weight.
|
||||
* @param minGroupCount minimum mobs in group.
|
||||
* @param maxGroupCount maximum mobs in group.
|
||||
*/
|
||||
public static <M extends Mob> void addBiomeMobSpawn(Biome biome, EntityType<M> entityType, int weight, int minGroupCount, int maxGroupCount) {
|
||||
MobCategory category = entityType.getCategory();
|
||||
SpawnSettingsAccessor accessor = (SpawnSettingsAccessor) biome.getMobSettings();
|
||||
Map<MobCategory, WeightedRandomList<SpawnerData>> spawners = getMutableMap(accessor.fabric_getSpawners());
|
||||
List<SpawnerData> mobs = spawners.containsKey(category) ? getMutableList(spawners.get(category).unwrap()) : Lists.newArrayList();
|
||||
mobs.add(new SpawnerData(entityType, weight, minGroupCount, maxGroupCount));
|
||||
spawners.put(category, WeightedRandomList.create(mobs));
|
||||
accessor.fabric_setSpawners(spawners);
|
||||
}
|
||||
|
||||
private static <T extends Object> List<T> getMutableList(List<T> input) {
|
||||
if (input instanceof ImmutableList) {
|
||||
if (input!=null) {
|
||||
System.out.println("getMutableList: " + input.getClass().getName());
|
||||
for (Class cl : input.getClass().getInterfaces()){
|
||||
System.out.println(" - " + cl.getName());
|
||||
}
|
||||
}
|
||||
if (/*input instanceof ImmutableList ||*/ !(input instanceof ArrayList || input instanceof LinkedList)) {
|
||||
return Lists.newArrayList(input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
private static <K extends Object, V extends Object> Map<K, V> getMutableMap(Map<K, V> input) {
|
||||
if (input instanceof ImmutableMap) {
|
||||
return Maps.newHashMap(input);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
|
22
src/main/java/ru/bclib/api/ComposterAPI.java
Normal file
22
src/main/java/ru/bclib/api/ComposterAPI.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package ru.bclib.api;
|
||||
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import ru.bclib.mixin.common.ComposterBlockAccessor;
|
||||
|
||||
public class ComposterAPI {
|
||||
public static Block allowCompost(float chance, Block block){
|
||||
if (block != null) {
|
||||
allowCompost(chance, block.asItem());
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
public static Item allowCompost(float chance, Item item){
|
||||
if (item != null && item != Items.AIR) {
|
||||
ComposterBlockAccessor.callAdd(chance, item);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
120
src/main/java/ru/bclib/api/LifeCycleAPI.java
Normal file
120
src/main/java/ru/bclib/api/LifeCycleAPI.java
Normal file
|
@ -0,0 +1,120 @@
|
|||
package ru.bclib.api;
|
||||
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.world.level.CustomSpawner;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* provides some lifetime hooks for a Minecraft instance
|
||||
*/
|
||||
public class LifeCycleAPI {
|
||||
private final static List<LevelLoadBiomesCall> onLoadLevelBiomes = new ArrayList<>(2);
|
||||
private final static List<LevelLoadCall> onLoadLevel = new ArrayList<>(2);
|
||||
|
||||
/**
|
||||
* A callback function that is used for each new ServerLevel instance
|
||||
*/
|
||||
public interface LevelLoadBiomesCall {
|
||||
void onLoad(ServerLevel world, long seed, Registry<Biome> registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback function that is used for each new ServerLevel instance
|
||||
*/
|
||||
public interface LevelLoadCall {
|
||||
void onLoad(
|
||||
ServerLevel world,
|
||||
MinecraftServer minecraftServer,
|
||||
Executor executor,
|
||||
LevelStorageSource.LevelStorageAccess levelStorageAccess,
|
||||
ServerLevelData serverLevelData,
|
||||
ResourceKey<Level> resourceKey,
|
||||
DimensionType dimensionType,
|
||||
ChunkProgressListener chunkProgressListener,
|
||||
ChunkGenerator chunkGenerator,
|
||||
boolean bl,
|
||||
long l,
|
||||
List<CustomSpawner> list,
|
||||
boolean bl2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback that is called when a new {@code ServerLevel is instantiated}.
|
||||
* This callback will receive the world seed as well as it's biome registry.
|
||||
* @param call The calbback Method
|
||||
*/
|
||||
public static void onLevelLoad(LevelLoadBiomesCall call){
|
||||
onLoadLevelBiomes.add(call);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback that is called when a new {@code ServerLevel is instantiated}.
|
||||
* This callbacl will receiv all parameters that were passed to the ServerLevel's constructor
|
||||
* @param call The calbback Method
|
||||
*/
|
||||
public static void onLevelLoad(LevelLoadCall call){
|
||||
onLoadLevel.add(call);
|
||||
}
|
||||
|
||||
/**
|
||||
* For internal use, You should not call this method!
|
||||
* @param minecraftServer
|
||||
* @param executor
|
||||
* @param levelStorageAccess
|
||||
* @param serverLevelData
|
||||
* @param resourceKey
|
||||
* @param dimensionType
|
||||
* @param chunkProgressListener
|
||||
* @param chunkGenerator
|
||||
* @param bl
|
||||
* @param l
|
||||
* @param list
|
||||
* @param bl2
|
||||
*/
|
||||
public static void _runLevelLoad(ServerLevel world,
|
||||
MinecraftServer minecraftServer,
|
||||
Executor executor,
|
||||
LevelStorageSource.LevelStorageAccess levelStorageAccess,
|
||||
ServerLevelData serverLevelData,
|
||||
ResourceKey<Level> resourceKey,
|
||||
DimensionType dimensionType,
|
||||
ChunkProgressListener chunkProgressListener,
|
||||
ChunkGenerator chunkGenerator,
|
||||
boolean bl,
|
||||
long l,
|
||||
List<CustomSpawner> list,
|
||||
boolean bl2){
|
||||
onLoadLevel.forEach(c -> c.onLoad(
|
||||
world,
|
||||
minecraftServer,
|
||||
executor,
|
||||
levelStorageAccess,
|
||||
serverLevelData,
|
||||
resourceKey,
|
||||
dimensionType,
|
||||
chunkProgressListener,
|
||||
chunkGenerator,
|
||||
bl,
|
||||
l,
|
||||
list,
|
||||
bl2)
|
||||
);
|
||||
|
||||
final long seed = world.getSeed();
|
||||
final Registry<Biome> biomeRegistry = world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
onLoadLevelBiomes.forEach(c -> c.onLoad(world, seed, biomeRegistry));
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ import ru.bclib.util.Logger;
|
|||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -570,6 +571,9 @@ public class DataFixerAPI {
|
|||
private static List<File> getAllPlayers(File dir) {
|
||||
List<File> list = new ArrayList<>();
|
||||
dir = new File(dir, "playerdata");
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
return list;
|
||||
}
|
||||
for (File file : dir.listFiles()) {
|
||||
if (file.isFile() && file.getName().endsWith(".dat")) {
|
||||
list.add(file);
|
||||
|
@ -604,7 +608,7 @@ public class DataFixerAPI {
|
|||
private static CompoundTag readNbt(File file) throws IOException {
|
||||
try {
|
||||
return NbtIo.readCompressed(file);
|
||||
} catch (ZipException e){
|
||||
} catch (ZipException | EOFException e){
|
||||
return NbtIo.read(file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package ru.bclib.api.datafixer;
|
|||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import ru.bclib.interfaces.PatchBiFunction;
|
||||
import ru.bclib.interfaces.PatchFunction;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
|
@ -7,6 +7,8 @@ import net.minecraft.nbt.Tag;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import ru.bclib.BCLib;
|
||||
import ru.bclib.api.WorldDataAPI;
|
||||
import ru.bclib.interfaces.PatchBiFunction;
|
||||
import ru.bclib.interfaces.PatchFunction;
|
||||
import ru.bclib.util.ModUtil;
|
||||
|
||||
import java.io.File;
|
||||
|
|
|
@ -3,6 +3,8 @@ package ru.bclib.api.datafixer;
|
|||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import ru.bclib.interfaces.PatchBiFunction;
|
||||
import ru.bclib.interfaces.PatchFunction;
|
||||
import ru.bclib.util.ModUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
package ru.bclib.api.datafixer;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PatchBiFunction<U, V, R> {
|
||||
R apply(U t, V v, MigrationProfile profile) throws PatchDidiFailException;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package ru.bclib.api.datafixer;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PatchFunction<T, R> {
|
||||
R apply(T t, MigrationProfile profile) throws PatchDidiFailException;
|
||||
}
|
308
src/main/java/ru/bclib/api/spawning/SpawnRuleBuilder.java
Normal file
308
src/main/java/ru/bclib/api/spawning/SpawnRuleBuilder.java
Normal file
|
@ -0,0 +1,308 @@
|
|||
package ru.bclib.api.spawning;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import net.fabricmc.fabric.mixin.object.builder.SpawnRestrictionAccessor;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.Difficulty;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.SpawnPlacements.SpawnPredicate;
|
||||
import net.minecraft.world.entity.SpawnPlacements.Type;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.levelgen.Heightmap.Types;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import ru.bclib.interfaces.SpawnRule;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SpawnRuleBuilder<M extends Mob> {
|
||||
private static final Map<String, SpawnRuleEntry> RULES_CACHE = Maps.newHashMap();
|
||||
private static final SpawnRuleBuilder INSTANCE = new SpawnRuleBuilder();
|
||||
private List<SpawnRuleEntry> rules = Lists.newArrayList();
|
||||
private SpawnRuleEntry entryInstance;
|
||||
private EntityType<M> entityType;
|
||||
|
||||
private SpawnRuleBuilder() {}
|
||||
|
||||
/**
|
||||
* Starts new rule building process.
|
||||
* @return prepared {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public static SpawnRuleBuilder start(EntityType<? extends Mob> entityType) {
|
||||
INSTANCE.entityType = entityType;
|
||||
INSTANCE.rules.clear();
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop entity spawn on peaceful {@link Difficulty}
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder notPeaceful() {
|
||||
entryInstance = getFromCache("not_peaceful", () -> {
|
||||
return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> world.getDifficulty() != Difficulty.PEACEFUL);
|
||||
});
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts entity spawn above world surface (flying mobs).
|
||||
* @param minHeight minimal spawn height.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder aboveGround(int minHeight) {
|
||||
entryInstance = getFromCache("above_ground", () -> {
|
||||
return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> {
|
||||
if (pos.getY() < world.getMinBuildHeight() + 2) {
|
||||
return false;
|
||||
}
|
||||
return pos.getY() > world.getHeight(Types.WORLD_SURFACE, pos.getX(), pos.getZ()) + minHeight;
|
||||
});
|
||||
});
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts entity spawn below world logical height (useful for Nether mobs).
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder belowMaxHeight() {
|
||||
entryInstance = getFromCache("below_max_height", () -> {
|
||||
return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> pos.getY() < world.dimensionType().logicalHeight());
|
||||
});
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts spawning only to vanilla valid blocks.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder onlyOnValidBlocks() {
|
||||
entryInstance = getFromCache("only_on_valid_blocks", () -> {
|
||||
return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> {
|
||||
BlockPos below = pos.below();
|
||||
return world.getBlockState(below).isValidSpawn(world, below, type);
|
||||
});
|
||||
});
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restricts spawning only to specified blocks.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder onlyOnBlocks(Block... blocks) {
|
||||
final Block[] floorBlocks = blocks;
|
||||
Arrays.sort(floorBlocks, Comparator.comparing(Block::getDescriptionId));
|
||||
|
||||
StringBuilder builder = new StringBuilder("only_on_blocks");
|
||||
for (Block block : floorBlocks) {
|
||||
builder.append('_');
|
||||
builder.append(block.getDescriptionId());
|
||||
}
|
||||
|
||||
entryInstance = getFromCache(builder.toString(), () -> {
|
||||
return new SpawnRuleEntry(0, (type, world, spawnReason, pos, random) -> {
|
||||
Block below = world.getBlockState(pos.below()).getBlock();
|
||||
for (Block floor: floorBlocks) {
|
||||
if (floor == below) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will spawn entity with 1 / chance probability (randomly).
|
||||
* @param chance probability limit.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder withChance(int chance) {
|
||||
entryInstance = getFromCache("with_chance_" + chance, () -> {
|
||||
return new SpawnRuleEntry(1, (type, world, spawnReason, pos, random) -> random.nextInt(chance) == 0);
|
||||
});
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will spawn entity only below specified brightness value.
|
||||
* @param lightLevel light level upper limit.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder belowBrightness(int lightLevel) {
|
||||
entryInstance = getFromCache("below_brightness_" + lightLevel, () -> {
|
||||
return new SpawnRuleEntry(2, (type, world, spawnReason, pos, random) -> world.getMaxLocalRawBrightness(pos) <= lightLevel);
|
||||
});
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will spawn entity only above specified brightness value.
|
||||
* @param lightLevel light level lower limit.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder aboveBrightness(int lightLevel) {
|
||||
entryInstance = getFromCache("above_brightness_" + lightLevel, () -> {
|
||||
return new SpawnRuleEntry(2, (type, world, spawnReason, pos, random) -> world.getMaxLocalRawBrightness(pos) >= lightLevel);
|
||||
});
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity spawn will follow common vanilla spawn rules - spawn in darkness and not on peaceful level.
|
||||
* @param lightLevel light level upper limit.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder hostile(int lightLevel) {
|
||||
return notPeaceful().belowBrightness(lightLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity spawn will follow common vanilla spawn rules - spawn in darkness (below light level 7) and not on peaceful level.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder vanillaHostile() {
|
||||
return hostile(7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will spawn entity only if count of nearby entities will be lower than specified.
|
||||
* @param selectorType selector {@link EntityType} to search.
|
||||
* @param count max entity count.
|
||||
* @param side side of box to search in.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder maxNearby(EntityType<?> selectorType, int count, int side) {
|
||||
final Class<? extends Entity> baseClass = selectorType.getBaseClass();
|
||||
entryInstance = getFromCache("max_nearby_" + selectorType.getDescriptionId()+"_"+count+"_"+side, () -> {
|
||||
return new SpawnRuleEntry(3, (type, world, spawnReason, pos, random) -> {
|
||||
try {
|
||||
final AABB box = new AABB(pos).inflate(side, world.getHeight(), side);
|
||||
final List<?> list = world.getEntitiesOfClass(baseClass, box, (entity) -> true);
|
||||
return list.size() < count;
|
||||
}
|
||||
catch (Exception e) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
rules.add(entryInstance);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will spawn entity only if count of nearby entities with same type will be lower than specified.
|
||||
* @param count max entity count.
|
||||
* @param side side of box to search in.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder maxNearby(int count, int side) {
|
||||
return maxNearby(entityType, count, side);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will spawn entity only if count of nearby entities with same type will be lower than specified.
|
||||
* @param count max entity count.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder maxNearby(int count) {
|
||||
return maxNearby(entityType, count, 256);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to add custom spawning rule for specific entities.
|
||||
* @param rule {@link SpawnRule} rule, can be a lambda expression.
|
||||
* @return same {@link SpawnRuleBuilder} instance.
|
||||
*/
|
||||
public SpawnRuleBuilder customRule(SpawnRule rule) {
|
||||
rules.add(new SpawnRuleEntry(7, rule));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize spawning rule creation.
|
||||
* @param spawnType {@link Type} of spawn.
|
||||
* @param heightmapType {@link Types} heightmap type.
|
||||
*/
|
||||
public void build(Type spawnType, Types heightmapType) {
|
||||
final List<SpawnRuleEntry> rulesCopy = Lists.newArrayList(this.rules);
|
||||
Collections.sort(rulesCopy);
|
||||
|
||||
SpawnPredicate<M> predicate = (entityType, serverLevelAccessor, mobSpawnType, blockPos, random) -> {
|
||||
for (SpawnRuleEntry rule: rulesCopy) {
|
||||
if (!rule.canSpawn(entityType, serverLevelAccessor, mobSpawnType, blockPos, random)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
SpawnRestrictionAccessor.callRegister(entityType, spawnType, heightmapType, predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize spawning rule creation with No Restrictions spawn type, useful for flying entities.
|
||||
* @param heightmapType {@link Types} heightmap type.
|
||||
*/
|
||||
public void buildNoRestrictions(Types heightmapType) {
|
||||
build(Type.NO_RESTRICTIONS, heightmapType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize spawning rule creation with On Ground spawn type, useful for common entities.
|
||||
* @param heightmapType {@link Types} heightmap type.
|
||||
*/
|
||||
public void buildOnGround(Types heightmapType) {
|
||||
build(Type.ON_GROUND, heightmapType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize spawning rule creation with In Water spawn type, useful for water entities.
|
||||
* @param heightmapType {@link Types} heightmap type.
|
||||
*/
|
||||
public void buildInWater(Types heightmapType) {
|
||||
build(Type.IN_WATER, heightmapType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize spawning rule creation with In Lava spawn type, useful for lava entities.
|
||||
* @param heightmapType {@link Types} heightmap type.
|
||||
*/
|
||||
public void buildInLava(Types heightmapType) {
|
||||
build(Type.IN_LAVA, heightmapType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function, will take entry from cache or create it if necessary.
|
||||
* @param name {@link String} entry internal name.
|
||||
* @param supplier {@link Supplier} for {@link SpawnRuleEntry}.
|
||||
* @return new or existing {@link SpawnRuleEntry}.
|
||||
*/
|
||||
private static SpawnRuleEntry getFromCache(String name, Supplier<SpawnRuleEntry> supplier) {
|
||||
SpawnRuleEntry entry = RULES_CACHE.get(name);
|
||||
if (entry == null) {
|
||||
entry = supplier.get();
|
||||
RULES_CACHE.put(name, entry);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
30
src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java
Normal file
30
src/main/java/ru/bclib/api/spawning/SpawnRuleEntry.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package ru.bclib.api.spawning;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import ru.bclib.interfaces.SpawnRule;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class SpawnRuleEntry<M extends Mob> implements Comparable<SpawnRuleEntry> {
|
||||
private final SpawnRule rule;
|
||||
private final byte priority;
|
||||
|
||||
public SpawnRuleEntry(int priority, SpawnRule rule) {
|
||||
this.priority = (byte) priority;
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
boolean canSpawn(EntityType<M> type, LevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
|
||||
return rule.canSpawn(type, world, spawnReason, pos, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull SpawnRuleEntry entry) {
|
||||
return Integer.compare(priority, entry.priority);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue