diff --git a/nms-patches/net/minecraft/server/level/ChunkProviderServer.patch b/nms-patches/net/minecraft/server/level/ChunkProviderServer.patch index 461fedb5f..17df44b75 100644 --- a/nms-patches/net/minecraft/server/level/ChunkProviderServer.patch +++ b/nms-patches/net/minecraft/server/level/ChunkProviderServer.patch @@ -122,7 +122,7 @@ gameprofilerfiller.push("pollingChunks"); int k = this.level.getGameRules().getInt(GameRules.RULE_RANDOMTICKING); - boolean flag1 = worlddata.getGameTime() % 400L == 0L; -+ boolean flag1 = level.ticksPerAnimalSpawns != 0L && worlddata.getGameTime() % level.ticksPerAnimalSpawns == 0L; // CraftBukkit ++ boolean flag1 = level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && worlddata.getGameTime() % level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit gameprofilerfiller.push("naturalSpawnCount"); int l = this.distanceManager.getNaturalSpawnChunkCount(); diff --git a/nms-patches/net/minecraft/world/level/SpawnerCreature.patch b/nms-patches/net/minecraft/world/level/SpawnerCreature.patch index 5d81d866c..bd56967d9 100644 --- a/nms-patches/net/minecraft/world/level/SpawnerCreature.patch +++ b/nms-patches/net/minecraft/world/level/SpawnerCreature.patch @@ -1,18 +1,20 @@ --- a/net/minecraft/world/level/SpawnerCreature.java +++ b/net/minecraft/world/level/SpawnerCreature.java -@@ -47,6 +47,11 @@ +@@ -47,6 +47,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +// CraftBukkit start +import net.minecraft.world.level.storage.WorldData; ++import org.bukkit.craftbukkit.util.CraftSpawnCategory; ++import org.bukkit.entity.SpawnCategory; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + public final class SpawnerCreature { private static final Logger LOGGER = LogManager.getLogger(); -@@ -73,7 +78,8 @@ +@@ -73,7 +80,8 @@ if (entity instanceof EntityInsentient) { EntityInsentient entityinsentient = (EntityInsentient) entity; @@ -22,50 +24,21 @@ continue; } } -@@ -111,10 +117,54 @@ +@@ -111,10 +119,25 @@ EnumCreatureType[] aenumcreaturetype = SpawnerCreature.SPAWNING_CATEGORIES; int i = aenumcreaturetype.length; -+ // CraftBukkit start - Other mob type spawn tick rate -+ WorldData worlddata = worldserver.getLevelData(); -+ boolean spawnAnimalThisTick = worldserver.ticksPerAnimalSpawns != 0L && worlddata.getGameTime() % worldserver.ticksPerAnimalSpawns == 0L; -+ boolean spawnMonsterThisTick = worldserver.ticksPerMonsterSpawns != 0L && worlddata.getGameTime() % worldserver.ticksPerMonsterSpawns == 0L; -+ boolean spawnWaterThisTick = worldserver.ticksPerWaterSpawns != 0L && worlddata.getGameTime() % worldserver.ticksPerWaterSpawns == 0L; -+ boolean spawnAmbientThisTick = worldserver.ticksPerAmbientSpawns != 0L && worlddata.getGameTime() % worldserver.ticksPerAmbientSpawns == 0L; -+ boolean spawnWaterAmbientThisTick = worldserver.ticksPerWaterAmbientSpawns != 0L && worlddata.getGameTime() % worldserver.ticksPerWaterAmbientSpawns == 0L; -+ boolean spawnWaterUndergroundCreatureThisTick = worldserver.ticksPerWaterUndergroundCreatureSpawns != 0L && worlddata.getGameTime() % worldserver.ticksPerWaterUndergroundCreatureSpawns == 0L; -+ // CraftBukkit end ++ WorldData worlddata = worldserver.getLevelData(); // CraftBukkit - Other mob type spawn tick rate + for (int j = 0; j < i; ++j) { EnumCreatureType enumcreaturetype = aenumcreaturetype[j]; + // CraftBukkit start - Use per-world spawn limits + boolean spawnThisTick = true; + int limit = enumcreaturetype.getMaxInstancesPerChunk(); -+ switch (enumcreaturetype) { -+ case MONSTER: -+ spawnThisTick = spawnMonsterThisTick; -+ limit = worldserver.getWorld().getMonsterSpawnLimit(); -+ break; -+ case CREATURE: -+ spawnThisTick = spawnAnimalThisTick; -+ limit = worldserver.getWorld().getAnimalSpawnLimit(); -+ break; -+ case WATER_CREATURE: -+ spawnThisTick = spawnWaterThisTick; -+ limit = worldserver.getWorld().getWaterAnimalSpawnLimit(); -+ break; -+ case UNDERGROUND_WATER_CREATURE: -+ spawnThisTick = spawnWaterUndergroundCreatureThisTick; -+ limit = worldserver.getWorld().getWaterUndergroundCreatureSpawnLimit(); -+ break; -+ case AMBIENT: -+ spawnThisTick = spawnAmbientThisTick; -+ limit = worldserver.getWorld().getAmbientSpawnLimit(); -+ break; -+ case WATER_AMBIENT: -+ spawnThisTick = spawnWaterAmbientThisTick; -+ limit = worldserver.getWorld().getWaterAmbientSpawnLimit(); -+ break; ++ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype); ++ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { ++ spawnThisTick = worldserver.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && worlddata.getGameTime() % worldserver.ticksPerSpawnCategory.getLong(spawnCategory) == 0; ++ limit = worldserver.getWorld().getSpawnLimit(spawnCategory); + } - if ((flag || !enumcreaturetype.isFriendly()) && (flag1 || enumcreaturetype.isFriendly()) && (flag2 || !enumcreaturetype.isPersistent()) && spawnercreature_d.canSpawnForCategory(enumcreaturetype, chunk.getPos())) { @@ -78,7 +51,7 @@ Objects.requireNonNull(spawnercreature_d); SpawnerCreature.c spawnercreature_c = spawnercreature_d::canSpawn; -@@ -199,10 +249,14 @@ +@@ -199,10 +222,14 @@ entityinsentient.moveTo(d0, (double) i, d1, worldserver.random.nextFloat() * 360.0F, 0.0F); if (isValidPositionForMob(worldserver, entityinsentient, d2)) { groupdataentity = entityinsentient.finalizeSpawn(worldserver, worldserver.getCurrentDifficultyAt(entityinsentient.blockPosition()), EnumMobSpawn.NATURAL, groupdataentity, (NBTTagCompound) null); @@ -97,7 +70,7 @@ if (j >= entityinsentient.getMaxSpawnClusterSize()) { return; } -@@ -377,7 +431,7 @@ +@@ -377,7 +404,7 @@ if (entityinsentient.checkSpawnRules(worldaccess, EnumMobSpawn.CHUNK_GENERATION) && entityinsentient.checkSpawnObstruction(worldaccess)) { groupdataentity = entityinsentient.finalizeSpawn(worldaccess, worldaccess.getCurrentDifficultyAt(entityinsentient.blockPosition()), EnumMobSpawn.CHUNK_GENERATION, groupdataentity, (NBTTagCompound) null); @@ -106,7 +79,7 @@ flag = true; } } -@@ -498,8 +552,10 @@ +@@ -498,8 +525,10 @@ return this.unmodifiableMobCategoryCounts; } diff --git a/nms-patches/net/minecraft/world/level/World.patch b/nms-patches/net/minecraft/world/level/World.patch index e360bd46c..001312a7d 100644 --- a/nms-patches/net/minecraft/world/level/World.patch +++ b/nms-patches/net/minecraft/world/level/World.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/level/World.java +++ b/net/minecraft/world/level/World.java -@@ -67,6 +67,29 @@ +@@ -67,6 +67,31 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -22,7 +22,9 @@ +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.CapturedBlockState; +import org.bukkit.craftbukkit.block.data.CraftBlockData; ++import org.bukkit.craftbukkit.util.CraftSpawnCategory; +import org.bukkit.craftbukkit.util.CraftNamespacedKey; ++import org.bukkit.entity.SpawnCategory; +import org.bukkit.event.block.BlockPhysicsEvent; +import org.bukkit.event.world.GenericGameEvent; +// CraftBukkit end @@ -30,7 +32,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable { protected static final Logger LOGGER = LogManager.getLogger(); -@@ -104,7 +127,46 @@ +@@ -104,7 +129,43 @@ private final ResourceKey dimension; private long subTickCount; @@ -47,12 +49,7 @@ + public Map capturedBlockStates = new java.util.LinkedHashMap<>(); + public Map capturedTileEntities = new HashMap<>(); + public List captureDrops; -+ public long ticksPerAnimalSpawns; -+ public long ticksPerMonsterSpawns; -+ public long ticksPerWaterSpawns; -+ public long ticksPerWaterAmbientSpawns; -+ public long ticksPerWaterUndergroundCreatureSpawns; -+ public long ticksPerAmbientSpawns; ++ public final it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap ticksPerSpawnCategory = new it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap<>(); + public boolean populating; + + public CraftWorld getWorld() { @@ -68,17 +65,19 @@ + protected World(WorldDataMutable worlddatamutable, ResourceKey resourcekey, final DimensionManager dimensionmanager, Supplier supplier, boolean flag, boolean flag1, long i, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env) { + this.generator = gen; + this.world = new CraftWorld((WorldServer) this, gen, biomeProvider, env); -+ this.ticksPerAnimalSpawns = this.getCraftServer().getTicksPerAnimalSpawns(); // CraftBukkit -+ this.ticksPerMonsterSpawns = this.getCraftServer().getTicksPerMonsterSpawns(); // CraftBukkit -+ this.ticksPerWaterSpawns = this.getCraftServer().getTicksPerWaterSpawns(); // CraftBukkit -+ this.ticksPerWaterAmbientSpawns = this.getCraftServer().getTicksPerWaterAmbientSpawns(); // CraftBukkit -+ this.ticksPerWaterUndergroundCreatureSpawns = this.getCraftServer().getTicksPerWaterUndergroundCreatureSpawns(); // CraftBukkit -+ this.ticksPerAmbientSpawns = this.getCraftServer().getTicksPerAmbientSpawns(); // CraftBukkit ++ ++ // CraftBukkit Ticks things ++ for (SpawnCategory spawnCategory : SpawnCategory.values()) { ++ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { ++ this.ticksPerSpawnCategory.put(spawnCategory, (long) this.getCraftServer().getTicksPerSpawns(spawnCategory)); ++ } ++ } ++ + // CraftBukkit end this.profiler = supplier; this.levelData = worlddatamutable; this.dimensionType = dimensionmanager; -@@ -114,12 +176,12 @@ +@@ -114,12 +175,12 @@ this.worldBorder = new WorldBorder() { @Override public double getCenterX() { @@ -93,7 +92,7 @@ } }; } else { -@@ -129,6 +191,42 @@ +@@ -129,6 +190,42 @@ this.thread = Thread.currentThread(); this.biomeManager = new BiomeManager(this, i); this.isDebug = flag1; @@ -136,7 +135,7 @@ } @Override -@@ -186,6 +284,17 @@ +@@ -186,6 +283,17 @@ @Override public boolean setBlock(BlockPosition blockposition, IBlockData iblockdata, int i, int j) { @@ -154,7 +153,7 @@ if (this.isOutsideBuildHeight(blockposition)) { return false; } else if (!this.isClientSide && this.isDebug()) { -@@ -193,9 +302,24 @@ +@@ -193,9 +301,24 @@ } else { Chunk chunk = this.getChunkAt(blockposition); Block block = iblockdata.getBlock(); @@ -180,7 +179,7 @@ return false; } else { IBlockData iblockdata2 = this.getBlockState(blockposition); -@@ -206,6 +330,7 @@ +@@ -206,6 +329,7 @@ this.getProfiler().pop(); } @@ -188,7 +187,7 @@ if (iblockdata2 == iblockdata) { if (iblockdata1 != iblockdata2) { this.setBlocksDirty(blockposition, iblockdata1, iblockdata2); -@@ -232,12 +357,69 @@ +@@ -232,12 +356,69 @@ this.onBlockStateChange(blockposition, iblockdata1, iblockdata2); } @@ -258,7 +257,7 @@ public void onBlockStateChange(BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1) {} @Override -@@ -327,6 +509,17 @@ +@@ -327,6 +508,17 @@ IBlockData iblockdata = this.getBlockState(blockposition); try { @@ -276,7 +275,7 @@ iblockdata.neighborChanged(this, blockposition, block, blockposition1, false); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Exception while updating neighbours"); -@@ -369,6 +562,14 @@ +@@ -369,6 +561,14 @@ @Override public IBlockData getBlockState(BlockPosition blockposition) { @@ -291,7 +290,7 @@ if (this.isOutsideBuildHeight(blockposition)) { return Blocks.VOID_AIR.defaultBlockState(); } else { -@@ -494,6 +695,16 @@ +@@ -494,6 +694,16 @@ @Nullable @Override public TileEntity getBlockEntity(BlockPosition blockposition) { @@ -308,7 +307,7 @@ return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, Chunk.EnumTileEntityState.IMMEDIATE)); } -@@ -501,6 +712,12 @@ +@@ -501,6 +711,12 @@ BlockPosition blockposition = tileentity.getBlockPos(); if (!this.isOutsideBuildHeight(blockposition)) { @@ -321,7 +320,7 @@ this.getChunkAt(blockposition).addAndRegisterBlockEntity(tileentity); } } -@@ -605,7 +822,7 @@ +@@ -605,7 +821,7 @@ for (int j = 0; j < i; ++j) { EntityComplexPart entitycomplexpart = aentitycomplexpart[j]; @@ -330,7 +329,7 @@ if (t0 != null && predicate.test(t0)) { list.add(t0); -@@ -931,6 +1148,14 @@ +@@ -931,6 +1147,14 @@ public abstract LevelEntityGetter getEntities(); protected void postGameEventInRadius(@Nullable Entity entity, GameEvent gameevent, BlockPosition blockposition, int i) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 8c99f76af..0eb444280 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -4,7 +4,6 @@ import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; @@ -13,12 +12,11 @@ import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; -import com.mojang.serialization.DynamicOps; import com.mojang.serialization.Lifecycle; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; @@ -28,7 +26,6 @@ import java.io.InputStreamReader; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.Base64; import java.util.Collections; import java.util.HashSet; @@ -57,12 +54,8 @@ import net.minecraft.commands.arguments.ArgumentEntity; import net.minecraft.core.BlockPosition; import net.minecraft.core.IRegistry; import net.minecraft.core.RegistryMaterials; -import net.minecraft.nbt.DynamicOpsNBT; -import net.minecraft.nbt.NBTBase; import net.minecraft.resources.MinecraftKey; -import net.minecraft.resources.RegistryReadOps; import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; import net.minecraft.server.ServerCommand; import net.minecraft.server.bossevents.BossBattleCustom; import net.minecraft.server.commands.CommandReload; @@ -75,7 +68,6 @@ import net.minecraft.server.level.TicketType; import net.minecraft.server.level.WorldServer; import net.minecraft.server.players.GameProfileBanEntry; import net.minecraft.server.players.IpBanEntry; -import net.minecraft.server.players.JsonListEntry; import net.minecraft.server.players.OpListEntry; import net.minecraft.server.players.PlayerList; import net.minecraft.server.players.WhiteListEntry; @@ -199,11 +191,13 @@ import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.craftbukkit.util.CraftIconCache; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.craftbukkit.util.CraftNamespacedKey; +import org.bukkit.craftbukkit.util.CraftSpawnCategory; import org.bukkit.craftbukkit.util.DatFileFilter; import org.bukkit.craftbukkit.util.Versioning; import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.entity.SpawnCategory; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerChatTabCompleteEvent; import org.bukkit.event.server.BroadcastMessageEvent; @@ -276,12 +270,7 @@ public final class CraftServer implements Server { private final EntityMetadataStore entityMetadata = new EntityMetadataStore(); private final PlayerMetadataStore playerMetadata = new PlayerMetadataStore(); private final WorldMetadataStore worldMetadata = new WorldMetadataStore(); - private int monsterSpawn = -1; - private int animalSpawn = -1; - private int waterAnimalSpawn = -1; - private int waterAmbientSpawn = -1; - private int waterUndergroundCreatureSpawn = -1; - private int ambientSpawn = -1; + private final Object2IntOpenHashMap spawnCategoryLimit = new Object2IntOpenHashMap<>(); private File container; private WarningState warningState = WarningState.DEFAULT; public String minimumAPI; @@ -366,12 +355,7 @@ public final class CraftServer implements Server { overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*"); ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions"); pluginManager.useTimings(configuration.getBoolean("settings.plugin-profiling")); - monsterSpawn = configuration.getInt("spawn-limits.monsters"); - animalSpawn = configuration.getInt("spawn-limits.animals"); - waterAnimalSpawn = configuration.getInt("spawn-limits.water-animals"); - waterAmbientSpawn = configuration.getInt("spawn-limits.water-ambient"); - waterUndergroundCreatureSpawn = configuration.getInt("spawn-limits.water-underground-creature"); - ambientSpawn = configuration.getInt("spawn-limits.ambient"); + overrideSpawnLimits(); console.autosavePeriod = configuration.getInt("ticks-per.autosave"); warningState = WarningState.value(configuration.getString("settings.deprecated-verbose")); TicketType.PLUGIN.timeout = configuration.getInt("chunk-gc.period-in-ticks"); @@ -391,6 +375,14 @@ public final class CraftServer implements Server { return (File) console.options.valueOf("commands-settings"); } + private void overrideSpawnLimits() { + for (SpawnCategory spawnCategory : SpawnCategory.values()) { + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { + spawnCategoryLimit.put(spawnCategory, configuration.getInt(CraftSpawnCategory.getConfigNameSpawnLimit(spawnCategory))); + } + } + } + private void saveConfig() { try { configuration.save(getConfigFile()); @@ -721,33 +713,46 @@ public final class CraftServer implements Server { } @Override + @Deprecated public int getTicksPerAnimalSpawns() { - return this.configuration.getInt("ticks-per.animal-spawns"); + return getTicksPerSpawns(SpawnCategory.ANIMAL); } @Override + @Deprecated public int getTicksPerMonsterSpawns() { - return this.configuration.getInt("ticks-per.monster-spawns"); + return getTicksPerSpawns(SpawnCategory.MONSTER); } @Override + @Deprecated public int getTicksPerWaterSpawns() { - return this.configuration.getInt("ticks-per.water-spawns"); + return getTicksPerSpawns(SpawnCategory.WATER_ANIMAL); } @Override + @Deprecated public int getTicksPerWaterAmbientSpawns() { - return this.configuration.getInt("ticks-per.water-ambient-spawns"); + return getTicksPerSpawns(SpawnCategory.WATER_AMBIENT); } @Override + @Deprecated public int getTicksPerWaterUndergroundCreatureSpawns() { - return this.configuration.getInt("ticks-per.water-underground-creature-spawns"); + return getTicksPerSpawns(SpawnCategory.WATER_UNDERGROUND_CREATURE); } @Override + @Deprecated public int getTicksPerAmbientSpawns() { - return this.configuration.getInt("ticks-per.ambient-spawns"); + return getTicksPerSpawns(SpawnCategory.AMBIENT); + } + + @Override + public int getTicksPerSpawns(SpawnCategory spawnCategory) { + Validate.notNull(spawnCategory, "SpawnCategory cannot be null"); + Validate.isTrue(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory." + spawnCategory + " are not supported."); + return this.configuration.getInt(CraftSpawnCategory.getConfigNameTicksPerSpawn(spawnCategory)); } @Override @@ -825,12 +830,7 @@ public final class CraftServer implements Server { console.setPvpAllowed(config.pvp); console.setFlightAllowed(config.allowFlight); console.setMotd(config.motd); - monsterSpawn = configuration.getInt("spawn-limits.monsters"); - animalSpawn = configuration.getInt("spawn-limits.animals"); - waterAnimalSpawn = configuration.getInt("spawn-limits.water-animals"); - waterAmbientSpawn = configuration.getInt("spawn-limits.water-ambient"); - waterUndergroundCreatureSpawn = configuration.getInt("spawn-limits.water-underground-creature"); - ambientSpawn = configuration.getInt("spawn-limits.ambient"); + overrideSpawnLimits(); warningState = WarningState.value(configuration.getString("settings.deprecated-verbose")); TicketType.PLUGIN.timeout = configuration.getInt("chunk-gc.period-in-ticks"); minimumAPI = configuration.getString("settings.minimum-api"); @@ -852,40 +852,16 @@ public final class CraftServer implements Server { for (WorldServer world : console.getAllLevels()) { world.serverLevelData.setDifficulty(config.difficulty); world.setSpawnSettings(config.spawnMonsters, config.spawnAnimals); - if (this.getTicksPerAnimalSpawns() < 0) { - world.ticksPerAnimalSpawns = 400; - } else { - world.ticksPerAnimalSpawns = this.getTicksPerAnimalSpawns(); - } - if (this.getTicksPerMonsterSpawns() < 0) { - world.ticksPerMonsterSpawns = 1; - } else { - world.ticksPerMonsterSpawns = this.getTicksPerMonsterSpawns(); - } - - if (this.getTicksPerWaterSpawns() < 0) { - world.ticksPerWaterSpawns = 1; - } else { - world.ticksPerWaterSpawns = this.getTicksPerWaterSpawns(); - } - - if (this.getTicksPerWaterAmbientSpawns() < 0) { - world.ticksPerWaterAmbientSpawns = 1; - } else { - world.ticksPerWaterAmbientSpawns = this.getTicksPerWaterAmbientSpawns(); - } - - if (this.getTicksPerWaterUndergroundCreatureSpawns() < 0) { - world.ticksPerWaterUndergroundCreatureSpawns = 1; - } else { - world.ticksPerWaterUndergroundCreatureSpawns = this.getTicksPerWaterUndergroundCreatureSpawns(); - } - - if (this.getTicksPerAmbientSpawns() < 0) { - world.ticksPerAmbientSpawns = 1; - } else { - world.ticksPerAmbientSpawns = this.getTicksPerAmbientSpawns(); + for (SpawnCategory spawnCategory : SpawnCategory.values()) { + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { + long ticksPerCategorySpawn = this.getTicksPerSpawns(spawnCategory); + if (ticksPerCategorySpawn < 0) { + world.ticksPerSpawnCategory.put(spawnCategory, CraftSpawnCategory.getDefaultTicksPerSpawn(spawnCategory)); + } else { + world.ticksPerSpawnCategory.put(spawnCategory, ticksPerCategorySpawn); + } + } } } @@ -1873,33 +1849,44 @@ public final class CraftServer implements Server { } @Override + @Deprecated public int getMonsterSpawnLimit() { - return monsterSpawn; + return getSpawnLimit(SpawnCategory.MONSTER); } @Override + @Deprecated public int getAnimalSpawnLimit() { - return animalSpawn; + return getSpawnLimit(SpawnCategory.ANIMAL); } @Override + @Deprecated public int getWaterAnimalSpawnLimit() { - return waterAnimalSpawn; + return getSpawnLimit(SpawnCategory.WATER_ANIMAL); } @Override + @Deprecated public int getWaterAmbientSpawnLimit() { - return waterAmbientSpawn; + return getSpawnLimit(SpawnCategory.WATER_AMBIENT); } @Override + @Deprecated public int getWaterUndergroundCreatureSpawnLimit() { - return waterUndergroundCreatureSpawn; + return getSpawnLimit(SpawnCategory.WATER_UNDERGROUND_CREATURE); } @Override + @Deprecated public int getAmbientSpawnLimit() { - return ambientSpawn; + return getSpawnLimit(SpawnCategory.AMBIENT); + } + + @Override + public int getSpawnLimit(SpawnCategory spawnCategory) { + return spawnCategoryLimit.getOrDefault(spawnCategory, -1); } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index b4e765936..902b92cc3 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -52,7 +53,6 @@ import net.minecraft.world.level.Explosion; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.RayTrace; import net.minecraft.world.level.biome.BiomeBase; -import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.IChunkAccess; import net.minecraft.world.level.chunk.ProtoChunkExtension; @@ -94,6 +94,7 @@ import org.bukkit.craftbukkit.metadata.BlockMetadataStore; import org.bukkit.craftbukkit.potion.CraftPotionUtil; import org.bukkit.craftbukkit.util.CraftMagicNumbers; import org.bukkit.craftbukkit.util.CraftRayTraceResult; +import org.bukkit.craftbukkit.util.CraftSpawnCategory; import org.bukkit.entity.AbstractArrow; import org.bukkit.entity.Arrow; import org.bukkit.entity.Entity; @@ -101,6 +102,7 @@ import org.bukkit.entity.FallingBlock; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.LightningStrike; import org.bukkit.entity.Player; +import org.bukkit.entity.SpawnCategory; import org.bukkit.entity.SpectralArrow; import org.bukkit.entity.TippedArrow; import org.bukkit.entity.Trident; @@ -134,12 +136,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { private final BiomeProvider biomeProvider; private final List populators = new ArrayList(); private final BlockMetadataStore blockMetadata = new BlockMetadataStore(this); - private int monsterSpawn = -1; - private int animalSpawn = -1; - private int waterAnimalSpawn = -1; - private int waterAmbientSpawn = -1; - private int waterUndergroundCreatureSpawn = -1; - private int ambientSpawn = -1; + private final Object2IntOpenHashMap spawnCategoryLimit = new Object2IntOpenHashMap<>(); private static final Random rand = new Random(); @@ -1320,63 +1317,91 @@ public class CraftWorld extends CraftRegionAccessor implements World { } @Override + @Deprecated public long getTicksPerAnimalSpawns() { - return world.ticksPerAnimalSpawns; + return getTicksPerSpawns(SpawnCategory.ANIMAL); } @Override + @Deprecated public void setTicksPerAnimalSpawns(int ticksPerAnimalSpawns) { - world.ticksPerAnimalSpawns = ticksPerAnimalSpawns; + setTicksPerSpawns(SpawnCategory.ANIMAL, ticksPerAnimalSpawns); } @Override + @Deprecated public long getTicksPerMonsterSpawns() { - return world.ticksPerMonsterSpawns; + return getTicksPerSpawns(SpawnCategory.MONSTER); } @Override + @Deprecated public void setTicksPerMonsterSpawns(int ticksPerMonsterSpawns) { - world.ticksPerMonsterSpawns = ticksPerMonsterSpawns; + setTicksPerSpawns(SpawnCategory.MONSTER, ticksPerMonsterSpawns); } @Override + @Deprecated public long getTicksPerWaterSpawns() { - return world.ticksPerWaterSpawns; + return getTicksPerSpawns(SpawnCategory.WATER_ANIMAL); } @Override + @Deprecated public void setTicksPerWaterSpawns(int ticksPerWaterSpawns) { - world.ticksPerWaterSpawns = ticksPerWaterSpawns; + setTicksPerSpawns(SpawnCategory.WATER_ANIMAL, ticksPerWaterSpawns); } @Override + @Deprecated public long getTicksPerWaterAmbientSpawns() { - return world.ticksPerWaterAmbientSpawns; + return getTicksPerSpawns(SpawnCategory.WATER_AMBIENT); } @Override + @Deprecated public void setTicksPerWaterAmbientSpawns(int ticksPerWaterAmbientSpawns) { - world.ticksPerWaterAmbientSpawns = ticksPerWaterAmbientSpawns; + setTicksPerSpawns(SpawnCategory.WATER_AMBIENT, ticksPerWaterAmbientSpawns); } @Override + @Deprecated public long getTicksPerWaterUndergroundCreatureSpawns() { - return world.ticksPerWaterUndergroundCreatureSpawns; + return getTicksPerSpawns(SpawnCategory.WATER_UNDERGROUND_CREATURE); } @Override + @Deprecated public void setTicksPerWaterUndergroundCreatureSpawns(int ticksPerWaterUndergroundCreatureSpawns) { - world.ticksPerWaterUndergroundCreatureSpawns = ticksPerWaterUndergroundCreatureSpawns; + setTicksPerSpawns(SpawnCategory.WATER_UNDERGROUND_CREATURE, ticksPerWaterUndergroundCreatureSpawns); } @Override + @Deprecated public long getTicksPerAmbientSpawns() { - return world.ticksPerAmbientSpawns; + return getTicksPerSpawns(SpawnCategory.AMBIENT); } @Override + @Deprecated public void setTicksPerAmbientSpawns(int ticksPerAmbientSpawns) { - world.ticksPerAmbientSpawns = ticksPerAmbientSpawns; + setTicksPerSpawns(SpawnCategory.AMBIENT, ticksPerAmbientSpawns); + } + + @Override + public void setTicksPerSpawns(SpawnCategory spawnCategory, int ticksPerCategorySpawn) { + Validate.notNull(spawnCategory, "SpawnCategory cannot be null"); + Validate.isTrue(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory." + spawnCategory + " are not supported."); + + world.ticksPerSpawnCategory.put(spawnCategory, (long) ticksPerCategorySpawn); + } + + @Override + public long getTicksPerSpawns(SpawnCategory spawnCategory) { + Validate.notNull(spawnCategory, "SpawnCategory cannot be null"); + Validate.isTrue(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory." + spawnCategory + " are not supported."); + + return world.ticksPerSpawnCategory.getLong(spawnCategory); } @Override @@ -1400,87 +1425,95 @@ public class CraftWorld extends CraftRegionAccessor implements World { } @Override + @Deprecated public int getMonsterSpawnLimit() { - if (monsterSpawn < 0) { - return server.getMonsterSpawnLimit(); - } - - return monsterSpawn; + return getSpawnLimit(SpawnCategory.MONSTER); } @Override + @Deprecated public void setMonsterSpawnLimit(int limit) { - monsterSpawn = limit; + setSpawnLimit(SpawnCategory.MONSTER, limit); } @Override + @Deprecated public int getAnimalSpawnLimit() { - if (animalSpawn < 0) { - return server.getAnimalSpawnLimit(); - } - - return animalSpawn; + return getSpawnLimit(SpawnCategory.ANIMAL); } @Override + @Deprecated public void setAnimalSpawnLimit(int limit) { - animalSpawn = limit; + setSpawnLimit(SpawnCategory.ANIMAL, limit); } @Override + @Deprecated public int getWaterAnimalSpawnLimit() { - if (waterAnimalSpawn < 0) { - return server.getWaterAnimalSpawnLimit(); - } - - return waterAnimalSpawn; + return getSpawnLimit(SpawnCategory.WATER_ANIMAL); } @Override + @Deprecated public void setWaterAnimalSpawnLimit(int limit) { - waterAnimalSpawn = limit; + setSpawnLimit(SpawnCategory.WATER_ANIMAL, limit); } @Override + @Deprecated public int getWaterAmbientSpawnLimit() { - if (waterAmbientSpawn < 0) { - return server.getWaterAmbientSpawnLimit(); - } - - return waterAmbientSpawn; + return getSpawnLimit(SpawnCategory.WATER_AMBIENT); } @Override + @Deprecated public void setWaterAmbientSpawnLimit(int limit) { - waterAmbientSpawn = limit; + setSpawnLimit(SpawnCategory.WATER_AMBIENT, limit); } @Override + @Deprecated public int getWaterUndergroundCreatureSpawnLimit() { - if (waterUndergroundCreatureSpawn < 0) { - return server.getWaterUndergroundCreatureSpawnLimit(); - } - - return waterUndergroundCreatureSpawn; + return getSpawnLimit(SpawnCategory.WATER_UNDERGROUND_CREATURE); } @Override + @Deprecated public void setWaterUndergroundCreatureSpawnLimit(int limit) { - waterUndergroundCreatureSpawn = limit; + setSpawnLimit(SpawnCategory.WATER_UNDERGROUND_CREATURE, limit); } @Override + @Deprecated public int getAmbientSpawnLimit() { - if (ambientSpawn < 0) { - return server.getAmbientSpawnLimit(); - } - - return ambientSpawn; + return getSpawnLimit(SpawnCategory.AMBIENT); } @Override + @Deprecated public void setAmbientSpawnLimit(int limit) { - ambientSpawn = limit; + setSpawnLimit(SpawnCategory.AMBIENT, limit); + } + + @Override + public int getSpawnLimit(SpawnCategory spawnCategory) { + Validate.notNull(spawnCategory, "SpawnCategory cannot be null"); + Validate.isTrue(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory." + spawnCategory + " are not supported."); + + int limit = spawnCategoryLimit.getInt(spawnCategory); + if (limit < 0) { + limit = server.getSpawnLimit(spawnCategory); + } + return limit; + } + + @Override + public void setSpawnLimit(SpawnCategory spawnCategory, int limit) { + Validate.notNull(spawnCategory, "SpawnCategory cannot be null"); + Validate.isTrue(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory." + spawnCategory + " are not supported."); + + spawnCategoryLimit.put(spawnCategory, limit); } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index f4049559d..72d8401b2 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -165,8 +165,10 @@ import org.bukkit.craftbukkit.block.CraftBlock; import org.bukkit.craftbukkit.persistence.CraftPersistentDataContainer; import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.craftbukkit.util.CraftSpawnCategory; import org.bukkit.craftbukkit.util.CraftVector; import org.bukkit.entity.Pose; +import org.bukkit.entity.SpawnCategory; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.metadata.MetadataValue; @@ -1021,6 +1023,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return Pose.values()[getHandle().getPose().ordinal()]; } + @Override + public SpawnCategory getSpawnCategory() { + return CraftSpawnCategory.toBukkit(getHandle().getType().getCategory()); + } + public void storeBukkitValues(NBTTagCompound c) { if (!this.persistentDataContainer.isEmpty()) { c.put("BukkitValues", this.persistentDataContainer.toTagCompound()); diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftSpawnCategory.java b/src/main/java/org/bukkit/craftbukkit/util/CraftSpawnCategory.java new file mode 100644 index 000000000..857c7811f --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftSpawnCategory.java @@ -0,0 +1,74 @@ +package org.bukkit.craftbukkit.util; + +import net.minecraft.world.entity.EnumCreatureType; +import org.bukkit.entity.SpawnCategory; + +public class CraftSpawnCategory { + + public static boolean isValidForLimits(SpawnCategory spawnCategory) { + return spawnCategory != null && spawnCategory != SpawnCategory.MISC; + } + + public static String getConfigNameSpawnLimit(SpawnCategory spawnCategory) { + return switch (spawnCategory) { + case MONSTER -> "spawn-limits.monsters"; + case ANIMAL -> "spawn-limits.animals"; + case WATER_ANIMAL -> "spawn-limits.water-animals"; + case WATER_AMBIENT -> "spawn-limits.water-ambient"; + case WATER_UNDERGROUND_CREATURE -> "spawn-limits.water-underground-creature"; + case AMBIENT -> "spawn-limits.ambient"; + case AXOLOTL -> "spawn-limits.axolotls"; + default -> throw new UnsupportedOperationException("Unknown Config value " + spawnCategory + " for spawn-limits"); + }; + } + + public static String getConfigNameTicksPerSpawn(SpawnCategory spawnCategory) { + return switch (spawnCategory) { + case MONSTER -> "ticks-per.monster-spawns"; + case ANIMAL -> "ticks-per.animal-spawns"; + case WATER_ANIMAL -> "ticks-per.water-spawns"; + case WATER_AMBIENT -> "ticks-per.water-ambient-spawns"; + case WATER_UNDERGROUND_CREATURE -> "ticks-per.water-underground-creature-spawns"; + case AMBIENT -> "ticks-per.ambient-spawns"; + case AXOLOTL -> "ticks-per.axolotl-spawns"; + default -> throw new UnsupportedOperationException("Unknown Config value " + spawnCategory + " for ticks-per"); + }; + } + + public static long getDefaultTicksPerSpawn(SpawnCategory spawnCategory) { + return switch (spawnCategory) { + case MONSTER, AXOLOTL, AMBIENT, WATER_UNDERGROUND_CREATURE, WATER_AMBIENT, WATER_ANIMAL -> 1; + case ANIMAL -> 400; // This value come from commit 2995a08324f + default -> throw new UnsupportedOperationException("Unknown Config value " + spawnCategory + " for ticks-per"); + }; + } + + public static SpawnCategory toBukkit(EnumCreatureType enumCreatureType) { + return switch (enumCreatureType) { + case MONSTER -> SpawnCategory.MONSTER; + case CREATURE -> SpawnCategory.ANIMAL; + case AMBIENT -> SpawnCategory.AMBIENT; + case AXOLOTLS -> SpawnCategory.AXOLOTL; + case WATER_CREATURE -> SpawnCategory.WATER_ANIMAL; + case WATER_AMBIENT -> SpawnCategory.WATER_AMBIENT; + case UNDERGROUND_WATER_CREATURE -> SpawnCategory.WATER_UNDERGROUND_CREATURE; + case MISC -> SpawnCategory.MISC; + default -> throw new UnsupportedOperationException("Unknown EnumCreatureType " + enumCreatureType + " for SpawnCategory"); + }; + } + + public static EnumCreatureType toNMS(SpawnCategory spawnCategory) { + return switch (spawnCategory) { + case MONSTER -> EnumCreatureType.MONSTER; + case ANIMAL -> EnumCreatureType.CREATURE; + case AMBIENT -> EnumCreatureType.AMBIENT; + case AXOLOTL -> EnumCreatureType.AXOLOTLS; + case WATER_ANIMAL -> EnumCreatureType.WATER_CREATURE; + case WATER_AMBIENT -> EnumCreatureType.WATER_AMBIENT; + case WATER_UNDERGROUND_CREATURE -> EnumCreatureType.UNDERGROUND_WATER_CREATURE; + case MISC -> EnumCreatureType.MISC; + default -> throw new UnsupportedOperationException("Unknown SpawnCategory " + spawnCategory + " for EnumCreatureType"); + }; + } + +} diff --git a/src/main/resources/configurations/bukkit.yml b/src/main/resources/configurations/bukkit.yml index 6ef7d5436..97239f7aa 100644 --- a/src/main/resources/configurations/bukkit.yml +++ b/src/main/resources/configurations/bukkit.yml @@ -28,6 +28,7 @@ spawn-limits: water-animals: 5 water-ambient: 20 water-underground-creature: 5 + axolotls: 5 ambient: 15 chunk-gc: period-in-ticks: 600 @@ -37,6 +38,7 @@ ticks-per: water-spawns: 1 water-ambient-spawns: 1 water-underground-creature-spawns: 1 + axolotl-spawns: 1 ambient-spawns: 1 autosave: 6000 aliases: now-in-commands.yml diff --git a/src/test/java/org/bukkit/entity/SpawnCategoryTest.java b/src/test/java/org/bukkit/entity/SpawnCategoryTest.java new file mode 100644 index 000000000..0084138b8 --- /dev/null +++ b/src/test/java/org/bukkit/entity/SpawnCategoryTest.java @@ -0,0 +1,27 @@ +package org.bukkit.entity; + +import net.minecraft.world.entity.EnumCreatureType; +import org.bukkit.craftbukkit.util.CraftSpawnCategory; +import org.junit.Test; + +public class SpawnCategoryTest { + + @Test + public void testMatch() { + for (EnumCreatureType enumCreatureType : EnumCreatureType.values()) { + // If it is missing a convert to Bukkit then throw a UnsupportedOperationException + SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumCreatureType); + + if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { + long defaultTicks = CraftSpawnCategory.getDefaultTicksPerSpawn(spawnCategory); + String nameConfigSpawnLimit = CraftSpawnCategory.getConfigNameSpawnLimit(spawnCategory); + String nameConfigTicksPerSpawn = CraftSpawnCategory.getConfigNameTicksPerSpawn(spawnCategory); + } + } + + for (SpawnCategory spawnCategory : SpawnCategory.values()) { + // If it is missing a convert to NMS then throw a UnsupportedOperationException + EnumCreatureType enumCreatureType = CraftSpawnCategory.toNMS(spawnCategory); + } + } +}