package net.minecraft.world.level; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.Optional; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.InclusiveRange; import net.minecraft.util.random.WeightedList; import net.minecraft.world.entity.EquipmentTable; public record SpawnData(CompoundTag entityToSpawn, Optional customSpawnRules, Optional equipment) { public static final String ENTITY_TAG = "entity"; public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( CompoundTag.CODEC.fieldOf("entity").forGetter(spawnData -> spawnData.entityToSpawn), SpawnData.CustomSpawnRules.CODEC.optionalFieldOf("custom_spawn_rules").forGetter(spawnData -> spawnData.customSpawnRules), EquipmentTable.CODEC.optionalFieldOf("equipment").forGetter(spawnData -> spawnData.equipment) ) .apply(instance, SpawnData::new) ); public static final Codec> LIST_CODEC = WeightedList.codec(CODEC); public SpawnData() { this(new CompoundTag(), Optional.empty(), Optional.empty()); } public SpawnData(CompoundTag entityToSpawn, Optional customSpawnRules, Optional equipment) { Optional optional = entityToSpawn.read("id", ResourceLocation.CODEC); if (optional.isPresent()) { entityToSpawn.store("id", ResourceLocation.CODEC, (ResourceLocation)optional.get()); } else { entityToSpawn.remove("id"); } this.entityToSpawn = entityToSpawn; this.customSpawnRules = customSpawnRules; this.equipment = equipment; } public CompoundTag getEntityToSpawn() { return this.entityToSpawn; } public Optional getCustomSpawnRules() { return this.customSpawnRules; } public Optional getEquipment() { return this.equipment; } public record CustomSpawnRules(InclusiveRange blockLightLimit, InclusiveRange skyLightLimit) { private static final InclusiveRange LIGHT_RANGE = new InclusiveRange(0, 15); public static final Codec CODEC = RecordCodecBuilder.create( instance -> instance.group( lightLimit("block_light_limit").forGetter(customSpawnRules -> customSpawnRules.blockLightLimit), lightLimit("sky_light_limit").forGetter(customSpawnRules -> customSpawnRules.skyLightLimit) ) .apply(instance, SpawnData.CustomSpawnRules::new) ); private static DataResult> checkLightBoundaries(InclusiveRange lightValues) { return !LIGHT_RANGE.contains(lightValues) ? DataResult.error(() -> "Light values must be withing range " + LIGHT_RANGE) : DataResult.success(lightValues); } private static MapCodec> lightLimit(String fieldName) { return InclusiveRange.INT.lenientOptionalFieldOf(fieldName, LIGHT_RANGE).validate(SpawnData.CustomSpawnRules::checkLightBoundaries); } public boolean isValidPosition(BlockPos pos, ServerLevel level) { return this.blockLightLimit.isValueInRange(level.getBrightness(LightLayer.BLOCK, pos)) && this.skyLightLimit.isValueInRange(level.getBrightness(LightLayer.SKY, pos)); } } }