diff --git a/paper-server/nms-patches/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.patch b/paper-server/nms-patches/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.patch index 82527bb9c6..81598fd22e 100644 --- a/paper-server/nms-patches/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.patch +++ b/paper-server/nms-patches/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.patch @@ -1,5 +1,27 @@ --- a/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java +++ b/net/minecraft/world/level/block/entity/trialspawner/TrialSpawner.java +@@ -57,16 +57,16 @@ + private static final int MAX_MOB_TRACKING_DISTANCE = 47; + private static final int MAX_MOB_TRACKING_DISTANCE_SQR = MathHelper.square(47); + private static final float SPAWNING_AMBIENT_SOUND_CHANCE = 0.02F; +- private final TrialSpawnerConfig normalConfig; +- private final TrialSpawnerConfig ominousConfig; ++ public TrialSpawnerConfig normalConfig; // PAIL - private->public, -final ++ public TrialSpawnerConfig ominousConfig; // PAIL - private->public, -final + private final TrialSpawnerData data; +- private final int requiredPlayerRange; +- private final int targetCooldownLength; ++ public int requiredPlayerRange; // PAIL - private->public, -final ++ public int targetCooldownLength; // PAIL - private->public, -final + public final TrialSpawner.b stateAccessor; + private PlayerDetector playerDetector; + private final PlayerDetector.a entitySelector; + private boolean overridePeacefulAndMobSpawnRule; +- private boolean isOminous; ++ public boolean isOminous; // PAIL - private->public + + public Codec codec() { + return RecordCodecBuilder.create((instance) -> { @@ -219,13 +219,13 @@ } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java index 566a3630cc..334f27ca1e 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java @@ -15,6 +15,7 @@ import net.minecraft.util.random.SimpleWeightedRandomList; import net.minecraft.util.random.WeightedEntry.b; import net.minecraft.world.entity.EntityTypes; import net.minecraft.world.entity.EquipmentTable; +import net.minecraft.world.level.MobSpawnerAbstract; import net.minecraft.world.level.MobSpawnerData; import net.minecraft.world.level.block.entity.TileEntityMobSpawner; import org.bukkit.Location; @@ -75,60 +76,94 @@ public class CraftCreatureSpawner extends CraftBlockEntityState builder = SimpleWeightedRandomList.builder(); // PAIL rename Builder - this.getSnapshot().getSpawner().spawnPotentials.unwrap().forEach(entry -> builder.add(entry.data(), entry.getWeight().asInt())); - builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(toMinecraftRule(spawnRule)), Optional.empty()), weight); - this.getSnapshot().getSpawner().spawnPotentials = builder.build(); + spawner.spawnPotentials.unwrap().forEach(entry -> builder.add(entry.data(), entry.getWeight().asInt())); + builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(toMinecraftRule(spawnRule)), getEquipment(equipment)), weight); + spawner.spawnPotentials = builder.build(); } @Override public void addPotentialSpawn(SpawnerEntry spawnerEntry) { + Preconditions.checkArgument(spawnerEntry != null, "Entry cannot be null"); + addPotentialSpawn(spawnerEntry.getSnapshot(), spawnerEntry.getSpawnWeight(), spawnerEntry.getSpawnRule()); } @Override public void setPotentialSpawns(Collection entries) { + setPotentialSpawns(this.getSnapshot().getSpawner(), entries); + } + + public static void setPotentialSpawns(MobSpawnerAbstract spawner, Collection entries) { + Preconditions.checkArgument(entries != null, "Entries cannot be null"); + SimpleWeightedRandomList.a builder = SimpleWeightedRandomList.builder(); for (SpawnerEntry spawnerEntry : entries) { NBTTagCompound compoundTag = ((CraftEntitySnapshot) spawnerEntry.getSnapshot()).getData(); builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(toMinecraftRule(spawnerEntry.getSpawnRule())), getEquipment(spawnerEntry.getEquipment())), spawnerEntry.getSpawnWeight()); } - this.getSnapshot().getSpawner().spawnPotentials = builder.build(); + spawner.spawnPotentials = builder.build(); } @Override public List getPotentialSpawns() { + return getPotentialSpawns(this.getSnapshot().getSpawner()); + } + + public static List getPotentialSpawns(MobSpawnerAbstract spawner) { List entries = new ArrayList<>(); - for (b entry : this.getSnapshot().getSpawner().spawnPotentials.unwrap()) { // PAIL rename Wrapper + for (b entry : spawner.spawnPotentials.unwrap()) { // PAIL rename Wrapper CraftEntitySnapshot snapshot = CraftEntitySnapshot.create(entry.data().getEntityToSpawn()); if (snapshot != null) { - SpawnRule rule = entry.data().customSpawnRules().map(this::fromMinecraftRule).orElse(null); + SpawnRule rule = entry.data().customSpawnRules().map(CraftCreatureSpawner::fromMinecraftRule).orElse(null); entries.add(new SpawnerEntry(snapshot, entry.getWeight().asInt(), rule, getEquipment(entry.data().equipment()))); } } return entries; } - private MobSpawnerData.a toMinecraftRule(SpawnRule rule) { // PAIL rename CustomSpawnRules + public static MobSpawnerData.a toMinecraftRule(SpawnRule rule) { // PAIL rename CustomSpawnRules if (rule == null) { return null; } return new MobSpawnerData.a(new InclusiveRange<>(rule.getMinBlockLight(), rule.getMaxBlockLight()), new InclusiveRange<>(rule.getMinSkyLight(), rule.getMaxSkyLight())); } - private SpawnRule fromMinecraftRule(MobSpawnerData.a rule) { + public static SpawnRule fromMinecraftRule(MobSpawnerData.a rule) { InclusiveRange blockLight = rule.blockLightLimit(); InclusiveRange skyLight = rule.skyLightLimit(); @@ -240,7 +275,7 @@ public class CraftCreatureSpawner extends CraftBlockEntityState getEquipment(SpawnerEntry.Equipment bukkit) { + public static Optional getEquipment(SpawnerEntry.Equipment bukkit) { if (bukkit == null) { return Optional.empty(); } @@ -251,7 +286,7 @@ public class CraftCreatureSpawner extends CraftBlockEntityState optional) { + public static SpawnerEntry.Equipment getEquipment(Optional optional) { return optional.map((nms) -> new SpawnerEntry.Equipment( CraftLootTable.minecraftToBukkit(nms.lootTable()), new HashMap<>(nms.slotDropChances().entrySet().stream().collect(Collectors.toMap((entry) -> CraftEquipmentSlot.getSlot(entry.getKey()), Map.Entry::getValue))) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawner.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawner.java index 900d0fd8cd..b2e49031d7 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawner.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawner.java @@ -1,18 +1,162 @@ package org.bukkit.craftbukkit.block; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import java.util.Collection; +import java.util.UUID; +import net.minecraft.world.level.block.TrialSpawnerBlock; import net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity; +import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.TrialSpawner; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.spawner.TrialSpawnerConfiguration; public class CraftTrialSpawner extends CraftBlockEntityState implements TrialSpawner { + private final CraftTrialSpawnerConfiguration normalConfig; + private final CraftTrialSpawnerConfiguration ominousConfig; + public CraftTrialSpawner(World world, TrialSpawnerBlockEntity tileEntity) { super(world, tileEntity); + this.normalConfig = new CraftTrialSpawnerConfiguration(tileEntity.getTrialSpawner().getNormalConfig(), getSnapshot()); + this.ominousConfig = new CraftTrialSpawnerConfiguration(tileEntity.getTrialSpawner().getOminousConfig(), getSnapshot()); } protected CraftTrialSpawner(CraftTrialSpawner state, Location location) { super(state, location); + this.normalConfig = state.normalConfig; + this.ominousConfig = state.ominousConfig; + } + + @Override + public int getCooldownLength() { + return getSnapshot().trialSpawner.getTargetCooldownLength(); + } + + @Override + public void setCooldownLength(int ticks) { + getSnapshot().trialSpawner.targetCooldownLength = ticks; + } + + @Override + public int getRequiredPlayerRange() { + return getSnapshot().trialSpawner.getRequiredPlayerRange(); + } + + @Override + public void setRequiredPlayerRange(int requiredPlayerRange) { + getSnapshot().trialSpawner.requiredPlayerRange = requiredPlayerRange; + } + + @Override + public Collection getTrackedPlayers() { + ImmutableSet.Builder players = ImmutableSet.builder(); + + for (UUID uuid : getTrialData().detectedPlayers) { + Player player = Bukkit.getPlayer(uuid); + if (player != null) { + players.add(player); + } + } + return players.build(); + } + + @Override + public boolean isTrackingPlayer(Player player) { + Preconditions.checkArgument(player != null, "Player cannot be null"); + + return getTrialData().detectedPlayers.contains(player.getUniqueId()); + } + + @Override + public void startTrackingPlayer(Player player) { + Preconditions.checkArgument(player != null, "Player cannot be null"); + + getTrialData().detectedPlayers.add(player.getUniqueId()); + } + + @Override + public void stopTrackingPlayer(Player player) { + Preconditions.checkArgument(player != null, "Player cannot be null"); + + getTrialData().detectedPlayers.remove(player.getUniqueId()); + } + + @Override + public Collection getTrackedEntities() { + ImmutableSet.Builder entities = ImmutableSet.builder(); + + for (UUID uuid : getTrialData().currentMobs) { + Entity entity = Bukkit.getEntity(uuid); + if (entity != null) { + entities.add(entity); + } + } + return entities.build(); + } + + @Override + public boolean isTrackingEntity(Entity entity) { + Preconditions.checkArgument(entity != null, "Entity cannot be null"); + + return getTrialData().currentMobs.contains(entity.getUniqueId()); + } + + @Override + public void startTrackingEntity(Entity entity) { + Preconditions.checkArgument(entity != null, "Entity cannot be null"); + + getTrialData().currentMobs.add(entity.getUniqueId()); + } + + @Override + public void stopTrackingEntity(Entity entity) { + Preconditions.checkArgument(entity != null, "Entity cannot be null"); + + getTrialData().currentMobs.remove(entity.getUniqueId()); + } + + @Override + public boolean isOminous() { + return getHandle().getValue(TrialSpawnerBlock.OMINOUS); + } + + @Override + public void setOminous(boolean ominous) { + getSnapshot().trialSpawner.isOminous = ominous; + if (ominous) { + setData(getHandle().setValue(TrialSpawnerBlock.OMINOUS, true)); + // TODO: Consider calling TrialSpawnerData#resetAfterBecomingOminous in update(...), but note that method also removes entities + return; + } + + setData(getHandle().setValue(TrialSpawnerBlock.OMINOUS, false)); + } + + @Override + public TrialSpawnerConfiguration getNormalConfiguration() { + return normalConfig; + } + + @Override + public TrialSpawnerConfiguration getOminousConfiguration() { + return ominousConfig; + } + + @Override + protected void applyTo(TrialSpawnerBlockEntity tileEntity) { + super.applyTo(tileEntity); + + tileEntity.trialSpawner.normalConfig = normalConfig.toMinecraft(); + tileEntity.trialSpawner.ominousConfig = ominousConfig.toMinecraft(); + } + + private TrialSpawnerData getTrialData() { + return getSnapshot().getTrialSpawner().getData(); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawnerConfiguration.java b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawnerConfiguration.java new file mode 100644 index 0000000000..0062664cf7 --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/block/CraftTrialSpawnerConfiguration.java @@ -0,0 +1,305 @@ +package org.bukkit.craftbukkit.block; + +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.random.SimpleWeightedRandomList; +import net.minecraft.util.random.WeightedEntry.b; +import net.minecraft.world.entity.EntityTypes; +import net.minecraft.world.level.MobSpawnerData; +import net.minecraft.world.level.block.entity.TrialSpawnerBlockEntity; +import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerConfig; +import net.minecraft.world.level.block.entity.trialspawner.TrialSpawnerData; +import org.bukkit.block.spawner.SpawnRule; +import org.bukkit.block.spawner.SpawnerEntry; +import org.bukkit.craftbukkit.CraftLootTable; +import org.bukkit.craftbukkit.entity.CraftEntitySnapshot; +import org.bukkit.craftbukkit.entity.CraftEntityType; +import org.bukkit.entity.EntitySnapshot; +import org.bukkit.entity.EntityType; +import org.bukkit.loot.LootTable; +import org.bukkit.spawner.TrialSpawnerConfiguration; + +public class CraftTrialSpawnerConfiguration implements TrialSpawnerConfiguration { + private final TrialSpawnerBlockEntity snapshot; + + private int spawnRange; + private float totalMobs; + private float simultaneousMobs; + private float totalMobsAddedPerPlayer; + private float simultaneousMobsAddedPerPlayer; + private int ticksBetweenSpawn; + private SimpleWeightedRandomList spawnPotentialsDefinition; + private SimpleWeightedRandomList> lootTablesToEject; + private ResourceKey itemsToDropWhenOminous; + + public CraftTrialSpawnerConfiguration(TrialSpawnerConfig minecraft, TrialSpawnerBlockEntity snapshot) { + this.snapshot = snapshot; + + this.spawnRange = minecraft.spawnRange(); + this.totalMobs = minecraft.totalMobs(); + this.simultaneousMobs = minecraft.simultaneousMobs(); + this.totalMobsAddedPerPlayer = minecraft.totalMobsAddedPerPlayer(); + this.simultaneousMobsAddedPerPlayer = minecraft.simultaneousMobsAddedPerPlayer(); + this.ticksBetweenSpawn = minecraft.ticksBetweenSpawn(); + this.spawnPotentialsDefinition = minecraft.spawnPotentialsDefinition(); + this.lootTablesToEject = minecraft.lootTablesToEject(); + this.itemsToDropWhenOminous = minecraft.itemsToDropWhenOminous(); + } + + @Override + public EntityType getSpawnedType() { + if (spawnPotentialsDefinition.isEmpty()) { + return null; + } + + Optional> type = EntityTypes.by(spawnPotentialsDefinition.unwrap().get(0).data().getEntityToSpawn()); + return type.map(CraftEntityType::minecraftToBukkit).orElse(null); + } + + @Override + public void setSpawnedType(EntityType entityType) { + if (entityType == null) { + getTrialData().nextSpawnData = Optional.empty(); + spawnPotentialsDefinition = SimpleWeightedRandomList.empty(); // need clear the spawnPotentials to avoid nextSpawnData being replaced later + return; + } + Preconditions.checkArgument(entityType != EntityType.UNKNOWN, "Can't spawn EntityType %s from mob spawners!", entityType); + + MobSpawnerData data = new MobSpawnerData(); + data.getEntityToSpawn().putString("id", BuiltInRegistries.ENTITY_TYPE.getKey(CraftEntityType.bukkitToMinecraft(entityType)).toString()); + getTrialData().nextSpawnData = Optional.of(data); + spawnPotentialsDefinition = SimpleWeightedRandomList.single(data); + } + + @Override + public float getBaseSpawnsBeforeCooldown() { + return totalMobs; + } + + @Override + public void setBaseSpawnsBeforeCooldown(float amount) { + totalMobs = amount; + } + + @Override + public float getBaseSimultaneousEntities() { + return simultaneousMobs; + } + + @Override + public void setBaseSimultaneousEntities(float amount) { + simultaneousMobs = amount; + } + + @Override + public float getAdditionalSpawnsBeforeCooldown() { + return totalMobsAddedPerPlayer; + } + + @Override + public void setAdditionalSpawnsBeforeCooldown(float amount) { + totalMobsAddedPerPlayer = amount; + } + + @Override + public float getAdditionalSimultaneousEntities() { + return simultaneousMobsAddedPerPlayer; + } + + @Override + public void setAdditionalSimultaneousEntities(float amount) { + simultaneousMobsAddedPerPlayer = amount; + } + + @Override + public int getDelay() { + return ticksBetweenSpawn; + } + + @Override + public void setDelay(int delay) { + Preconditions.checkArgument(delay >= 0, "Delay cannot be less than 0"); + + ticksBetweenSpawn = delay; + } + + @Override + public int getSpawnRange() { + return spawnRange; + } + + @Override + public void setSpawnRange(int spawnRange) { + this.spawnRange = spawnRange; + } + + @Override + public EntitySnapshot getSpawnedEntity() { + SimpleWeightedRandomList potentials = spawnPotentialsDefinition; + if (potentials.isEmpty()) { + return null; + } + + return CraftEntitySnapshot.create(potentials.unwrap().get(0).data().getEntityToSpawn()); + } + + @Override + public void setSpawnedEntity(EntitySnapshot snapshot) { + setSpawnedEntity(snapshot, null, null); + } + + @Override + public void setSpawnedEntity(SpawnerEntry spawnerEntry) { + Preconditions.checkArgument(spawnerEntry != null, "Entry cannot be null"); + + setSpawnedEntity(spawnerEntry.getSnapshot(), spawnerEntry.getSpawnRule(), spawnerEntry.getEquipment()); + } + + private void setSpawnedEntity(EntitySnapshot snapshot, SpawnRule spawnRule, SpawnerEntry.Equipment equipment) { + if (snapshot == null) { + getTrialData().nextSpawnData = Optional.empty(); + spawnPotentialsDefinition = SimpleWeightedRandomList.empty(); // need clear the spawnPotentials to avoid nextSpawnData being replaced later + return; + } + + NBTTagCompound compoundTag = ((CraftEntitySnapshot) snapshot).getData(); + MobSpawnerData data = new MobSpawnerData(compoundTag, Optional.ofNullable(CraftCreatureSpawner.toMinecraftRule(spawnRule)), CraftCreatureSpawner.getEquipment(equipment)); + + getTrialData().nextSpawnData = Optional.of(data); + spawnPotentialsDefinition = SimpleWeightedRandomList.single(data); + } + + @Override + public void addPotentialSpawn(EntitySnapshot snapshot, int weight, SpawnRule spawnRule) { + addPotentialSpawn(snapshot, weight, spawnRule, null); + } + + private void addPotentialSpawn(EntitySnapshot snapshot, int weight, SpawnRule spawnRule, SpawnerEntry.Equipment equipment) { + Preconditions.checkArgument(snapshot != null, "Snapshot cannot be null"); + + NBTTagCompound compoundTag = ((CraftEntitySnapshot) snapshot).getData(); + + SimpleWeightedRandomList.a builder = SimpleWeightedRandomList.builder(); // PAIL rename Builder + spawnPotentialsDefinition.unwrap().forEach(entry -> builder.add(entry.data(), entry.getWeight().asInt())); + builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(CraftCreatureSpawner.toMinecraftRule(spawnRule)), CraftCreatureSpawner.getEquipment(equipment)), weight); + spawnPotentialsDefinition = builder.build(); + } + + @Override + public void addPotentialSpawn(SpawnerEntry spawnerEntry) { + Preconditions.checkArgument(spawnerEntry != null, "Entry cannot be null"); + + addPotentialSpawn(spawnerEntry.getSnapshot(), spawnerEntry.getSpawnWeight(), spawnerEntry.getSpawnRule(), spawnerEntry.getEquipment()); + } + + @Override + public void setPotentialSpawns(Collection entries) { + Preconditions.checkArgument(entries != null, "Entries cannot be null"); + + SimpleWeightedRandomList.a builder = SimpleWeightedRandomList.builder(); + for (SpawnerEntry spawnerEntry : entries) { + NBTTagCompound compoundTag = ((CraftEntitySnapshot) spawnerEntry.getSnapshot()).getData(); + builder.add(new MobSpawnerData(compoundTag, Optional.ofNullable(CraftCreatureSpawner.toMinecraftRule(spawnerEntry.getSpawnRule())), CraftCreatureSpawner.getEquipment(spawnerEntry.getEquipment())), spawnerEntry.getSpawnWeight()); + } + spawnPotentialsDefinition = builder.build(); + } + + @Override + public List getPotentialSpawns() { + List entries = new ArrayList<>(); + + for (b entry : spawnPotentialsDefinition.unwrap()) { // PAIL rename Wrapper + CraftEntitySnapshot snapshot = CraftEntitySnapshot.create(entry.data().getEntityToSpawn()); + + if (snapshot != null) { + SpawnRule rule = entry.data().customSpawnRules().map(CraftCreatureSpawner::fromMinecraftRule).orElse(null); + entries.add(new SpawnerEntry(snapshot, entry.getWeight().asInt(), rule)); + } + } + return entries; + } + + @Override + public Map getPossibleRewards() { + Map tables = new HashMap<>(); + + for (b> entry : lootTablesToEject.unwrap()) { + LootTable table = CraftLootTable.minecraftToBukkit(entry.data()); + if (table != null) { + tables.put(table, entry.getWeight().asInt()); + } + } + + return tables; + } + + @Override + public void addPossibleReward(LootTable table, int weight) { + Preconditions.checkArgument(table != null, "Table cannot be null"); + Preconditions.checkArgument(weight >= 1, "Weight must be at least 1"); + + SimpleWeightedRandomList.a> builder = SimpleWeightedRandomList.builder(); + lootTablesToEject.unwrap().forEach(entry -> builder.add(entry.data(), entry.getWeight().asInt())); + builder.add(CraftLootTable.bukkitToMinecraft(table), weight); + lootTablesToEject = builder.build(); + } + + @Override + public void removePossibleReward(LootTable table) { + Preconditions.checkArgument(table != null, "Key cannot be null"); + + ResourceKey minecraftKey = CraftLootTable.bukkitToMinecraft(table); + SimpleWeightedRandomList.a> builder = SimpleWeightedRandomList.builder(); + + for (b> entry : lootTablesToEject.unwrap()) { + if (!entry.data().equals(minecraftKey)) { + builder.add(entry.data(), entry.getWeight().asInt()); + } + } + lootTablesToEject = builder.build(); + } + + @Override + public void setPossibleRewards(Map rewards) { + if (rewards == null || rewards.isEmpty()) { + lootTablesToEject = SimpleWeightedRandomList.empty(); + return; + } + + SimpleWeightedRandomList.a> builder = SimpleWeightedRandomList.builder(); + rewards.forEach((table, weight) -> { + Preconditions.checkArgument(table != null, "Table cannot be null"); + Preconditions.checkArgument(weight >= 1, "Weight must be at least 1"); + + builder.add(CraftLootTable.bukkitToMinecraft(table), weight); + }); + + lootTablesToEject = builder.build(); + } + + @Override + public int getRequiredPlayerRange() { + return snapshot.trialSpawner.getRequiredPlayerRange(); + } + + @Override + public void setRequiredPlayerRange(int requiredPlayerRange) { + snapshot.trialSpawner.requiredPlayerRange = requiredPlayerRange; + } + + private TrialSpawnerData getTrialData() { + return snapshot.getTrialSpawner().getData(); + } + + protected TrialSpawnerConfig toMinecraft() { + return new TrialSpawnerConfig(spawnRange, totalMobs, simultaneousMobs, totalMobsAddedPerPlayer, simultaneousMobsAddedPerPlayer, ticksBetweenSpawn, spawnPotentialsDefinition, lootTablesToEject, itemsToDropWhenOminous); + } +} diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java index 1f8468054f..546472fda4 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMinecartMobSpawner.java @@ -1,7 +1,20 @@ package org.bukkit.craftbukkit.entity; +import com.google.common.base.Preconditions; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import net.minecraft.util.RandomSource; +import net.minecraft.util.random.SimpleWeightedRandomList; +import net.minecraft.world.entity.EntityTypes; import net.minecraft.world.entity.vehicle.EntityMinecartMobSpawner; +import net.minecraft.world.level.MobSpawnerData; +import org.bukkit.block.spawner.SpawnRule; +import org.bukkit.block.spawner.SpawnerEntry; import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.block.CraftCreatureSpawner; +import org.bukkit.entity.EntitySnapshot; +import org.bukkit.entity.EntityType; import org.bukkit.entity.minecart.SpawnerMinecart; final class CraftMinecartMobSpawner extends CraftMinecart implements SpawnerMinecart { @@ -9,6 +22,152 @@ final class CraftMinecartMobSpawner extends CraftMinecart implements SpawnerMine super(server, entity); } + @Override + public EntityType getSpawnedType() { + MobSpawnerData spawnData = getHandle().getSpawner().nextSpawnData; + if (spawnData == null) { + return null; + } + + Optional> type = EntityTypes.by(spawnData.getEntityToSpawn()); + return type.map(CraftEntityType::minecraftToBukkit).orElse(null); + } + + @Override + public void setSpawnedType(EntityType entityType) { + if (entityType == null) { + getHandle().getSpawner().spawnPotentials = SimpleWeightedRandomList.empty(); // need clear the spawnPotentials to avoid nextSpawnData being replaced later + getHandle().getSpawner().nextSpawnData = new MobSpawnerData(); + return; + } + Preconditions.checkArgument(entityType != EntityType.UNKNOWN, "Can't spawn EntityType %s from mob spawners!", entityType); + + RandomSource rand = getHandle().level().getRandom(); + getHandle().getSpawner().setEntityId(CraftEntityType.bukkitToMinecraft(entityType), getHandle().level(), rand, getHandle().blockPosition()); + } + + @Override + public EntitySnapshot getSpawnedEntity() { + MobSpawnerData spawnData = getHandle().getSpawner().nextSpawnData; + if (spawnData == null) { + return null; + } + + return CraftEntitySnapshot.create(spawnData.getEntityToSpawn()); + } + + @Override + public void setSpawnedEntity(EntitySnapshot snapshot) { + CraftCreatureSpawner.setSpawnedEntity(getHandle().getSpawner(), snapshot, null, null); + } + + @Override + public void setSpawnedEntity(SpawnerEntry spawnerEntry) { + Preconditions.checkArgument(spawnerEntry != null, "Entry cannot be null"); + + CraftCreatureSpawner.setSpawnedEntity(getHandle().getSpawner(), spawnerEntry.getSnapshot(), spawnerEntry.getSpawnRule(), spawnerEntry.getEquipment()); + } + + @Override + public void addPotentialSpawn(EntitySnapshot snapshot, int weight, SpawnRule spawnRule) { + CraftCreatureSpawner.addPotentialSpawn(getHandle().getSpawner(), snapshot, weight, spawnRule, null); + } + + @Override + public void addPotentialSpawn(SpawnerEntry spawnerEntry) { + Preconditions.checkArgument(spawnerEntry != null, "Entry cannot be null"); + + CraftCreatureSpawner.addPotentialSpawn(getHandle().getSpawner(), spawnerEntry.getSnapshot(), spawnerEntry.getSpawnWeight(), spawnerEntry.getSpawnRule(), spawnerEntry.getEquipment()); + } + + @Override + public void setPotentialSpawns(Collection entries) { + CraftCreatureSpawner.setPotentialSpawns(getHandle().getSpawner(), entries); + } + + @Override + public List getPotentialSpawns() { + return CraftCreatureSpawner.getPotentialSpawns(getHandle().getSpawner()); + } + + @Override + public int getDelay() { + return getHandle().getSpawner().spawnDelay; + } + + @Override + public void setDelay(int delay) { + getHandle().getSpawner().spawnDelay = delay; + } + + @Override + public int getMinSpawnDelay() { + return getHandle().getSpawner().minSpawnDelay; + } + + @Override + public void setMinSpawnDelay(int spawnDelay) { + Preconditions.checkArgument(spawnDelay <= getMaxSpawnDelay(), "Minimum Spawn Delay must be less than or equal to Maximum Spawn Delay"); + getHandle().getSpawner().minSpawnDelay = spawnDelay; + } + + @Override + public int getMaxSpawnDelay() { + return getHandle().getSpawner().maxSpawnDelay; + } + + @Override + public void setMaxSpawnDelay(int spawnDelay) { + Preconditions.checkArgument(spawnDelay > 0, "Maximum Spawn Delay must be greater than 0."); + Preconditions.checkArgument(spawnDelay >= getMinSpawnDelay(), "Maximum Spawn Delay must be greater than or equal to Minimum Spawn Delay"); + getHandle().getSpawner().maxSpawnDelay = spawnDelay; + } + + @Override + public int getMaxNearbyEntities() { + return getHandle().getSpawner().maxNearbyEntities; + } + + @Override + public void setMaxNearbyEntities(int maxNearbyEntities) { + getHandle().getSpawner().maxNearbyEntities = maxNearbyEntities; + } + + @Override + public int getSpawnCount() { + return getHandle().getSpawner().spawnCount; + } + + @Override + public void setSpawnCount(int count) { + getHandle().getSpawner().spawnCount = count; + } + + @Override + public int getRequiredPlayerRange() { + return getHandle().getSpawner().requiredPlayerRange; + } + + @Override + public void setRequiredPlayerRange(int requiredPlayerRange) { + getHandle().getSpawner().requiredPlayerRange = requiredPlayerRange; + } + + @Override + public int getSpawnRange() { + return getHandle().getSpawner().spawnRange; + } + + @Override + public void setSpawnRange(int spawnRange) { + getHandle().getSpawner().spawnRange = spawnRange; + } + + @Override + public EntityMinecartMobSpawner getHandle() { + return (EntityMinecartMobSpawner) entity; + } + @Override public String toString() { return "CraftMinecartMobSpawner";