mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-28 11:29:11 +00:00
* Init 1.21.6
* Finish API patches
* Apply purpur patches & all paper server patches
* Apply some
* Apply all
* Updated Upstream (Paper/Purpur)
Upstream has released updates that appear to apply and compile correctly
Paper Changes:
PaperMC/Paper@6aafe29e 1.21.6 dev
PaperMC/Paper@ae7c74ec tweak mob goal keys, generate missing api
PaperMC/Paper@9ea1460b Revert "fix sponge-absorb deleting chest content (#12647)"
Purpur Changes:
PurpurMC/Purpur@eb0ba67d Updated Upstream (Paper)
PurpurMC/Purpur@7c6502dc Updated Upstream (Paper)
PurpurMC/Purpur@aa289e2c Updated Upstream (Paper)
PurpurMC/Purpur@b1d412fb Updated Upstream (Paper)
PurpurMC/Purpur@293e28a0 use empty registryaccess where context is not needed, closes #1676
PurpurMC/Purpur@452bb319 port PaperMC/Paper#12654, closes #1665
PurpurMC/Purpur@849bc79c register test subcommands used for debugging, closes #1675
* Fix build & diff
* Update vanilla attribute size
* [ci/skip] Forget to remove this
* Updated Upstream (Paper/Purpur)
Upstream has released updates that appear to apply and compile correctly
Paper Changes:
PaperMC/Paper@a24f9b20 1.21.6 dev
PaperMC/Paper@457a0a69 Bump version string and protocol version to 1.21.6 release
PaperMC/Paper@245b5473 Update base to 1.21.6
PaperMC/Paper@b28d77b3 Update Moonrise to 1.21.6
PaperMC/Paper@8318dcb7 Update DataConverter to 1.21.6-rc1
Purpur Changes:
PurpurMC/Purpur@b228e316 Updated Upstream (Paper)
PurpurMC/Purpur@d6562a57 make happy ghast takes-damage-from-water and always-drop-exp options work
PurpurMC/Purpur@08d989b6 inline import
PurpurMC/Purpur@61d7f559 Updated Upstream (Paper)
PurpurMC/Purpur@469a4de5 Merge branch 'ver/1.21.5' into ver/1.21.6
* Updated Upstream (Paper)
Upstream has released updates that appear to apply and compile correctly
Paper Changes:
PaperMC/Paper@d2b1158e Set correct world access on BSL snapshots
* Updated Upstream (Paper)
Upstream has released updates that appear to apply and compile correctly
Paper Changes:
PaperMC/Paper@023e6c29 Ensure waypoints are per world
PaperMC/Paper@40d5d31c Fix recursively converting JSON Text Components
* Update spigot mapping to remap reobf mapping to mojamp for plugins
* Updated Upstream (Paper/Purpur)
Upstream has released updates that appear to apply and compile correctly
Paper Changes:
PaperMC/Paper@1fcc4162 Implement FeatureHooks#isChunkSent for Moonrise chunk system
PaperMC/Paper@e4d7178a Implement WaypointTransmitter#isChunkVisible
PaperMC/Paper@8980ead7 Directly walk text component after converting from JSON
PaperMC/Paper@5613ed6d Provide env environment variable and copy spigots sys prop for overriding default repository
PaperMC/Paper@62b7f86d Avoid and discourage use of Maven Central as a CDN (#12689)
PaperMC/Paper@f5534cb7 [ci/skip] Run generators (#12691)
PaperMC/Paper@4781d28b Re-add Log4j Javadocs (#12693)
PaperMC/Paper@74fbcce5 Check type of Material in get/set stats (#12607)
PaperMC/Paper@4b3f967e Improve Fix MC-44654 (#12703)
PaperMC/Paper@a7dd2635 Enable spigot obfuscation support (#12695)
PaperMC/Paper@6a51c44e Cleanup Primed TNT Fix (#12704)
PaperMC/Paper@839c6a18 Fix #11169 (#12706)
PaperMC/Paper@c77d5f99 Fix MC-297591
PaperMC/Paper@219f86ee Implement chunk unload delay config option
PaperMC/Paper@e4eb69b8 Do not allow ticket level decreases to be processed asynchronously
PaperMC/Paper@71b0c768 Adds support for vanilla negative explosions (#12705)
PaperMC/Paper@3750927a [ci/skip] Fix PermissionManager#clearPermissions() docs bad wording (#12657)
PaperMC/Paper@d61a51e8 Add ItemType#getBurnDuration() (#12604)
PaperMC/Paper@29fc8532 Allow empty paths in namespaced keys (#12687)
PaperMC/Paper@4419afb9 fix: Safely handle nanosecond overflow in ClickCallback (#12686)
PaperMC/Paper@56528821 Add `isProxyEnabled` method to ServerConfiguration (#12664)
PaperMC/Paper@c0dda0ea Add `isForceDefaultGameMode` method (#12673)
PaperMC/Paper@e714de63 Fix excess slot updates
PaperMC/Paper@6d0821d2 [ci/skip] Fix docs for Spawner class and cleanup (#12710)
PaperMC/Paper@cceffe3d Release ItemType and BlockType (#12708)
PaperMC/Paper@186e9e33 Relocate CommandMap#registerServerAliases() call to after lifecycle events have been run (#12601)
PaperMC/Paper@5edcf6dd Cleanup/command dispatching (#12713)
Purpur Changes:
PurpurMC/Purpur@baa196f5 Updated Upstream (Paper)
PurpurMC/Purpur@fdd1e980 Updated Upstream (Paper)
PurpurMC/Purpur@439f15db Updated Upstream (Paper)
PurpurMC/Purpur@46a28b93 [ci/skip] update version in README
PurpurMC/Purpur@162bd288 Updated Upstream (Paper)
PurpurMC/Purpur@afcdf9bb Updated Upstream (Paper)
* fix random tick order
Signed-off-by: hayanesuru <hayanesuru@outlook.jp>
* update optimize random tick
* fix mob spawning over limit
* optimize mob spawning
* fix FastBitRadixSort memory leak
* optimize brain collections
* [ci/skip] update comments
* Revert "fix FastBitRadixSort memory leak"
This reverts commit bdea683525.
* optimize brain collections
* fix async mob spawning data race
* [ci/skip] cleanup
* remove hash lookup in optimize random tick
* Updated Upstream (Paper/Purpur)
Upstream has released updates that appear to apply and compile correctly
Paper Changes:
PaperMC/Paper@803baf0b Support hidden entities in Waypoints (#12715)
PaperMC/Paper@1814d8b4 build: publish to fill (#12717)
PaperMC/Paper@e454fef4 Add support for private constructors in plugin main classes (#12652)
PaperMC/Paper@d0e808f4 Move player to correct position in vehicle move packet handler
PaperMC/Paper@46b4b0b8 Improve keepalive ping system
PaperMC/Paper@38c1ddb5 Add and use FeatureHooks.getAllEntities
PaperMC/Paper@2f083acb Ensure player entity schedulers are ticked when they are dead
PaperMC/Paper@7c90c7c4 Optimise EntityScheduler ticking
PaperMC/Paper@aa6ee44a Re-add global region scheduler tick erroneously removed in last commit
PaperMC/Paper@d7510efc Fix #12722 (#12726)
PaperMC/Paper@0caf75f8 Fix #12721 (#12725)
PaperMC/Paper@bee28792 Adventure 4.23.0 (#12690)
PaperMC/Paper@692e93a9 Fix MC-299110
PaperMC/Paper@ea10fa4a Don't mutate the position of Items for MC-4 Fix (#12702)
PaperMC/Paper@aa6cd74c Remove unnecesary item check for ServerboundPlayerActionPacket RELEASE_USE_ITEM (#12668)
PaperMC/Paper@c9e89f49 Expose arrow velocity in EntityShootBowEvent for mobs (#12688)
PaperMC/Paper@7ec3174a Jump out of experimental phase
PaperMC/Paper@4e1a2555 Update try catch for command handling
PaperMC/Paper@e382e687 Fix 0 yield on explosion events
PaperMC/Paper@35b2c6ec Use dropped item for stats info (#12747)
PaperMC/Paper@bd79e20c [ci/skip] PluginManager#getPlugin and PluginManager#isPluginEnabled are case-insensitive (#12723)
Purpur Changes:
PurpurMC/Purpur@4a5974cf Updated Upstream (Paper)
PurpurMC/Purpur@3893bba0 this is important...
PurpurMC/Purpur@47e758fb Updated Upstream (Paper)
* Update PWT patch
* cleanup
* fix playermobcaps command
* fix Didn't fill chunk buffer in async chunk sending
* cleanup
* fix HurtByTargetGoal npe
* optimize collectSpawningChunks (#382)
* optimize collectSpawningChunks
* add bit set
* cleanup
* cleanup
* cleanup
* cleanup
* Updated Upstream (Paper/Purpur)
Upstream has released updates that appear to apply and compile correctly
Paper Changes:
PaperMC/Paper@57c202e0 Allow adventure nbt codec to parse all tags
Purpur Changes:
PurpurMC/Purpur@a4776d7b Updated Upstream (Paper)
PurpurMC/Purpur@dfd8a7ad Fix phantom going at high speed when ridden (#1680)
* Fix diff
---------
Signed-off-by: hayanesuru <hayanesuru@outlook.jp>
Co-authored-by: hayanesuru <hayanesuru@outlook.jp>
597 lines
36 KiB
Diff
597 lines
36 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: hayanesuru <hayanesuru@outlook.jp>
|
|
Date: Tue, 3 Jun 2025 15:20:59 +0900
|
|
Subject: [PATCH] optimize mob spawning
|
|
|
|
Avoid getChunk calls if its position is same as the chunk used for mob spawning
|
|
|
|
Fix data race in async mob spawning
|
|
by adding chunk position to the mob count map
|
|
then apply result on server thread.
|
|
|
|
Generally faster than the non-async approach
|
|
|
|
iterate over all entities, get their chunk, and increment the count
|
|
|
|
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
|
|
index 79674f4bd7a12c42dec19a4175012d7a2dc88b84..0a97a491737807d59815b75635fa3d8c94901ba8 100644
|
|
--- a/net/minecraft/server/level/ChunkMap.java
|
|
+++ b/net/minecraft/server/level/ChunkMap.java
|
|
@@ -287,6 +287,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
}
|
|
// Paper end - per player mob count backoff
|
|
public int getMobCountNear(final ServerPlayer player, final net.minecraft.world.entity.MobCategory mobCategory) {
|
|
+ // Leaf - diff - async mob spawning - optimize mob spawning
|
|
return player.mobCounts[mobCategory.ordinal()] + player.mobBackoffCounts[mobCategory.ordinal()]; // Paper - per player mob count backoff
|
|
}
|
|
// Paper end - Optional per player mob spawns
|
|
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
|
index 46e171ca454253c32e22c0c18587e9a7ba19f331..1c7e2e5124c7d4c1b86039b0327bfd92c49df9b1 100644
|
|
--- a/net/minecraft/server/level/ServerChunkCache.java
|
|
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
|
@@ -70,11 +70,11 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
private final long[] lastChunkPos = new long[4];
|
|
private final ChunkStatus[] lastChunkStatus = new ChunkStatus[4];
|
|
private final ChunkAccess[] lastChunk = new ChunkAccess[4];
|
|
- private final List<LevelChunk> spawningChunks = new ObjectArrayList<>();
|
|
+ private final List<LevelChunk> spawningChunks = it.unimi.dsi.fastutil.objects.ReferenceArrayList.wrap(new LevelChunk[0]); // Leaf - optimize mob spawning
|
|
private final Set<ChunkHolder> chunkHoldersToBroadcast = new ReferenceOpenHashSet<>();
|
|
@Nullable
|
|
@VisibleForDebug
|
|
- private NaturalSpawner.SpawnState lastSpawnState;
|
|
+ private volatile NaturalSpawner.SpawnState lastSpawnState; // Leaf - optimize mob spawning
|
|
// Paper start
|
|
public final ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<net.minecraft.world.level.chunk.LevelChunk> fullChunks = new ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable<>();
|
|
public int getFullChunksCount() {
|
|
@@ -498,6 +498,23 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
}
|
|
|
|
private void tickChunks() {
|
|
+ // Leaf start - optimize mob spawning
|
|
+ if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled && this.level.tickRateManager().runsNormally()) {
|
|
+ for (ServerPlayer player : this.level.players) {
|
|
+ // Paper start - per player mob spawning backoff
|
|
+ for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
|
|
+ player.mobCounts[ii] = 0;
|
|
+
|
|
+ int newBackoff = player.mobBackoffCounts[ii] - 1; // TODO make configurable bleed // TODO use nonlinear algorithm?
|
|
+ if (newBackoff < 0) {
|
|
+ newBackoff = 0;
|
|
+ }
|
|
+ player.mobBackoffCounts[ii] = newBackoff;
|
|
+ }
|
|
+ // Paper end - per player mob spawning backoff
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
long gameTime = this.level.getGameTime();
|
|
long l = gameTime - this.lastInhabitedUpdate;
|
|
this.lastInhabitedUpdate = gameTime;
|
|
@@ -511,8 +528,8 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
}
|
|
|
|
// Pufferfish start - optimize mob spawning
|
|
- if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled) {
|
|
- for (ServerPlayer player : this.level.players) {
|
|
+ if (org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled && this.level.tickRateManager().runsNormally()) {
|
|
+ /*for (ServerPlayer player : this.level.players) {
|
|
// Paper start - per player mob spawning backoff
|
|
for (int ii = 0; ii < ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; ii++) {
|
|
player.mobCounts[ii] = 0;
|
|
@@ -524,14 +541,14 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
player.mobBackoffCounts[ii] = newBackoff;
|
|
}
|
|
// Paper end - per player mob spawning backoff
|
|
- }
|
|
+ }*/
|
|
if (firstRunSpawnCounts) {
|
|
firstRunSpawnCounts = false;
|
|
_pufferfish_spawnCountsReady.set(true);
|
|
}
|
|
if (_pufferfish_spawnCountsReady.getAndSet(false)) {
|
|
+ int mapped = distanceManager.getNaturalSpawnChunkCount();
|
|
net.minecraft.server.MinecraftServer.getServer().mobSpawnExecutor.submit(() -> {
|
|
- int mapped = distanceManager.getNaturalSpawnChunkCount();
|
|
ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.Iterator<Entity> objectiterator =
|
|
level.entityTickList.entities.iterator(ca.spottedleaf.moonrise.common.list.IteratorSafeOrderedReferenceSet.ITERATOR_FLAG_SEE_ADDITIONS);
|
|
try {
|
|
@@ -542,10 +559,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
new LocalMobCapCalculator(chunkMap) : null;
|
|
|
|
// This ensures the caps are properly enforced by using the correct calculator
|
|
- lastSpawnState = NaturalSpawner.createState(
|
|
+ lastSpawnState = NaturalSpawner.createState1( // Leaf - optimize mob spawning
|
|
mapped,
|
|
wrappedIterator,
|
|
- ServerChunkCache.this::getFullChunk,
|
|
+ this.level, // Leaf - optimize mob spawning
|
|
mobCapCalculator, // This is the key fix - was previously null
|
|
level.paperConfig().entities.spawning.perPlayerMobSpawns
|
|
);
|
|
@@ -610,6 +627,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
chunkRange = Math.min(chunkRange, 8);
|
|
entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange);
|
|
entityPlayer.playerNaturallySpawnedEvent.callEvent();
|
|
+ this.level.natureSpawnChunkMap.addPlayer(entityPlayer); // Leaf - optimize mob spawning
|
|
}
|
|
// Paper end - PlayerNaturallySpawnCreaturesEvent
|
|
boolean flag = this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) != 0L && this.level.getLevelData().getGameTime() % this.level.ticksPerSpawnCategory.getLong(org.bukkit.entity.SpawnCategory.ANIMAL) == 0L; // CraftBukkit
|
|
@@ -621,16 +639,40 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
List<LevelChunk> list = this.spawningChunks;
|
|
|
|
try {
|
|
- this.chunkMap.collectSpawningChunks(list);
|
|
+ // Leaf start - optimize mob spawning
|
|
+ // this.chunkMap.collectSpawningChunks(list);
|
|
+ this.level.natureSpawnChunkMap.build();
|
|
// Paper start - chunk tick iteration optimisation
|
|
- this.shuffleRandom.setSeed(this.level.random.nextLong());
|
|
- if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
|
|
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
|
+ this.level.natureSpawnChunkMap.collectSpawningChunks(this.level.moonrise$getPlayerTickingChunks(), list);
|
|
+ this.shuffleRandom.setSeed(this.level.random.nextLong()); // Leaf - paw optimization - Only set seed if is really used
|
|
+ Util.shuffle(list, this.shuffleRandom); // Paper - Optional per player mob spawns; do not need this when per-player is enabled
|
|
+ }
|
|
// Paper end - chunk tick iteration optimisation
|
|
|
|
- for (LevelChunk levelChunk : list) {
|
|
- this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, lastSpawnState); // Pufferfish
|
|
+ if (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get()) {
|
|
+ NaturalSpawner.SpawnState currentState = lastSpawnState;
|
|
+ if (currentState != null) {
|
|
+ currentState.applyPerPlayerMobCount(level);
|
|
+ if (!this.level.paperConfig().entities.spawning.perPlayerMobSpawns) {
|
|
+ for (LevelChunk levelChunk : list) {
|
|
+ this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, currentState); // Pufferfish
|
|
+ }
|
|
+ } else {
|
|
+ ca.spottedleaf.moonrise.common.list.ReferenceList<LevelChunk> chunks = this.level.moonrise$getPlayerTickingChunks();
|
|
+ LevelChunk[] raw = chunks.getRawDataUnchecked();
|
|
+ for (int i = 0, length = chunks.size(); i < length; i++) {
|
|
+ LevelChunk levelChunk = raw[i];
|
|
+ if (level.natureSpawnChunkMap.contains(levelChunk.locX, levelChunk.locZ)) {
|
|
+ this.tickSpawningChunk(levelChunk, timeInhabited, filteredSpawningCategories, currentState); // Pufferfish
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
+ // Leaf end - optimize mob spawning
|
|
} finally {
|
|
+ this.level.natureSpawnChunkMap.clear(); // Leaf - optimize mob spawning
|
|
list.clear();
|
|
}
|
|
|
|
@@ -648,7 +690,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
}
|
|
|
|
if (!spawnCategories.isEmpty()) {
|
|
- if (this.level.getWorldBorder().isWithinBounds(pos) && (!org.dreeam.leaf.config.modules.async.AsyncMobSpawning.enabled || _pufferfish_spawnCountsReady.get())) { // Paper - rewrite chunk system // Pufferfish
|
|
+ if (this.level.getWorldBorder().isWithinBounds(pos)) { // Paper - rewrite chunk system
|
|
NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories);
|
|
}
|
|
}
|
|
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
|
index fc61a7083232148397a0b330ce2a4d70ba38543d..d319e4d7e12c4bae63893ad25dcdc3ed59c88d97 100644
|
|
--- a/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
|
@@ -1114,6 +1114,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
|
|
private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking
|
|
|
|
+ public final org.dreeam.leaf.world.NatureSpawnChunkMap natureSpawnChunkMap = new org.dreeam.leaf.world.NatureSpawnChunkMap(); // Leaf - optimize mob spawning
|
|
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
|
|
final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting
|
|
ChunkPos pos = chunk.getPos();
|
|
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
|
index 81e176d17fb072f9ee531639abfe42134ae833a9..ab6fa7ed111ef16a0b6774c21988589ee2110c66 100644
|
|
--- a/net/minecraft/world/level/NaturalSpawner.java
|
|
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
|
@@ -68,6 +68,7 @@ public final class NaturalSpawner {
|
|
return createState(spawnableChunkCount, entities, chunkGetter, calculator, false);
|
|
}
|
|
|
|
+ @Deprecated // Leaf - optimize mob spawning
|
|
public static NaturalSpawner.SpawnState createState(
|
|
int spawnableChunkCount, Iterable<Entity> entities, NaturalSpawner.ChunkGetter chunkGetter, LocalMobCapCalculator calculator, final boolean countMobs
|
|
) {
|
|
@@ -108,9 +109,69 @@ public final class NaturalSpawner {
|
|
}
|
|
}
|
|
|
|
- return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator);
|
|
+ return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator, new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>()); // Leaf - optimize mob spawning
|
|
}
|
|
|
|
+ // Leaf start - optimize mob spawning
|
|
+ public static NaturalSpawner.SpawnState createState1(
|
|
+ int spawnableChunkCount, Iterable<Entity> entities, ServerLevel level, LocalMobCapCalculator calculator, final boolean countMobs
|
|
+ ) {
|
|
+ // Paper end - Optional per player mob spawns
|
|
+ PotentialCalculator potentialCalculator = new PotentialCalculator();
|
|
+ Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>();
|
|
+ it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> chunkCap = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>();
|
|
+ boolean countAllMobsForSpawning = level.paperConfig().entities.spawning.countAllMobsForSpawning;
|
|
+ for (Entity entity : entities) {
|
|
+ if (!(entity instanceof Mob mob && (mob.isPersistenceRequired() || mob.requiresCustomPersistence()))) {
|
|
+ MobCategory category = entity.getType().getCategory();
|
|
+ if (category != MobCategory.MISC) {
|
|
+ // Paper start - Only count natural spawns
|
|
+ if (!countAllMobsForSpawning &&
|
|
+ !(entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ||
|
|
+ entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
|
|
+ continue;
|
|
+ }
|
|
+ // Paper end - Only count natural spawns
|
|
+ BlockPos blockPos = entity.blockPosition();
|
|
+ LevelChunk chunk = level.chunkSource.fullChunks.get(ChunkPos.asLong(blockPos));
|
|
+ MobSpawnSettings.MobSpawnCost mobSpawnCost = getRoughBiome(blockPos, chunk).getMobSettings().getMobSpawnCost(entity.getType());
|
|
+ if (mobSpawnCost != null) {
|
|
+ potentialCalculator.addCharge(entity.blockPosition(), mobSpawnCost.charge());
|
|
+ }
|
|
+
|
|
+ if (calculator != null && entity instanceof Mob) { // Paper - Optional per player mob spawns
|
|
+ calculator.addMob(chunk.getPos(), category);
|
|
+ }
|
|
+
|
|
+ map.addTo(category, 1);
|
|
+ // Paper start - Optional per player mob spawns
|
|
+ if (countMobs) {
|
|
+ final int index = entity.getType().getCategory().ordinal();
|
|
+ ++chunkCap.computeIfAbsent(chunk.getPos().longKey, k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index];
|
|
+ /*
|
|
+ final int index = entity.getType().getCategory().ordinal();
|
|
+ final var inRange = level.moonrise$getNearbyPlayers().getPlayers(entity.chunkPosition(), NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
|
|
+ if (inRange == null) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ final net.minecraft.server.level.ServerPlayer[] backingSet = inRange.getRawDataUnchecked();
|
|
+ for (int i = 0, len = inRange.size(); i < len; i++) {
|
|
+ final net.minecraft.server.level.ServerPlayer player = backingSet[i];
|
|
+ if (player == null) continue;
|
|
+ ++playerCap.computeIfAbsent(player, k -> new int[net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS])[index];
|
|
+ }
|
|
+ */
|
|
+ }
|
|
+ // Paper end - Optional per player mob spawns
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return new NaturalSpawner.SpawnState(spawnableChunkCount, map, potentialCalculator, calculator, chunkCap);
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
+
|
|
static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) {
|
|
return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value();
|
|
}
|
|
@@ -265,28 +326,67 @@ public final class NaturalSpawner {
|
|
MobCategory category, ServerLevel level, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate filter, NaturalSpawner.AfterSpawnCallback callback, final int maxSpawns, final @Nullable Consumer<Entity> trackEntity, final boolean nothing
|
|
// Paper end - throttle failed spawn attempts
|
|
) {
|
|
+ // Leaf start - optimize mob spawning
|
|
+ if (!(chunk instanceof LevelChunk levelChunk)) {
|
|
+ // unreachable
|
|
+ return 0;
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
// Paper end - Optional per player mob spawns
|
|
StructureManager structureManager = level.structureManager();
|
|
ChunkGenerator generator = level.getChunkSource().getGenerator();
|
|
int y = pos.getY();
|
|
+ // Leaf start - optimize mob spawning
|
|
+ int posX = pos.getX();
|
|
+ int posZ = pos.getZ();
|
|
int i = 0; // Paper - throttle failed spawn attempts
|
|
- BlockState blockState = level.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn
|
|
+ BlockState blockState = level.getWorldBorder().isWithinBounds(pos) ? (ChunkPos.asLong(pos) == levelChunk.getPos().longKey ? levelChunk.getBlockStateFinal(posX, y, posZ) : level.getBlockStateIfLoaded(pos)) : null; // Paper - don't load chunks for mob spawn // Leaf
|
|
if (blockState != null && !blockState.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
//int i = 0; // Paper - throttle failed spawn attempts - move up
|
|
-
|
|
+ // 4 * (3 * ([1, 4] * 4)) + 3 * 1 * 2
|
|
+ long rand = level.random.nextLong();
|
|
+ int bits = 0;
|
|
for (int i1 = 0; i1 < 3; i1++) {
|
|
- int x = pos.getX();
|
|
- int z = pos.getZ();
|
|
- int i2 = 6;
|
|
+ int x = posX;
|
|
+ int z = posZ;
|
|
+ //int i2 = 6;
|
|
MobSpawnSettings.SpawnerData spawnerData = null;
|
|
SpawnGroupData spawnGroupData = null;
|
|
- int ceil = Mth.ceil(level.random.nextFloat() * 4.0F);
|
|
+ //int ceil = Mth.ceil(level.random.nextFloat() * 4.0F);
|
|
+ int ceil = (int) (((rand >>> bits) & 0x3L) + 1);
|
|
+ bits += 2;
|
|
+ if (bits >= 62) {
|
|
+ rand = level.random.nextInt();
|
|
+ bits = 0;
|
|
+ }
|
|
int i3 = 0;
|
|
|
|
for (int i4 = 0; i4 < ceil; i4++) {
|
|
- x += level.random.nextInt(6) - level.random.nextInt(6);
|
|
- z += level.random.nextInt(6) - level.random.nextInt(6);
|
|
+ int rand1=0,rand2=0,rand3=0,rand4=0,valuesNeeded=4;
|
|
+ while (valuesNeeded > 0) {
|
|
+ // [0, 61] 3 remains
|
|
+ int threeBits = (int) ((rand >>> bits) & 0x7L);
|
|
+ bits += 3;
|
|
+ if (threeBits != 7 && threeBits != 6) {
|
|
+ switch (valuesNeeded) {
|
|
+ case 1 -> rand4 = threeBits;
|
|
+ case 2 -> rand3 = threeBits;
|
|
+ case 3 -> rand2 = threeBits;
|
|
+ case 4 -> rand1 = threeBits;
|
|
+ }
|
|
+ valuesNeeded--;
|
|
+ }
|
|
+ if (bits >= 62) {
|
|
+ rand = level.random.nextInt();
|
|
+ bits = 0;
|
|
+ }
|
|
+ }
|
|
+ x += rand1 - rand2;
|
|
+ z += rand3 - rand4;
|
|
+ // x += level.random.nextInt(6) - level.random.nextInt(6);
|
|
+ // z += level.random.nextInt(6) - level.random.nextInt(6);
|
|
+ // Leaf end - optimize mob spawning
|
|
mutableBlockPos.set(x, y, z);
|
|
double d = x + 0.5;
|
|
double d1 = z + 0.5;
|
|
@@ -295,8 +395,8 @@ public final class NaturalSpawner {
|
|
double d2 = nearestPlayer.distanceToSqr(d, y, d1);
|
|
if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn
|
|
if (spawnerData == null) {
|
|
- Optional<MobSpawnSettings.SpawnerData> randomSpawnMobAt = getRandomSpawnMobAt(
|
|
- level, structureManager, generator, category, level.random, mutableBlockPos
|
|
+ Optional<MobSpawnSettings.SpawnerData> randomSpawnMobAt = getRandomSpawnMobAtWithChunk( // Leaf - optimize mob spawning
|
|
+ level, structureManager, generator, category, level.random, mutableBlockPos, levelChunk // Leaf - optimize mob spawning
|
|
);
|
|
if (randomSpawnMobAt.isEmpty()) {
|
|
break;
|
|
@@ -307,7 +407,7 @@ public final class NaturalSpawner {
|
|
}
|
|
|
|
// Paper start - PreCreatureSpawnEvent
|
|
- PreSpawnStatus doSpawning = isValidSpawnPostitionForType(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2);
|
|
+ PreSpawnStatus doSpawning = isValidSpawnPostitionForTypeWithChunk(level, category, structureManager, generator, spawnerData, mutableBlockPos, d2, levelChunk); // Leaf
|
|
// Paper start - per player mob count backoff
|
|
if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) {
|
|
level.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(mutableBlockPos.getX() >> 4, mutableBlockPos.getZ() >> 4, category);
|
|
@@ -414,6 +514,44 @@ public final class NaturalSpawner {
|
|
&& level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5));
|
|
return success ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper - PreCreatureSpawnEvent
|
|
}
|
|
+ // Leaf start - optimize mob spawning
|
|
+ private static PreSpawnStatus isValidSpawnPostitionForTypeWithChunk(
|
|
+ // Paper end - PreCreatureSpawnEvent
|
|
+ ServerLevel level,
|
|
+ MobCategory category,
|
|
+ StructureManager structureManager,
|
|
+ ChunkGenerator generator,
|
|
+ MobSpawnSettings.SpawnerData data,
|
|
+ BlockPos.MutableBlockPos pos,
|
|
+ double distance,
|
|
+ LevelChunk chunk
|
|
+ ) {
|
|
+ EntityType<?> entityType = data.type();
|
|
+ // Paper start - PreCreatureSpawnEvent
|
|
+ com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
|
|
+ org.bukkit.craftbukkit.util.CraftLocation.toBukkit(pos, level),
|
|
+ org.bukkit.craftbukkit.entity.CraftEntityType.minecraftToBukkit(entityType), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL
|
|
+ );
|
|
+ if (!event.callEvent()) {
|
|
+ if (event.shouldAbortSpawn()) {
|
|
+ return PreSpawnStatus.ABORT;
|
|
+ }
|
|
+ return PreSpawnStatus.CANCELLED;
|
|
+ }
|
|
+ final boolean success = entityType.getCategory() != MobCategory.MISC
|
|
+ // Paper end - PreCreatureSpawnEvent
|
|
+ && (
|
|
+ entityType.canSpawnFarFromPlayer()
|
|
+ || !(distance > entityType.getCategory().getDespawnDistance() * entityType.getCategory().getDespawnDistance())
|
|
+ )
|
|
+ && entityType.canSummon()
|
|
+ && mobsAtWithChunk(level, structureManager, generator, category, pos, null, chunk).contains(data)
|
|
+ && SpawnPlacements.isSpawnPositionOk(entityType, level, pos)
|
|
+ && SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random)
|
|
+ && level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5));
|
|
+ return success ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper - PreCreatureSpawnEvent
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
|
|
@Nullable
|
|
private static Mob getMobForSpawn(ServerLevel level, EntityType<?> entityType) {
|
|
@@ -449,6 +587,17 @@ public final class NaturalSpawner {
|
|
: mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random);
|
|
}
|
|
|
|
+ // Leaf start - optimize mob spawning
|
|
+ private static Optional<MobSpawnSettings.SpawnerData> getRandomSpawnMobAtWithChunk(
|
|
+ ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos, LevelChunk chunk
|
|
+ ) {
|
|
+ Holder<Biome> biome = org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(chunk, pos) : level.getBiome(pos); // Leaf - cache getBiome
|
|
+ return category == MobCategory.WATER_AMBIENT && biome.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F
|
|
+ ? Optional.empty()
|
|
+ : mobsAtWithChunk(level, structureManager, generator, category, pos, biome, chunk).getRandom(random);
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
+
|
|
private static boolean canSpawnMobAt(
|
|
ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, MobSpawnSettings.SpawnerData data, BlockPos pos
|
|
) {
|
|
@@ -463,6 +612,16 @@ public final class NaturalSpawner {
|
|
: generator.getMobsAt(biome != null ? biome : (org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(null, pos) : level.getBiome(pos)), structureManager, cetagory, pos); // Leaf - cache getBiome
|
|
}
|
|
|
|
+ // Leaf start - optimize mob spawning
|
|
+ private static WeightedList<MobSpawnSettings.SpawnerData> mobsAtWithChunk(
|
|
+ ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory cetagory, BlockPos pos, @Nullable Holder<Biome> biome, LevelChunk chunk
|
|
+ ) {
|
|
+ return isInNetherFortressBoundsChunk(pos, level, cetagory, structureManager, chunk)
|
|
+ ? NetherFortressStructure.FORTRESS_ENEMIES
|
|
+ : generator.getMobsAtChunk(biome != null ? biome : (org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(chunk, pos) : level.getBiome(pos)), structureManager, cetagory, pos, chunk); // Leaf - cache getBiome
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
+
|
|
public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) {
|
|
if (category == MobCategory.MONSTER && level.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)) {
|
|
Structure structure = structureManager.registryAccess().lookupOrThrow(Registries.STRUCTURE).getValue(BuiltinStructures.FORTRESS);
|
|
@@ -472,6 +631,17 @@ public final class NaturalSpawner {
|
|
}
|
|
}
|
|
|
|
+ // Leaf start - optimize mob spawning
|
|
+ public static boolean isInNetherFortressBoundsChunk(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager, LevelChunk chunk) {
|
|
+ if (category == MobCategory.MONSTER && ((chunk.getPos().longKey == ChunkPos.asLong(pos) ? chunk.getBlockStateFinal(pos.getX(), pos.getY() - 1, pos.getZ()).is(Blocks.NETHER_BRICKS) : level.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)))) {
|
|
+ Structure structure = structureManager.registryAccess().lookupOrThrow(Registries.STRUCTURE).getValue(BuiltinStructures.FORTRESS);
|
|
+ return structure != null && structureManager.getStructureAt(pos, structure).isValid();
|
|
+ } else {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
+
|
|
private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) {
|
|
ChunkPos pos = chunk.getPos();
|
|
int i = pos.getMinBlockX() + level.random.nextInt(16);
|
|
@@ -612,18 +782,21 @@ public final class NaturalSpawner {
|
|
@Nullable
|
|
private EntityType<?> lastCheckedType;
|
|
private double lastCharge;
|
|
+ public final it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> chunkCap; // Leaf - optimize mob spawning
|
|
|
|
SpawnState(
|
|
int spawnableChunkCount,
|
|
Object2IntOpenHashMap<MobCategory> mobCategoryCounts,
|
|
PotentialCalculator spawnPotential,
|
|
- LocalMobCapCalculator localMobCapCalculator
|
|
+ LocalMobCapCalculator localMobCapCalculator,
|
|
+ it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<int[]> playerCap // Leaf - optimize mob spawning
|
|
) {
|
|
this.spawnableChunkCount = spawnableChunkCount;
|
|
this.mobCategoryCounts = mobCategoryCounts;
|
|
this.spawnPotential = spawnPotential;
|
|
this.localMobCapCalculator = localMobCapCalculator;
|
|
this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(mobCategoryCounts);
|
|
+ this.chunkCap = playerCap; // Leaf - optimize mob spawning
|
|
}
|
|
|
|
private boolean canSpawn(EntityType<?> entityType, BlockPos pos, ChunkAccess chunk) {
|
|
@@ -680,5 +853,32 @@ public final class NaturalSpawner {
|
|
boolean canSpawnForCategoryLocal(MobCategory category, ChunkPos chunkPos) {
|
|
return this.localMobCapCalculator.canSpawn(category, chunkPos);
|
|
}
|
|
+
|
|
+ // Leaf start - optimize mob spawning
|
|
+ public void applyPerPlayerMobCount(ServerLevel level) {
|
|
+ if (chunkCap.isEmpty()) {
|
|
+ return;
|
|
+ }
|
|
+ final var iterator = chunkCap.long2ObjectEntrySet().fastIterator();
|
|
+ final ca.spottedleaf.moonrise.common.misc.NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers();
|
|
+ while (iterator.hasNext()) {
|
|
+ var entry = iterator.next();
|
|
+ long chunk = entry.getLongKey();
|
|
+ int[] cap = entry.getValue();
|
|
+ ca.spottedleaf.moonrise.common.list.ReferenceList<net.minecraft.server.level.ServerPlayer> players = nearbyPlayers.getPlayersByChunk(ChunkPos.getX(chunk), ChunkPos.getZ(chunk), ca.spottedleaf.moonrise.common.misc.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE);
|
|
+ if (players == null) {
|
|
+ continue;
|
|
+ }
|
|
+ int playersSize = players.size();
|
|
+ net.minecraft.server.level.ServerPlayer[] playersRawDataUnchecked = players.getRawDataUnchecked();
|
|
+ for (int i = 0; i < playersSize; i++) {
|
|
+ int[] p = playersRawDataUnchecked[i].mobCounts;
|
|
+ for (int j = 0; j < net.minecraft.server.level.ServerPlayer.MOBCATEGORY_TOTAL_ENUMS; j++) {
|
|
+ p[j] += cap[j];
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
}
|
|
}
|
|
diff --git a/net/minecraft/world/level/StructureManager.java b/net/minecraft/world/level/StructureManager.java
|
|
index fbe93098ce0366054a6da857cd808af1431b6612..57de70773d2766a8f3a41e61efc16ceb7a9f80c8 100644
|
|
--- a/net/minecraft/world/level/StructureManager.java
|
|
+++ b/net/minecraft/world/level/StructureManager.java
|
|
@@ -90,6 +90,7 @@ public class StructureManager {
|
|
|
|
@Nullable
|
|
public StructureStart getStartForStructure(SectionPos sectionPos, Structure structure, StructureAccess structureAccess) {
|
|
+ // Leaf - optimize mob spawning - diff
|
|
return structureAccess.getStartForStructure(structure);
|
|
}
|
|
|
|
@@ -181,6 +182,12 @@ public class StructureManager {
|
|
//SectionPos sectionPos = SectionPos.of(pos); // Leaf - optimise ChunkGenerator#getMobsAt
|
|
return this.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, ChunkStatus.STRUCTURE_REFERENCES).getAllReferences(); // Leaf - optimise ChunkGenerator#getMobsAt
|
|
}
|
|
+ // Leaf start - optimize mob spawning
|
|
+ public Map<Structure, LongSet> getAllStructuresAtChunk(net.minecraft.world.level.chunk.ChunkAccess chunk) {
|
|
+ //SectionPos sectionPos = SectionPos.of(pos); // Leaf - optimise ChunkGenerator#getMobsAt
|
|
+ return chunk.getAllReferences(); // Leaf - optimise ChunkGenerator#getMobsAt
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
|
|
public StructureCheckResult checkStructurePresence(ChunkPos chunkPos, Structure structure, StructurePlacement placement, boolean skipKnownStructures) {
|
|
return this.structureCheck.checkStart(chunkPos, structure, placement, skipKnownStructures);
|
|
diff --git a/net/minecraft/world/level/biome/MobSpawnSettings.java b/net/minecraft/world/level/biome/MobSpawnSettings.java
|
|
index db3b8a237d63255aa9ffd70c88a093002a6bd770..4a69f404eee00d8972e9501a76031d4339136b6f 100644
|
|
--- a/net/minecraft/world/level/biome/MobSpawnSettings.java
|
|
+++ b/net/minecraft/world/level/biome/MobSpawnSettings.java
|
|
@@ -52,7 +52,7 @@ public class MobSpawnSettings {
|
|
Map<EntityType<?>, MobSpawnSettings.MobSpawnCost> mobSpawnCosts
|
|
) {
|
|
this.creatureGenerationProbability = creatureGenerationProbability;
|
|
- this.spawners = ImmutableMap.copyOf(spawners);
|
|
+ this.spawners = com.google.common.collect.Maps.immutableEnumMap(spawners); // Leaf - optimize mob spawning
|
|
this.mobSpawnCosts = ImmutableMap.copyOf(mobSpawnCosts);
|
|
}
|
|
|
|
diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java
|
|
index 11c7c299d4affb9e78488590e7db939efe6e3dd9..f1675db9942751235bb31634d5b99fdc30fb2950 100644
|
|
--- a/net/minecraft/world/level/chunk/ChunkGenerator.java
|
|
+++ b/net/minecraft/world/level/chunk/ChunkGenerator.java
|
|
@@ -516,6 +516,35 @@ public abstract class ChunkGenerator {
|
|
return biome.value().getMobSettings().getMobs(category);
|
|
}
|
|
|
|
+ // Leaf start - optimize mob spawning
|
|
+ public WeightedList<MobSpawnSettings.SpawnerData> getMobsAtChunk(Holder<Biome> biome, StructureManager structureManager, MobCategory category, BlockPos pos, LevelChunk chunk) {
|
|
+ Map<Structure, LongSet> allStructuresAt = structureManager.getAllStructuresAtChunk(ChunkPos.asLong(pos) == chunk.getPos().longKey ? chunk : structureManager.level.getChunk(pos.getX() >> 4, pos.getZ() >> 4, ChunkStatus.STRUCTURE_REFERENCES));
|
|
+
|
|
+ for (Entry<Structure, LongSet> entry : allStructuresAt.entrySet()) {
|
|
+ Structure structure = entry.getKey();
|
|
+ StructureSpawnOverride structureSpawnOverride = structure.spawnOverrides().get(category);
|
|
+ if (structureSpawnOverride != null) {
|
|
+ // Leaf start - optimise ChunkGenerator#getMobsAt
|
|
+ for (long l : entry.getValue()) {
|
|
+ StructureStart startForStructure = structureManager.getStartForStructure(
|
|
+ null, structure, chunk.getPos().longKey == l ? chunk : structureManager.level.getChunk(ChunkPos.getX(l), ChunkPos.getZ(l), ChunkStatus.STRUCTURE_STARTS)
|
|
+ );
|
|
+ if (startForStructure != null && startForStructure.isValid()) {
|
|
+ if (structureSpawnOverride.boundingBox() == StructureSpawnOverride.BoundingBoxType.PIECE
|
|
+ ? structureManager.structureHasPieceAt(pos, startForStructure)
|
|
+ : startForStructure.getBoundingBox().isInside(pos)) {
|
|
+ return structureSpawnOverride.spawns();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ // Leaf end - optimise ChunkGenerator#getMobsAt
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return biome.value().getMobSettings().getMobs(category);
|
|
+ }
|
|
+ // Leaf end - optimize mob spawning
|
|
+
|
|
public void createStructures(
|
|
RegistryAccess registryAccess,
|
|
ChunkGeneratorStructureState structureState,
|