New mob spawning API

This commit is contained in:
paulevsGitch 2021-11-28 07:15:07 +03:00
parent a55cbe67de
commit acec6a2213
11 changed files with 40 additions and 128 deletions

View file

@ -6,7 +6,7 @@ minecraft_version=1.17.1
yarn_mappings=6 yarn_mappings=6
loader_version=0.12.4 loader_version=0.12.4
# Mod Properties # Mod Properties
mod_version=0.12.4 mod_version=0.12.5
maven_group=ru.betterend maven_group=ru.betterend
archives_base_name=better-end archives_base_name=better-end
@ -15,6 +15,6 @@ archives_base_name=better-end
patchouli_version = 55-FABRIC-SNAPSHOT patchouli_version = 55-FABRIC-SNAPSHOT
fabric_version = 0.42.1+1.17 fabric_version = 0.42.1+1.17
bclib_version = 0.5.3 bclib_version = 0.5.4
rei_version = 6.0.264-alpha rei_version = 6.0.264-alpha
canvas_version = 1.0.+ canvas_version = 1.0.+

View file

@ -1,6 +1,5 @@
package ru.betterend.entity; package ru.betterend.entity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket; import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataAccessor;
@ -27,14 +26,10 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.phys.AABB;
import ru.bclib.api.BiomeAPI; import ru.bclib.api.BiomeAPI;
import ru.betterend.registry.EndBiomes; import ru.betterend.registry.EndBiomes;
import ru.betterend.registry.EndItems; import ru.betterend.registry.EndItems;
import java.util.List;
import java.util.Random;
public class CubozoaEntity extends AbstractSchoolingFish { public class CubozoaEntity extends AbstractSchoolingFish {
public static final int VARIANTS = 2; public static final int VARIANTS = 2;
private static final EntityDataAccessor<Byte> VARIANT = SynchedEntityData.defineId( private static final EntityDataAccessor<Byte> VARIANT = SynchedEntityData.defineId(
@ -124,14 +119,6 @@ public class CubozoaEntity extends AbstractSchoolingFish {
return getByteScale() / 32F + 0.75F; return getByteScale() / 32F + 0.75F;
} }
public static boolean canSpawn(EntityType<CubozoaEntity> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
AABB box = new AABB(pos).inflate(16);
List<CubozoaEntity> list = world.getEntitiesOfClass(CubozoaEntity.class, box, (entity) -> {
return true;
});
return list.size() < 9;
}
protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) {
return dimensions.height * 0.5F; return dimensions.height * 0.5F;
} }

View file

@ -9,7 +9,6 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.control.FlyingMoveControl; import net.minecraft.world.entity.ai.control.FlyingMoveControl;
@ -27,9 +26,7 @@ import net.minecraft.world.entity.animal.FlyingAnimal;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.pathfinder.BlockPathTypes; import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.level.pathfinder.Path; import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -39,7 +36,6 @@ import ru.betterend.registry.EndEntities;
import ru.betterend.registry.EndSounds; import ru.betterend.registry.EndSounds;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Random;
public class DragonflyEntity extends Animal implements FlyingAnimal { public class DragonflyEntity extends Animal implements FlyingAnimal {
public DragonflyEntity(EntityType<DragonflyEntity> entityType, Level world) { public DragonflyEntity(EntityType<DragonflyEntity> entityType, Level world) {
@ -218,9 +214,4 @@ public class DragonflyEntity extends Animal implements FlyingAnimal {
public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) { public AgeableMob getBreedOffspring(ServerLevel world, AgeableMob entity) {
return EndEntities.DRAGONFLY.create(world); return EndEntities.DRAGONFLY.create(world);
} }
public static boolean canSpawn(EntityType<DragonflyEntity> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
int y = world.getChunk(pos).getHeight(Types.WORLD_SURFACE, pos.getX() & 15, pos.getY() & 15);
return y > 0 && pos.getY() >= y;
}
} }

View file

@ -1,6 +1,5 @@
package ru.betterend.entity; package ru.betterend.entity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataAccessor;
@ -22,14 +21,10 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.AABB;
import ru.bclib.api.BiomeAPI; import ru.bclib.api.BiomeAPI;
import ru.betterend.registry.EndBiomes; import ru.betterend.registry.EndBiomes;
import ru.betterend.registry.EndItems; import ru.betterend.registry.EndItems;
import java.util.List;
import java.util.Random;
public class EndFishEntity extends AbstractSchoolingFish { public class EndFishEntity extends AbstractSchoolingFish {
public static final int VARIANTS_NORMAL = 5; public static final int VARIANTS_NORMAL = 5;
public static final int VARIANTS_SULPHUR = 3; public static final int VARIANTS_SULPHUR = 3;
@ -152,14 +147,6 @@ public class EndFishEntity extends AbstractSchoolingFish {
return getByteScale() / 32F + 0.75F; return getByteScale() / 32F + 0.75F;
} }
public static boolean canSpawn(EntityType<EndFishEntity> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
AABB box = new AABB(pos).inflate(16);
List<EndFishEntity> list = world.getEntitiesOfClass(EndFishEntity.class, box, (entity) -> {
return true;
});
return list.size() < 9;
}
@Override @Override
protected void dropFromLootTable(DamageSource source, boolean causedByPlayer) { protected void dropFromLootTable(DamageSource source, boolean causedByPlayer) {
ItemEntity drop = new ItemEntity(level, getX(), getY(), getZ(), new ItemStack(EndItems.END_FISH_RAW)); ItemEntity drop = new ItemEntity(level, getX(), getY(), getZ(), new ItemStack(EndItems.END_FISH_RAW));

View file

@ -30,11 +30,11 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.AABB;
import ru.bclib.api.BiomeAPI; import ru.bclib.api.BiomeAPI;
import ru.bclib.api.TagAPI;
import ru.bclib.util.BlocksHelper; import ru.bclib.util.BlocksHelper;
import ru.bclib.util.MHelper; import ru.bclib.util.MHelper;
import ru.bclib.world.biomes.BCLBiome; import ru.bclib.world.biomes.BCLBiome;
@ -42,7 +42,6 @@ import ru.betterend.interfaces.ISlime;
import ru.betterend.registry.EndBiomes; import ru.betterend.registry.EndBiomes;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import java.util.Random; import java.util.Random;
public class EndSlimeEntity extends Slime { public class EndSlimeEntity extends Slime {
@ -212,34 +211,26 @@ public class EndSlimeEntity extends Slime {
return this.entityData.get(VARIANT) == 0; return this.entityData.get(VARIANT) == 0;
} }
public static boolean canSpawn(EntityType<EndSlimeEntity> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) { public static boolean canSpawn(EntityType entityType, LevelAccessor world, MobSpawnType spawnType, BlockPos pos, Random random) {
return random.nextInt(16) == 0 || isPermanentBiome(world, pos) || (notManyEntities( if (!world.getBlockState(pos.below()).is(TagAPI.BLOCK_END_GROUND)) {
world, return false;
pos,
32,
3
) && isWaterNear(world, pos, 32, 8));
} }
BCLBiome biome = BiomeAPI.getFromBiome(world.getBiome(pos));
private static boolean isPermanentBiome(ServerLevelAccessor world, BlockPos pos) { if (biome == EndBiomes.CHORUS_FOREST || biome == EndBiomes.MEGALAKE) {
Biome biome = world.getBiome(pos);
return BiomeAPI.getFromBiome(biome) == EndBiomes.CHORUS_FOREST;
}
private static boolean notManyEntities(ServerLevelAccessor world, BlockPos pos, int radius, int maxCount) {
AABB box = new AABB(pos).inflate(radius);
List<EndSlimeEntity> list = world.getEntitiesOfClass(EndSlimeEntity.class, box, (entity) -> {
return true; return true;
}); }
return list.size() <= maxCount; if (biome == EndBiomes.MEGALAKE_GROVE && random.nextBoolean()) {
return true;
}
return random.nextInt(4) == 0 && isWaterNear(world, pos);
} }
private static boolean isWaterNear(ServerLevelAccessor world, BlockPos pos, int radius, int radius2) { private static boolean isWaterNear(LevelAccessor world, BlockPos pos) {
for (int x = pos.getX() - radius; x <= pos.getX() + radius; x++) { for (int x = pos.getX() - 32; x <= pos.getX() + 32; x++) {
POS.setX(x); POS.setX(x);
for (int z = pos.getZ() - radius; z <= pos.getZ() + radius; z++) { for (int z = pos.getZ() - 32; z <= pos.getZ() + 32; z++) {
POS.setZ(z); POS.setZ(z);
for (int y = pos.getY() - radius2; y <= pos.getY() + radius2; y++) { for (int y = pos.getY() - 8; y <= pos.getY() + 8; y++) {
POS.setY(y); POS.setY(y);
if (world.getBlockState(POS).getBlock() == Blocks.WATER) { if (world.getBlockState(POS).getBlock() == Blocks.WATER) {
return true; return true;

View file

@ -9,7 +9,6 @@ import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal; import net.minecraft.world.entity.ai.goal.LookAtPlayerGoal;
@ -20,15 +19,10 @@ import net.minecraft.world.entity.ai.goal.target.NearestAttackableTargetGoal;
import net.minecraft.world.entity.monster.Monster; import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import ru.bclib.util.MHelper; import ru.bclib.util.MHelper;
import ru.betterend.registry.EndSounds; import ru.betterend.registry.EndSounds;
import java.util.List;
import java.util.Random;
public class ShadowWalkerEntity extends Monster { public class ShadowWalkerEntity extends Monster {
public ShadowWalkerEntity(EntityType<ShadowWalkerEntity> entityType, Level world) { public ShadowWalkerEntity(EntityType<ShadowWalkerEntity> entityType, Level world) {
super(entityType, world); super(entityType, world);
@ -125,17 +119,6 @@ public class ShadowWalkerEntity extends Monster {
return attack; return attack;
} }
public static boolean canSpawn(EntityType<ShadowWalkerEntity> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
if (Monster.checkMonsterSpawnRules(type, world, spawnReason, pos, random)) {
AABB box = new AABB(pos).inflate(16);
List<ShadowWalkerEntity> entities = world.getEntitiesOfClass(ShadowWalkerEntity.class, box, (entity) -> {
return true;
});
return entities.size() < 6;
}
return false;
}
private final class AttackGoal extends MeleeAttackGoal { private final class AttackGoal extends MeleeAttackGoal {
private final ShadowWalkerEntity walker; private final ShadowWalkerEntity walker;
private int ticks; private int ticks;

View file

@ -15,7 +15,6 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.control.FlyingMoveControl; import net.minecraft.world.entity.ai.control.FlyingMoveControl;
@ -35,12 +34,9 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.pathfinder.BlockPathTypes; import net.minecraft.world.level.pathfinder.BlockPathTypes;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import ru.bclib.util.BlocksHelper; import ru.bclib.util.BlocksHelper;
@ -52,8 +48,6 @@ import ru.betterend.registry.EndEntities;
import ru.betterend.registry.EndItems; import ru.betterend.registry.EndItems;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List;
import java.util.Random;
public class SilkMothEntity extends Animal implements FlyingAnimal { public class SilkMothEntity extends Animal implements FlyingAnimal {
private BlockPos hivePos; private BlockPos hivePos;
@ -184,17 +178,6 @@ public class SilkMothEntity extends Animal implements FlyingAnimal {
this.level.addFreshEntity(drop); this.level.addFreshEntity(drop);
} }
public static boolean canSpawn(EntityType<SilkMothEntity> type, ServerLevelAccessor world, MobSpawnType spawnReason, BlockPos pos, Random random) {
int y = world.getChunk(pos).getHeight(Types.WORLD_SURFACE, pos.getX() & 15, pos.getY() & 15);
return y > 0 && pos.getY() >= y && notManyEntities(world, pos, 32, 1);
}
private static boolean notManyEntities(ServerLevelAccessor world, BlockPos pos, int radius, int maxCount) {
AABB box = new AABB(pos).inflate(radius);
List<SilkMothEntity> list = world.getEntitiesOfClass(SilkMothEntity.class, box, (entity) -> true);
return list.size() <= maxCount;
}
class MothLookControl extends LookControl { class MothLookControl extends LookControl {
MothLookControl(Mob entity) { MothLookControl(Mob entity) {
super(entity); super(entity);

View file

@ -8,7 +8,6 @@ import ru.bclib.api.BiomeAPI;
import ru.bclib.api.TagAPI; import ru.bclib.api.TagAPI;
import ru.bclib.integration.ModIntegration; import ru.bclib.integration.ModIntegration;
import ru.bclib.world.features.BCLFeature; import ru.bclib.world.features.BCLFeature;
import ru.betterend.registry.EndTags;
public class EnderscapeIntegration extends ModIntegration { public class EnderscapeIntegration extends ModIntegration {
public EnderscapeIntegration() { public EnderscapeIntegration() {

View file

@ -11,6 +11,8 @@ import net.minecraft.world.entity.EntityType.EntityFactory;
import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobCategory; import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder; import net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import ru.bclib.api.spawning.SpawnRuleBulder;
import ru.bclib.util.ColorUtil; import ru.bclib.util.ColorUtil;
import ru.betterend.BetterEnd; import ru.betterend.BetterEnd;
import ru.betterend.config.Configs; import ru.betterend.config.Configs;
@ -20,7 +22,6 @@ import ru.betterend.entity.EndFishEntity;
import ru.betterend.entity.EndSlimeEntity; import ru.betterend.entity.EndSlimeEntity;
import ru.betterend.entity.ShadowWalkerEntity; import ru.betterend.entity.ShadowWalkerEntity;
import ru.betterend.entity.SilkMothEntity; import ru.betterend.entity.SilkMothEntity;
import ru.betterend.util.SpawnHelper;
public class EndEntities { public class EndEntities {
public static final EntityType<DragonflyEntity> DRAGONFLY = register( public static final EntityType<DragonflyEntity> DRAGONFLY = register(
@ -91,12 +92,24 @@ public class EndEntities {
); );
public static void register() { public static void register() {
SpawnHelper.restrictionAir(DRAGONFLY, DragonflyEntity::canSpawn); // Air //
SpawnHelper.restrictionLand(END_SLIME, EndSlimeEntity::canSpawn); SpawnRuleBulder.start(DRAGONFLY).aboveGround(2).maxNearby(8).buildNoRestrictions(Types.MOTION_BLOCKING);
SpawnHelper.restrictionWater(END_FISH, EndFishEntity::canSpawn); SpawnRuleBulder.start(SILK_MOTH).aboveGround(2).maxNearby(4).buildNoRestrictions(Types.MOTION_BLOCKING);
SpawnHelper.restrictionLand(SHADOW_WALKER, ShadowWalkerEntity::canSpawn);
SpawnHelper.restrictionWater(CUBOZOA, CubozoaEntity::canSpawn); // Land //
SpawnHelper.restrictionAir(SILK_MOTH, SilkMothEntity::canSpawn); SpawnRuleBulder
.start(END_SLIME)
.notPeaceful()
.maxNearby(4, 64)
.onlyOnValidBlocks()
.customRule(EndSlimeEntity::canSpawn)
.buildNoRestrictions(Types.MOTION_BLOCKING);
SpawnRuleBulder.start(SHADOW_WALKER).notPeaceful().onlyOnValidBlocks().maxNearby(8, 64).buildNoRestrictions(Types.MOTION_BLOCKING);
// Water //
SpawnRuleBulder.start(END_FISH).maxNearby(8, 64).buildInWater(Types.MOTION_BLOCKING);
SpawnRuleBulder.start(CUBOZOA).maxNearby(8, 64).buildInWater(Types.MOTION_BLOCKING);
} }
protected static <T extends Entity> EntityType<T> register(String name, MobCategory group, float width, float height, EntityFactory<T> entity) { protected static <T extends Entity> EntityType<T> register(String name, MobCategory group, float width, float height, EntityFactory<T> entity) {

View file

@ -1,22 +0,0 @@
package ru.betterend.util;
import net.fabricmc.fabric.mixin.object.builder.SpawnRestrictionAccessor;
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.levelgen.Heightmap.Types;
public class SpawnHelper {
public static <T extends Mob> void restrictionAir(EntityType<T> entity, SpawnPredicate<T> predicate) {
SpawnRestrictionAccessor.callRegister(entity, Type.NO_RESTRICTIONS, Types.MOTION_BLOCKING, predicate);
}
public static <T extends Mob> void restrictionLand(EntityType<T> entity, SpawnPredicate<T> predicate) {
SpawnRestrictionAccessor.callRegister(entity, Type.ON_GROUND, Types.MOTION_BLOCKING, predicate);
}
public static <T extends Mob> void restrictionWater(EntityType<T> entity, SpawnPredicate<T> predicate) {
SpawnRestrictionAccessor.callRegister(entity, Type.IN_WATER, Types.MOTION_BLOCKING, predicate);
}
}

View file

@ -45,7 +45,7 @@
"fabricloader": ">=0.12.4", "fabricloader": ">=0.12.4",
"fabric": ">=0.42.1", "fabric": ">=0.42.1",
"minecraft": ">=1.17", "minecraft": ">=1.17",
"bclib": ">=0.5.3" "bclib": ">=0.5.4"
}, },
"suggests": { "suggests": {
"byg": ">=1.1.3", "byg": ">=1.1.3",