mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-19 15:09:25 +00:00
backport from dev/1.21.6
73ffcb09faoptimize mob spawning333373d204fix async mob spawning data race7c9f88e4f8[ci skip] cleanupbf9486f0f0remove hash lookup in optimize random tick915ac01cd3cleanupb90c1cd527fix playermobcaps commandfd34d9f626cleanup5d663b4d36optimize collectSpawningChunks (#382)
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: Taiyou06 <kaandindar21@gmail.com>
|
From: Taiyou06 <kaandindar21@gmail.com>
|
||||||
Date: Sun, 2 Mar 2025 21:23:20 +0100
|
Date: Sun, 2 Mar 2025 21:23:20 +0100
|
||||||
Subject: [PATCH] Async chunk send
|
Subject: [PATCH] Async chunk sending
|
||||||
|
|
||||||
|
|
||||||
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||||
index 32608df3da169159c070f37cb55407f4f6187744..3a78e7512772fd3f7cf8f221e3a72474def14bea 100644
|
index 32608df3da169159c070f37cb55407f4f6187744..fc3901acdfcdad85fbd435ca21869388a90b2207 100644
|
||||||
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
--- a/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||||
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
+++ b/ca/spottedleaf/moonrise/patches/chunk_system/player/RegionizedPlayerChunkLoader.java
|
||||||
@@ -436,7 +436,15 @@ public final class RegionizedPlayerChunkLoader {
|
@@ -436,7 +436,15 @@ public final class RegionizedPlayerChunkLoader {
|
||||||
@@ -13,7 +13,7 @@ index 32608df3da169159c070f37cb55407f4f6187744..3a78e7512772fd3f7cf8f221e3a72474
|
|||||||
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
((ChunkSystemChunkHolder)((ChunkSystemServerLevel)this.world).moonrise$getChunkTaskScheduler().chunkHolderManager
|
||||||
.getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$removeReceivedChunk(this.player);
|
.getChunkHolder(chunkX, chunkZ).vanillaChunkHolder).moonrise$removeReceivedChunk(this.player);
|
||||||
- this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
|
- this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
|
||||||
+ // Leaf start - Async chunk send
|
+ // Leaf start - Async chunk sending
|
||||||
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
||||||
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
||||||
+ () -> this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)))
|
+ () -> this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)))
|
||||||
@@ -21,26 +21,26 @@ index 32608df3da169159c070f37cb55407f4f6187744..3a78e7512772fd3f7cf8f221e3a72474
|
|||||||
+ } else {
|
+ } else {
|
||||||
+ this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
|
+ this.player.connection.send(new ClientboundForgetLevelChunkPacket(new ChunkPos(chunkX, chunkZ)));
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - Async chunk send
|
+ // Leaf end - Async chunk sending
|
||||||
// Paper start - PlayerChunkUnloadEvent
|
// Paper start - PlayerChunkUnloadEvent
|
||||||
if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
||||||
new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(new ChunkPos(chunkX, chunkZ).longKey), player.getBukkitEntity()).callEvent();
|
new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(new ChunkPos(chunkX, chunkZ).longKey), player.getBukkitEntity()).callEvent();
|
||||||
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||||
index 526c117e0d53ad527eb610c79cdc46ec16b18c0c..9151e580b4840fddab04e487d723130a5a769a1a 100644
|
index 526c117e0d53ad527eb610c79cdc46ec16b18c0c..9e5edbb05900fdaa8b9deed0fc65e9ca31fe6d61 100644
|
||||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
|
||||||
@@ -75,6 +75,45 @@ public class ClientboundLevelChunkPacketData {
|
@@ -75,6 +75,45 @@ public class ClientboundLevelChunkPacketData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+ // Leaf start - Async chunk send
|
+ // Leaf start - Async chunk sending
|
||||||
+ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo, BlockEntity[] blockEntities, Map<Heightmap.Types, long[]> heightmaps) {
|
+ public ClientboundLevelChunkPacketData(LevelChunk levelChunk, io.papermc.paper.antixray.ChunkPacketInfo<net.minecraft.world.level.block.state.BlockState> chunkPacketInfo, BlockEntity[] blockEntities, Map<Heightmap.Types, long[]> heightmaps) {
|
||||||
+ this.heightmaps = heightmaps;
|
+ this.heightmaps = heightmaps;
|
||||||
+
|
+
|
||||||
+ if (Thread.currentThread() instanceof org.dreeam.leaf.async.chunk.AsyncChunkSendThread) {
|
+ if (Thread.currentThread() instanceof org.dreeam.leaf.async.chunk.AsyncChunkSendThread) {
|
||||||
+ var buffer = new io.netty.buffer.UnpooledByteBufAllocator(false).buffer(calculateChunkSize(levelChunk));
|
+ ByteBuf buffer = Unpooled.buffer(calculateChunkSize(levelChunk));
|
||||||
+ extractChunkData(new FriendlyByteBuf(buffer), levelChunk, chunkPacketInfo);
|
+ extractChunkData(new FriendlyByteBuf(buffer), levelChunk, chunkPacketInfo);
|
||||||
+ var array = it.unimi.dsi.fastutil.bytes.ByteArrays.trim(buffer.array(), buffer.writerIndex());
|
+ byte[] array = it.unimi.dsi.fastutil.bytes.ByteArrays.setLength(buffer.array(), buffer.writerIndex());
|
||||||
+ if (chunkPacketInfo != null) {
|
+ if (chunkPacketInfo != null) {
|
||||||
+ chunkPacketInfo.setBuffer(array);
|
+ chunkPacketInfo.setBuffer(array);
|
||||||
+ }
|
+ }
|
||||||
@@ -70,20 +70,20 @@ index 526c117e0d53ad527eb610c79cdc46ec16b18c0c..9151e580b4840fddab04e487d723130a
|
|||||||
+ this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(blockEntity));
|
+ this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(blockEntity));
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - Async chunk send
|
+ // Leaf end - Async chunk sending
|
||||||
+
|
+
|
||||||
public ClientboundLevelChunkPacketData(RegistryFriendlyByteBuf buffer, int x, int z) {
|
public ClientboundLevelChunkPacketData(RegistryFriendlyByteBuf buffer, int x, int z) {
|
||||||
this.heightmaps = HEIGHTMAPS_STREAM_CODEC.decode(buffer);
|
this.heightmaps = HEIGHTMAPS_STREAM_CODEC.decode(buffer);
|
||||||
int varInt = buffer.readVarInt();
|
int varInt = buffer.readVarInt();
|
||||||
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
diff --git a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||||
index 8578d1f78ddd1bb75f3230f04bfaa35af9f5f822..4f54c4c8e49c1e0352ab2c5c23277b4103504c55 100644
|
index 8578d1f78ddd1bb75f3230f04bfaa35af9f5f822..d8e938abf5123b092cec80feb6468e3d91ae823e 100644
|
||||||
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
--- a/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||||
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
+++ b/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
|
||||||
@@ -44,6 +44,17 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
|
@@ -44,6 +44,17 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
|
||||||
this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
|
this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
|
||||||
chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
||||||
}
|
}
|
||||||
+ // Leaf start - Async chunk send
|
+ // Leaf start - Async chunk sending
|
||||||
+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight, boolean modifyBlocks, net.minecraft.world.level.block.entity.BlockEntity[] blockEntities, java.util.Map<net.minecraft.world.level.levelgen.Heightmap.Types, long[]> heightmaps) {
|
+ public ClientboundLevelChunkWithLightPacket(LevelChunk chunk, LevelLightEngine lightEngine, @Nullable BitSet skyLight, @Nullable BitSet blockLight, boolean modifyBlocks, net.minecraft.world.level.block.entity.BlockEntity[] blockEntities, java.util.Map<net.minecraft.world.level.levelgen.Heightmap.Types, long[]> heightmaps) {
|
||||||
+ ChunkPos pos = chunk.getPos();
|
+ ChunkPos pos = chunk.getPos();
|
||||||
+ this.x = pos.x;
|
+ this.x = pos.x;
|
||||||
@@ -93,12 +93,12 @@ index 8578d1f78ddd1bb75f3230f04bfaa35af9f5f822..4f54c4c8e49c1e0352ab2c5c23277b41
|
|||||||
+ this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
|
+ this.lightData = new ClientboundLightUpdatePacketData(pos, lightEngine, skyLight, blockLight);
|
||||||
+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
+ chunk.getLevel().chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - Async chunk send
|
+ // Leaf end - Async chunk sending
|
||||||
|
|
||||||
private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) {
|
private ClientboundLevelChunkWithLightPacket(RegistryFriendlyByteBuf buffer) {
|
||||||
this.x = buffer.readInt();
|
this.x = buffer.readInt();
|
||||||
diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java
|
diff --git a/net/minecraft/server/network/PlayerChunkSender.java b/net/minecraft/server/network/PlayerChunkSender.java
|
||||||
index 14878690a88fd4de3e2c127086607e6c819c636c..69581890ab34af20f9c608678f378ec9fe3ec775 100644
|
index 14878690a88fd4de3e2c127086607e6c819c636c..62e1dabd84b80d5e2e4a8fcb308934dce2d8c74b 100644
|
||||||
--- a/net/minecraft/server/network/PlayerChunkSender.java
|
--- a/net/minecraft/server/network/PlayerChunkSender.java
|
||||||
+++ b/net/minecraft/server/network/PlayerChunkSender.java
|
+++ b/net/minecraft/server/network/PlayerChunkSender.java
|
||||||
@@ -64,13 +64,29 @@ public class PlayerChunkSender {
|
@@ -64,13 +64,29 @@ public class PlayerChunkSender {
|
||||||
@@ -106,7 +106,7 @@ index 14878690a88fd4de3e2c127086607e6c819c636c..69581890ab34af20f9c608678f378ec9
|
|||||||
ServerGamePacketListenerImpl serverGamePacketListenerImpl = player.connection;
|
ServerGamePacketListenerImpl serverGamePacketListenerImpl = player.connection;
|
||||||
this.unacknowledgedBatches++;
|
this.unacknowledgedBatches++;
|
||||||
- serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE);
|
- serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE);
|
||||||
+ // Leaf start - Async chunk send
|
+ // Leaf start - Async chunk sending
|
||||||
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
||||||
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
||||||
+ () -> serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE)
|
+ () -> serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE)
|
||||||
@@ -114,14 +114,14 @@ index 14878690a88fd4de3e2c127086607e6c819c636c..69581890ab34af20f9c608678f378ec9
|
|||||||
+ } else {
|
+ } else {
|
||||||
+ serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE);
|
+ serverGamePacketListenerImpl.send(ClientboundChunkBatchStartPacket.INSTANCE);
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - Async chunk send
|
+ // Leaf end - Async chunk sending
|
||||||
|
|
||||||
for (LevelChunk levelChunk : list) {
|
for (LevelChunk levelChunk : list) {
|
||||||
sendChunk(serverGamePacketListenerImpl, serverLevel, levelChunk);
|
sendChunk(serverGamePacketListenerImpl, serverLevel, levelChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
- serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()));
|
- serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()));
|
||||||
+ // Leaf start - Async chunk send
|
+ // Leaf start - Async chunk sending
|
||||||
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
||||||
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
+ org.dreeam.leaf.async.chunk.AsyncChunkSend.POOL.execute(
|
||||||
+ () -> serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()))
|
+ () -> serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()))
|
||||||
@@ -129,7 +129,7 @@ index 14878690a88fd4de3e2c127086607e6c819c636c..69581890ab34af20f9c608678f378ec9
|
|||||||
+ } else {
|
+ } else {
|
||||||
+ serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()));
|
+ serverGamePacketListenerImpl.send(new ClientboundChunkBatchFinishedPacket(list.size()));
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - Async chunk send
|
+ // Leaf end - Async chunk sending
|
||||||
this.batchQuota = this.batchQuota - list.size();
|
this.batchQuota = this.batchQuota - list.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ index 14878690a88fd4de3e2c127086607e6c819c636c..69581890ab34af20f9c608678f378ec9
|
|||||||
public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
|
public static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
|
||||||
final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk);
|
final boolean shouldModify = level.chunkPacketBlockController.shouldModify(packetListener.player, chunk);
|
||||||
- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
|
- packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
|
||||||
+ // Leaf start - Async chunk send
|
+ // Leaf start - Async chunk sending
|
||||||
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
+ if (org.dreeam.leaf.config.modules.async.AsyncChunkSend.enabled) {
|
||||||
+ var blockEntities = chunk.blockEntities.values().toArray(new net.minecraft.world.level.block.entity.BlockEntity[0]);
|
+ var blockEntities = chunk.blockEntities.values().toArray(new net.minecraft.world.level.block.entity.BlockEntity[0]);
|
||||||
+ java.util.Map<net.minecraft.world.level.levelgen.Heightmap.Types, long[]> heightmaps = new java.util.concurrent.ConcurrentHashMap<>();
|
+ java.util.Map<net.minecraft.world.level.levelgen.Heightmap.Types, long[]> heightmaps = new java.util.concurrent.ConcurrentHashMap<>();
|
||||||
@@ -154,12 +154,12 @@ index 14878690a88fd4de3e2c127086607e6c819c636c..69581890ab34af20f9c608678f378ec9
|
|||||||
+ } else {
|
+ } else {
|
||||||
+ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
|
+ packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null, shouldModify));
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - Async chunk send
|
+ // Leaf end - Async chunk sending
|
||||||
// Paper end - Anti-Xray
|
// Paper end - Anti-Xray
|
||||||
// Paper start - PlayerChunkLoadEvent
|
// Paper start - PlayerChunkLoadEvent
|
||||||
if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
||||||
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
diff --git a/net/minecraft/world/level/chunk/LevelChunkSection.java b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||||
index 36c033b0ee63dfc273d721fb4b614733e8fdef19..1cc33a038060aaf5258ee4f1deb19b4a1be59a29 100644
|
index 36c033b0ee63dfc273d721fb4b614733e8fdef19..4d06df242ab73411bdefc4770e131b27a6ea668a 100644
|
||||||
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
|
--- a/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||||
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
+++ b/net/minecraft/world/level/chunk/LevelChunkSection.java
|
||||||
@@ -18,7 +18,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
@@ -18,7 +18,7 @@ public class LevelChunkSection implements ca.spottedleaf.moonrise.patches.block_
|
||||||
@@ -167,7 +167,7 @@ index 36c033b0ee63dfc273d721fb4b614733e8fdef19..1cc33a038060aaf5258ee4f1deb19b4a
|
|||||||
public static final int SECTION_SIZE = 4096;
|
public static final int SECTION_SIZE = 4096;
|
||||||
public static final int BIOME_CONTAINER_BITS = 2;
|
public static final int BIOME_CONTAINER_BITS = 2;
|
||||||
- short nonEmptyBlockCount; // Paper - package private
|
- short nonEmptyBlockCount; // Paper - package private
|
||||||
+ volatile short nonEmptyBlockCount; // Paper - package private // Leaf - Async chunk send - volatile
|
+ volatile short nonEmptyBlockCount; // Paper - package private // Leaf - Async chunk sending - volatile
|
||||||
private short tickingBlockCount;
|
private short tickingBlockCount;
|
||||||
private short tickingFluidCount;
|
private short tickingFluidCount;
|
||||||
private boolean isRandomlyTickingBlocksStatus; // Leaf - Cache random tick block status
|
private boolean isRandomlyTickingBlocksStatus; // Leaf - Cache random tick block status
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: hayanesuru <hayanesuru@outlook.jp>
|
From: hayanesuru <hayanesuru@outlook.jp>
|
||||||
Date: Tue, 3 Jun 2025 15:20:40 +0900
|
Date: Tue, 3 Jun 2025 15:20:40 +0900
|
||||||
Subject: [PATCH] optimise getBiome
|
Subject: [PATCH] cache getBiome
|
||||||
|
|
||||||
|
|
||||||
diff --git a/net/minecraft/advancements/critereon/LocationPredicate.java b/net/minecraft/advancements/critereon/LocationPredicate.java
|
diff --git a/net/minecraft/advancements/critereon/LocationPredicate.java b/net/minecraft/advancements/critereon/LocationPredicate.java
|
||||||
index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..65e4315cce35814c60b21bbd5baea2ffac82162c 100644
|
index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..7ba5c85bbce8528a4df072e63948673300630a9d 100644
|
||||||
--- a/net/minecraft/advancements/critereon/LocationPredicate.java
|
--- a/net/minecraft/advancements/critereon/LocationPredicate.java
|
||||||
+++ b/net/minecraft/advancements/critereon/LocationPredicate.java
|
+++ b/net/minecraft/advancements/critereon/LocationPredicate.java
|
||||||
@@ -49,7 +49,7 @@ public record LocationPredicate(
|
@@ -49,7 +49,7 @@ public record LocationPredicate(
|
||||||
@@ -13,12 +13,12 @@ index a26a5311f87873e0d4d26fda9cb8956a32ee81e8..65e4315cce35814c60b21bbd5baea2ff
|
|||||||
BlockPos blockPos = BlockPos.containing(x, y, z);
|
BlockPos blockPos = BlockPos.containing(x, y, z);
|
||||||
boolean isLoaded = level.isLoaded(blockPos);
|
boolean isLoaded = level.isLoaded(blockPos);
|
||||||
- return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(level.getBiome(blockPos)))
|
- return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(level.getBiome(blockPos)))
|
||||||
+ return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(org.dreeam.leaf.config.modules.opt.OptimizeBiome.advancement ? level.getBiomeCached(blockPos) : level.getBiome(blockPos))) // Leaf - cache getBiome
|
+ return (!this.biomes.isPresent() || isLoaded && this.biomes.get().contains(org.dreeam.leaf.config.modules.opt.OptimizeBiome.advancement ? level.getBiomeCached(null, blockPos) : level.getBiome(blockPos))) // Leaf - cache getBiome
|
||||||
&& (!this.structures.isPresent() || isLoaded && level.structureManager().getStructureWithPieceAt(blockPos, this.structures.get()).isValid())
|
&& (!this.structures.isPresent() || isLoaded && level.structureManager().getStructureWithPieceAt(blockPos, this.structures.get()).isValid())
|
||||||
&& (!this.smokey.isPresent() || isLoaded && this.smokey.get() == CampfireBlock.isSmokeyPos(level, blockPos))
|
&& (!this.smokey.isPresent() || isLoaded && this.smokey.get() == CampfireBlock.isSmokeyPos(level, blockPos))
|
||||||
&& (!this.light.isPresent() || this.light.get().matches(level, blockPos))
|
&& (!this.light.isPresent() || this.light.get().matches(level, blockPos))
|
||||||
diff --git a/net/minecraft/world/level/LevelReader.java b/net/minecraft/world/level/LevelReader.java
|
diff --git a/net/minecraft/world/level/LevelReader.java b/net/minecraft/world/level/LevelReader.java
|
||||||
index 0842fd6488c8b27d98c4344e1244996b4c0e9912..c6ddb49648c55443ae880c1adba9887ab0681c82 100644
|
index 0842fd6488c8b27d98c4344e1244996b4c0e9912..55c7f7486c293d4434b7e3facdbef034d105aa19 100644
|
||||||
--- a/net/minecraft/world/level/LevelReader.java
|
--- a/net/minecraft/world/level/LevelReader.java
|
||||||
+++ b/net/minecraft/world/level/LevelReader.java
|
+++ b/net/minecraft/world/level/LevelReader.java
|
||||||
@@ -57,6 +57,12 @@ public interface LevelReader extends ca.spottedleaf.moonrise.patches.chunk_syste
|
@@ -57,6 +57,12 @@ public interface LevelReader extends ca.spottedleaf.moonrise.patches.chunk_syste
|
||||||
@@ -26,8 +26,8 @@ index 0842fd6488c8b27d98c4344e1244996b4c0e9912..c6ddb49648c55443ae880c1adba9887a
|
|||||||
}
|
}
|
||||||
|
|
||||||
+ // Leaf start - cache getBiome
|
+ // Leaf start - cache getBiome
|
||||||
+ default Holder<Biome> getBiomeCached(BlockPos pos) {
|
+ default Holder<Biome> getBiomeCached(@Nullable net.minecraft.world.level.chunk.LevelChunk chunk, BlockPos pos) {
|
||||||
+ return this.getBiomeManager().getBiomeCached(pos);
|
+ return this.getBiomeManager().getBiomeCached(chunk, pos);
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - cache getBiome
|
+ // Leaf end - cache getBiome
|
||||||
+
|
+
|
||||||
@@ -35,7 +35,7 @@ index 0842fd6488c8b27d98c4344e1244996b4c0e9912..c6ddb49648c55443ae880c1adba9887a
|
|||||||
int floor = Mth.floor(aabb.minX);
|
int floor = Mth.floor(aabb.minX);
|
||||||
int floor1 = Mth.floor(aabb.maxX);
|
int floor1 = Mth.floor(aabb.maxX);
|
||||||
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
||||||
index a2da4fce50f31d56036d04041c4f80ed90c18b27..f242941ce06d356a025e306efe78c688e9b755c4 100644
|
index a2da4fce50f31d56036d04041c4f80ed90c18b27..81e176d17fb072f9ee531639abfe42134ae833a9 100644
|
||||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||||
@@ -443,7 +443,7 @@ public final class NaturalSpawner {
|
@@ -443,7 +443,7 @@ public final class NaturalSpawner {
|
||||||
@@ -43,7 +43,7 @@ index a2da4fce50f31d56036d04041c4f80ed90c18b27..f242941ce06d356a025e306efe78c688
|
|||||||
ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos
|
ServerLevel level, StructureManager structureManager, ChunkGenerator generator, MobCategory category, RandomSource random, BlockPos pos
|
||||||
) {
|
) {
|
||||||
- Holder<Biome> biome = level.getBiome(pos);
|
- Holder<Biome> biome = level.getBiome(pos);
|
||||||
+ Holder<Biome> biome = org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(pos) : level.getBiome(pos); // Leaf - cache getBiome
|
+ Holder<Biome> biome = org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(null, pos) : level.getBiome(pos); // Leaf - cache getBiome
|
||||||
return category == MobCategory.WATER_AMBIENT && biome.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F
|
return category == MobCategory.WATER_AMBIENT && biome.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F
|
||||||
? Optional.empty()
|
? Optional.empty()
|
||||||
: mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random);
|
: mobsAt(level, structureManager, generator, category, pos, biome).getRandom(random);
|
||||||
@@ -52,12 +52,12 @@ index a2da4fce50f31d56036d04041c4f80ed90c18b27..f242941ce06d356a025e306efe78c688
|
|||||||
return isInNetherFortressBounds(pos, level, cetagory, structureManager)
|
return isInNetherFortressBounds(pos, level, cetagory, structureManager)
|
||||||
? NetherFortressStructure.FORTRESS_ENEMIES
|
? NetherFortressStructure.FORTRESS_ENEMIES
|
||||||
- : generator.getMobsAt(biome != null ? biome : level.getBiome(pos), structureManager, cetagory, pos);
|
- : generator.getMobsAt(biome != null ? biome : level.getBiome(pos), structureManager, cetagory, pos);
|
||||||
+ : generator.getMobsAt(biome != null ? biome : (org.dreeam.leaf.config.modules.opt.OptimizeBiome.mobSpawn ? level.getBiomeCached(pos) : level.getBiome(pos)), structureManager, cetagory, pos); // Leaf - cache getBiome
|
+ : 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
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) {
|
public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager) {
|
||||||
diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java
|
diff --git a/net/minecraft/world/level/biome/BiomeManager.java b/net/minecraft/world/level/biome/BiomeManager.java
|
||||||
index a48175a7ebb1788ace46395621ed78d910178a53..00122472991ba0c1a0ea77053aad71cdfa92a7bd 100644
|
index a48175a7ebb1788ace46395621ed78d910178a53..bda44479d1537d9fa2d23f519049aeb96685fbe6 100644
|
||||||
--- a/net/minecraft/world/level/biome/BiomeManager.java
|
--- a/net/minecraft/world/level/biome/BiomeManager.java
|
||||||
+++ b/net/minecraft/world/level/biome/BiomeManager.java
|
+++ b/net/minecraft/world/level/biome/BiomeManager.java
|
||||||
@@ -15,10 +15,23 @@ public class BiomeManager {
|
@@ -15,10 +15,23 @@ public class BiomeManager {
|
||||||
@@ -84,12 +84,12 @@ index a48175a7ebb1788ace46395621ed78d910178a53..00122472991ba0c1a0ea77053aad71cd
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static long obfuscateSeed(long seed) {
|
public static long obfuscateSeed(long seed) {
|
||||||
@@ -29,6 +42,40 @@ public class BiomeManager {
|
@@ -29,6 +42,105 @@ public class BiomeManager {
|
||||||
return new BiomeManager(newSource, this.biomeZoomSeed);
|
return new BiomeManager(newSource, this.biomeZoomSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ // Leaf start - cache getBiome
|
+ // Leaf start - cache getBiome
|
||||||
+ public Holder<Biome> getBiomeCached(BlockPos pos) {
|
+ public Holder<Biome> getBiomeCached(@org.jetbrains.annotations.Nullable net.minecraft.world.level.chunk.LevelChunk chunk, BlockPos pos) {
|
||||||
+ if (biomeCache == null) {
|
+ if (biomeCache == null) {
|
||||||
+ return getBiome(pos);
|
+ return getBiome(pos);
|
||||||
+ }
|
+ }
|
||||||
@@ -113,19 +113,84 @@ index a48175a7ebb1788ace46395621ed78d910178a53..00122472991ba0c1a0ea77053aad71cd
|
|||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ Holder<Biome> biome = getBiome(pos);
|
+ Holder<Biome> biome = getBiomeCachedChunk(chunk, pos);
|
||||||
+
|
+
|
||||||
+ biomeCache[(int) hash] = biome;
|
+ biomeCache[(int) hash] = biome;
|
||||||
+ biomeCachePos[(int) hash] = packedPos;
|
+ biomeCachePos[(int) hash] = packedPos;
|
||||||
+
|
+
|
||||||
+ return biome;
|
+ return biome;
|
||||||
+ }
|
+ }
|
||||||
|
+ private Holder<Biome> getBiomeCachedChunk(@org.jetbrains.annotations.Nullable net.minecraft.world.level.chunk.LevelChunk chunk, BlockPos pos) {
|
||||||
|
+ // Leaf start - Carpet-Fixes - Optimized getBiome method
|
||||||
|
+ int xMinus2 = pos.getX() - 2;
|
||||||
|
+ int yMinus2 = pos.getY() - 2;
|
||||||
|
+ int zMinus2 = pos.getZ() - 2;
|
||||||
|
+ int x = xMinus2 >> 2; // BlockPos to BiomePos
|
||||||
|
+ int y = yMinus2 >> 2;
|
||||||
|
+ int z = zMinus2 >> 2;
|
||||||
|
+ double quartX = (double) (xMinus2 & 3) / 4.0; // quartLocal divided by 4
|
||||||
|
+ double quartY = (double) (yMinus2 & 3) / 4.0; // 0/4, 1/4, 2/4, 3/4
|
||||||
|
+ double quartZ = (double) (zMinus2 & 3) / 4.0; // [0, 0.25, 0.5, 0.75]
|
||||||
|
+ int smallestX = 0;
|
||||||
|
+ double smallestDist = Double.POSITIVE_INFINITY;
|
||||||
|
+ for (int biomeX = 0; biomeX < 8; ++biomeX) {
|
||||||
|
+ boolean everyOtherQuad = (biomeX & 4) == 0; // 1 1 1 1 0 0 0 0
|
||||||
|
+ boolean everyOtherPair = (biomeX & 2) == 0; // 1 1 0 0 1 1 0 0
|
||||||
|
+ boolean everyOther = (biomeX & 1) == 0; // 1 0 1 0 1 0 1 0
|
||||||
|
+ double quartXX = everyOtherQuad ? quartX : quartX - 1.0; //[-1.0,-0.75,-0.5,-0.25,0.0,0.25,0.5,0.75]
|
||||||
|
+ double quartYY = everyOtherPair ? quartY : quartY - 1.0;
|
||||||
|
+ double quartZZ = everyOther ? quartZ : quartZ - 1.0;
|
||||||
|
+
|
||||||
|
+ //This code block is new
|
||||||
|
+ double maxQuartYY = 0.0, maxQuartZZ = 0.0;
|
||||||
|
+ if (biomeX != 0) {
|
||||||
|
+ maxQuartYY = Mth.square(Math.max(quartYY + maxOffset, Math.abs(quartYY - maxOffset)));
|
||||||
|
+ maxQuartZZ = Mth.square(Math.max(quartZZ + maxOffset, Math.abs(quartZZ - maxOffset)));
|
||||||
|
+ double maxQuartXX = Mth.square(Math.max(quartXX + maxOffset, Math.abs(quartXX - maxOffset)));
|
||||||
|
+ if (smallestDist < maxQuartXX + maxQuartYY + maxQuartZZ) continue;
|
||||||
|
+ }
|
||||||
|
+ int xx = everyOtherQuad ? x : x + 1;
|
||||||
|
+ int yy = everyOtherPair ? y : y + 1;
|
||||||
|
+ int zz = everyOther ? z : z + 1;
|
||||||
|
+
|
||||||
|
+ //I transferred the code from method_38106 to here, so I could call continue halfway through
|
||||||
|
+ long seed = LinearCongruentialGenerator.next(this.biomeZoomSeed, xx);
|
||||||
|
+ seed = LinearCongruentialGenerator.next(seed, yy);
|
||||||
|
+ seed = LinearCongruentialGenerator.next(seed, zz);
|
||||||
|
+ seed = LinearCongruentialGenerator.next(seed, xx);
|
||||||
|
+ seed = LinearCongruentialGenerator.next(seed, yy);
|
||||||
|
+ seed = LinearCongruentialGenerator.next(seed, zz);
|
||||||
|
+ double offsetX = getFiddle(seed);
|
||||||
|
+ double sqrX = Mth.square(quartXX + offsetX);
|
||||||
|
+ if (biomeX != 0 && smallestDist < sqrX + maxQuartYY + maxQuartZZ) continue; //skip the rest of the loop
|
||||||
|
+ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed);
|
||||||
|
+ double offsetY = getFiddle(seed);
|
||||||
|
+ double sqrY = Mth.square(quartYY + offsetY);
|
||||||
|
+ if (biomeX != 0 && smallestDist < sqrX + sqrY + maxQuartZZ) continue; // skip the rest of the loop
|
||||||
|
+ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed);
|
||||||
|
+ double offsetZ = getFiddle(seed);
|
||||||
|
+ double biomeDist = sqrX + sqrY + Mth.square(quartZZ + offsetZ);
|
||||||
|
+
|
||||||
|
+ if (smallestDist > biomeDist) {
|
||||||
|
+ smallestX = biomeX;
|
||||||
|
+ smallestDist = biomeDist;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ int x1 = (smallestX & 4) == 0 ? x : x + 1;
|
||||||
|
+ int y1 = (smallestX & 2) == 0 ? y : y + 1;
|
||||||
|
+ int z1 = (smallestX & 1) == 0 ? z : z + 1;
|
||||||
|
+ if (chunk != null && chunk.locX == x >> 2 && chunk.locZ == z >> 2) {
|
||||||
|
+ return chunk.getNoiseBiome(x1, y1, z1);
|
||||||
|
+ }
|
||||||
|
+ return this.noiseBiomeSource.getNoiseBiome(x1, y1, z1);
|
||||||
|
+ // Leaf end - Carpet-Fixes - Optimized getBiome method
|
||||||
|
+ }
|
||||||
+ // Leaf end - cache getBiome
|
+ // Leaf end - cache getBiome
|
||||||
+
|
+
|
||||||
public Holder<Biome> getBiome(BlockPos pos) {
|
public Holder<Biome> getBiome(BlockPos pos) {
|
||||||
// Leaf start - Carpet-Fixes - Optimized getBiome method
|
// Leaf start - Carpet-Fixes - Optimized getBiome method
|
||||||
int xMinus2 = pos.getX() - 2;
|
int xMinus2 = pos.getX() - 2;
|
||||||
@@ -126,9 +173,18 @@ public class BiomeManager {
|
@@ -126,9 +238,18 @@ public class BiomeManager {
|
||||||
return Mth.square(zNoise + fiddle2) + Mth.square(yNoise + fiddle1) + Mth.square(xNoise + fiddle);
|
return Mth.square(zNoise + fiddle2) + Mth.square(yNoise + fiddle1) + Mth.square(xNoise + fiddle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3,26 +3,594 @@ From: hayanesuru <hayanesuru@outlook.jp>
|
|||||||
Date: Tue, 3 Jun 2025 15:20:59 +0900
|
Date: Tue, 3 Jun 2025 15:20:59 +0900
|
||||||
Subject: [PATCH] optimize mob spawning
|
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 083b49c8fbdbde96b5be3e5531c21b2c96371b60..ca707979e221ea0500efad42350994b90aac3d52 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 fc86e900e41305287a6cc6d766184c6e28d6189b..56516dd07433161f5afd448aaf535c59d14c2528 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() {
|
||||||
|
@@ -497,6 +497,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;
|
||||||
|
@@ -510,8 +527,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;
|
||||||
|
@@ -523,14 +540,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 {
|
||||||
|
@@ -541,10 +558,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
|
||||||
|
);
|
||||||
|
@@ -609,6 +626,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
|
||||||
|
@@ -620,16 +638,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();
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -647,7 +689,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 224a032e8992f104ad9380182ed67c316c93274e..983b03c9cf1c91e9954da0ed8be99c2dfaa5a9c6 100644
|
||||||
|
--- a/net/minecraft/server/level/ServerLevel.java
|
||||||
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||||
|
@@ -1110,6 +1110,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
|
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
||||||
index f242941ce06d356a025e306efe78c688e9b755c4..38a6a11c05c0a0a2f59c1477e516431263dce101 100644
|
index 81e176d17fb072f9ee531639abfe42134ae833a9..ab6fa7ed111ef16a0b6774c21988589ee2110c66 100644
|
||||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||||
@@ -472,6 +472,17 @@ public final class NaturalSpawner {
|
@@ -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
|
+ // Leaf start - optimize mob spawning
|
||||||
+ private static void mutableRandomPosWithin(BlockPos.MutableBlockPos pos1, Level level, LevelChunk chunk) {
|
+ public static boolean isInNetherFortressBoundsChunk(BlockPos pos, ServerLevel level, MobCategory category, StructureManager structureManager, LevelChunk chunk) {
|
||||||
+ ChunkPos pos = chunk.getPos();
|
+ 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)))) {
|
||||||
+ int randomX = pos.getMinBlockX() + level.random.nextInt(16);
|
+ Structure structure = structureManager.registryAccess().lookupOrThrow(Registries.STRUCTURE).getValue(BuiltinStructures.FORTRESS);
|
||||||
+ int randomZ = pos.getMinBlockZ() + level.random.nextInt(16);
|
+ return structure != null && structureManager.getStructureAt(pos, structure).isValid();
|
||||||
+ int surfaceY = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, randomX, randomZ) + 1;
|
+ } else {
|
||||||
+ int randomY = Mth.randomBetweenInclusive(level.random, level.getMinY(), surfaceY);
|
+ return false;
|
||||||
+ pos1.set(randomX, randomY, randomZ);
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - optimize mob spawning
|
+ // Leaf end - optimize mob spawning
|
||||||
+
|
+
|
||||||
private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) {
|
private static BlockPos getRandomPosWithin(Level level, LevelChunk chunk) {
|
||||||
ChunkPos pos = chunk.getPos();
|
ChunkPos pos = chunk.getPos();
|
||||||
int i = pos.getMinBlockX() + level.random.nextInt(16);
|
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,
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ Subject: [PATCH] throttle mob spawning
|
|||||||
|
|
||||||
|
|
||||||
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
|
||||||
index 38a6a11c05c0a0a2f59c1477e516431263dce101..a55abb08093847a8bfec446659f9af5fb1df2824 100644
|
index ab6fa7ed111ef16a0b6774c21988589ee2110c66..f3bed4b372f858656dbf51fb0140260bad4a41be 100644
|
||||||
--- a/net/minecraft/world/level/NaturalSpawner.java
|
--- a/net/minecraft/world/level/NaturalSpawner.java
|
||||||
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
+++ b/net/minecraft/world/level/NaturalSpawner.java
|
||||||
@@ -154,6 +154,17 @@ public final class NaturalSpawner {
|
@@ -215,6 +215,17 @@ public final class NaturalSpawner {
|
||||||
// Paper start - Optional per player mob spawns
|
// Paper start - Optional per player mob spawns
|
||||||
final boolean canSpawn;
|
final boolean canSpawn;
|
||||||
int maxSpawns = Integer.MAX_VALUE;
|
int maxSpawns = Integer.MAX_VALUE;
|
||||||
|
|||||||
@@ -5,93 +5,52 @@ Subject: [PATCH] optimize random tick
|
|||||||
|
|
||||||
|
|
||||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
||||||
index fc86e900e41305287a6cc6d766184c6e28d6189b..3f3450e79f768f9c9fa4f2c0fe485b7179419680 100644
|
index 56516dd07433161f5afd448aaf535c59d14c2528..9992eeda9535e6304e7019e7eb7e5c1458ddd89e 100644
|
||||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
--- a/net/minecraft/server/level/ServerChunkCache.java
|
||||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
||||||
@@ -634,6 +634,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
@@ -675,7 +675,13 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
||||||
|
list.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.iterateTickingChunksFaster(); // Paper - chunk tick iteration optimisations
|
- this.iterateTickingChunksFaster(); // Paper - chunk tick iteration optimisations
|
||||||
+ this.level.randomTickSystem.tick(this.level); // Leaf - optimize random tick
|
+ // Leaf start - optimize random tick
|
||||||
|
+ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) {
|
||||||
|
+ this.level.randomTickSystem.tick(this.level);
|
||||||
|
+ } else {
|
||||||
|
+ this.iterateTickingChunksFaster(); // Paper - chunk tick iteration optimisations
|
||||||
|
+ }
|
||||||
|
+ // Leaf end - optimize random tick
|
||||||
if (_boolean) {
|
if (_boolean) {
|
||||||
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
|
this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies);
|
||||||
}
|
}
|
||||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||||
index 224a032e8992f104ad9380182ed67c316c93274e..07f6da45a88e1630ca4249bb134fce1f95d2c39c 100644
|
index 983b03c9cf1c91e9954da0ed8be99c2dfaa5a9c6..4bd5e0268d52d63e160ccfe3d1509bbda421bc0f 100644
|
||||||
--- a/net/minecraft/server/level/ServerLevel.java
|
--- a/net/minecraft/server/level/ServerLevel.java
|
||||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||||
@@ -1110,6 +1110,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
@@ -1111,6 +1111,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
|
private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking
|
||||||
|
|
||||||
+ public org.dreeam.leaf.world.RandomTickSystem randomTickSystem = new org.dreeam.leaf.world.RandomTickSystem(); // Leaf - optimize random tick
|
public final org.dreeam.leaf.world.NatureSpawnChunkMap natureSpawnChunkMap = new org.dreeam.leaf.world.NatureSpawnChunkMap(); // Leaf - optimize mob spawning
|
||||||
|
+ public final org.dreeam.leaf.world.RandomTickSystem randomTickSystem = new org.dreeam.leaf.world.RandomTickSystem(); // Leaf - optimize random tick
|
||||||
public void tickChunk(LevelChunk chunk, int randomTickSpeed) {
|
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
|
final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting
|
||||||
ChunkPos pos = chunk.getPos();
|
ChunkPos pos = chunk.getPos();
|
||||||
@@ -1125,7 +1126,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
||||||
} // Paper - Option to disable ice and snow
|
|
||||||
|
|
||||||
if (randomTickSpeed > 0) {
|
|
||||||
- this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking
|
|
||||||
+ if (org.dreeam.leaf.config.modules.opt.OptimizeRandomTick.enabled) randomTickSystem.tickChunk(this.simpleRandom, chunk, randomTickSpeed); // Leaf - optimize random tick
|
|
||||||
+ else this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking // Leaf - optimize random tick
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||||
index 31f19dfe16e270b55f3b44754c97ed8d9fa422cf..31c6c035aca5400a5c0a030bfe5334545e2a4bca 100644
|
index 31f19dfe16e270b55f3b44754c97ed8d9fa422cf..174da710d2b86de98cbf6499d4a954f9476a225a 100644
|
||||||
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
--- a/net/minecraft/world/level/chunk/LevelChunk.java
|
||||||
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
|
||||||
@@ -150,6 +150,48 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
@@ -150,6 +150,10 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||||
}
|
}
|
||||||
// Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively
|
// Gale end - Airplane - optimize random calls in chunk ticking - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively
|
||||||
|
|
||||||
+ // Leaf start - optimize random tick
|
+ // Leaf start - optimize random tick
|
||||||
+ private boolean leaf$tickingBlocksDirty = true;
|
+ public boolean leaf$tickingBlocksDirty = true;
|
||||||
+ private int leaf$tickingBlocksCount;
|
+ public int[] leaf$tickingCount = {};
|
||||||
+ private int leaf$firstTickingSectionIndex = -1;
|
|
||||||
+ public final int leaf$tickingBlocksCount() {
|
|
||||||
+ if (!leaf$tickingBlocksDirty) {
|
|
||||||
+ return leaf$tickingBlocksCount;
|
|
||||||
+ }
|
|
||||||
+ leaf$tickingBlocksDirty = false;
|
|
||||||
+ int sum = 0;
|
|
||||||
+ leaf$firstTickingSectionIndex = -1;
|
|
||||||
+ for (int i = 0; i < sections.length; i++) {
|
|
||||||
+ LevelChunkSection section = sections[i];
|
|
||||||
+ int size = section.moonrise$getTickingBlockList().size();
|
|
||||||
+ if (size != 0 && leaf$firstTickingSectionIndex == -1) {
|
|
||||||
+ leaf$firstTickingSectionIndex = i;
|
|
||||||
+ }
|
|
||||||
+ sum += size;
|
|
||||||
+ }
|
|
||||||
+ leaf$tickingBlocksCount = sum;
|
|
||||||
+ return sum;
|
|
||||||
+ }
|
|
||||||
+ public final java.util.OptionalLong leaf$getTickingPos(int idx) {
|
|
||||||
+ if (leaf$firstTickingSectionIndex != -1) {
|
|
||||||
+ for (int i = leaf$firstTickingSectionIndex; i < sections.length; i++) {
|
|
||||||
+ LevelChunkSection section = sections[i];
|
|
||||||
+ var l = section.moonrise$getTickingBlockList();
|
|
||||||
+ int size = l.size();
|
|
||||||
+ if (idx < size) {
|
|
||||||
+ short loc = l.getRaw(idx);
|
|
||||||
+ int x = (loc & 15) | (chunkPos.x << 4);
|
|
||||||
+ int y = (loc >>> 8) | ((getMinSectionY() + i) << 4);
|
|
||||||
+ int z = ((loc >>> 4) & 15) | (chunkPos.z << 4);
|
|
||||||
+ return java.util.OptionalLong.of(BlockPos.asLong(x, y, z));
|
|
||||||
+ }
|
|
||||||
+ idx -= size;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ leaf$tickingBlocksDirty = true;
|
|
||||||
+ return java.util.OptionalLong.empty();
|
|
||||||
+ }
|
|
||||||
+ // Leaf end - optimize random tick
|
+ // Leaf end - optimize random tick
|
||||||
public LevelChunk(Level level, ChunkPos pos) {
|
public LevelChunk(Level level, ChunkPos pos) {
|
||||||
this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null);
|
this(level, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, null, null, null);
|
||||||
}
|
}
|
||||||
@@ -414,6 +456,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
@@ -414,6 +418,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
|
||||||
if (blockState == state) {
|
if (blockState == state) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -99,25 +99,8 @@ index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194
|
|||||||
- }
|
- }
|
||||||
- // Paper end - detailed watchdog information
|
- // Paper end - detailed watchdog information
|
||||||
}
|
}
|
||||||
diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
index 3f3450e79f768f9c9fa4f2c0fe485b7179419680..f16760c8817d0220f0a44ed620859ba910bf63a9 100644
|
|
||||||
--- a/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
+++ b/net/minecraft/server/level/ServerChunkCache.java
|
|
||||||
@@ -622,8 +622,10 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon
|
|
||||||
try {
|
|
||||||
this.chunkMap.collectSpawningChunks(list);
|
|
||||||
// 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.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) {
|
|
||||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||||
index 07f6da45a88e1630ca4249bb134fce1f95d2c39c..9fbaafaf5df6e003742cdea55da732cc7e602866 100644
|
index 4bd5e0268d52d63e160ccfe3d1509bbda421bc0f..36138a2fc86f5e0fe0ea3d40061c54f8a6096c4d 100644
|
||||||
--- a/net/minecraft/server/level/ServerLevel.java
|
--- a/net/minecraft/server/level/ServerLevel.java
|
||||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||||
@@ -1510,13 +1510,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
@@ -1510,13 +1510,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
From: hayanesuru <mc@jvavav.com>
|
From: hayanesuru <mc@jvavav.com>
|
||||||
Date: Fri, 2 May 2025 18:22:24 -0700
|
Date: Fri, 2 May 2025 18:22:24 -0700
|
||||||
Subject: [PATCH] Async chunk send
|
Subject: [PATCH] Async chunk sending
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
diff --git a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
||||||
index ca24f4cd7a50f0156d84e263c60f841cca95c669..7e15038e8fceab1e97c2245c2e9111deed6455fb 100644
|
index ca24f4cd7a50f0156d84e263c60f841cca95c669..e98a7edc4001d020d238a44a96fda00f389234eb 100644
|
||||||
--- a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
--- a/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
||||||
+++ b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
+++ b/src/main/java/io/papermc/paper/antixray/ChunkPacketBlockControllerAntiXray.java
|
||||||
@@ -185,7 +185,7 @@ public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockCo
|
@@ -185,7 +185,7 @@ public final class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockCo
|
||||||
@@ -13,7 +13,7 @@ index ca24f4cd7a50f0156d84e263c60f841cca95c669..7e15038e8fceab1e97c2245c2e9111de
|
|||||||
}
|
}
|
||||||
|
|
||||||
- if (!Bukkit.isPrimaryThread()) {
|
- if (!Bukkit.isPrimaryThread()) {
|
||||||
+ if (!Bukkit.isPrimaryThread() && !(Thread.currentThread() instanceof org.dreeam.leaf.async.chunk.AsyncChunkSendThread)) { // Leaf - Async chunk send
|
+ if (!Bukkit.isPrimaryThread() && !(Thread.currentThread() instanceof org.dreeam.leaf.async.chunk.AsyncChunkSendThread)) { // Leaf - Async chunk sending
|
||||||
// Plugins?
|
// Plugins?
|
||||||
MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo));
|
MinecraftServer.getServer().scheduleOnMain(() -> modifyBlocks(chunkPacket, chunkPacketInfo));
|
||||||
return;
|
return;
|
||||||
@@ -5,7 +5,7 @@ Subject: [PATCH] cache getBiome
|
|||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
|
diff --git a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
|
||||||
index 0e14f962b26823e49b192a4f97ec6c1f477ef0ff..cb6cd899fd8af069534f45a4a9e81137f941e250 100644
|
index 0e14f962b26823e49b192a4f97ec6c1f477ef0ff..757dd75474b134be8e432d64e1c11d52ecbb0587 100644
|
||||||
--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
|
--- a/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
|
||||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
|
+++ b/src/main/java/org/bukkit/craftbukkit/util/DelegatedGeneratorAccess.java
|
||||||
@@ -285,6 +285,13 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel {
|
@@ -285,6 +285,13 @@ public abstract class DelegatedGeneratorAccess implements WorldGenLevel {
|
||||||
@@ -14,8 +14,8 @@ index 0e14f962b26823e49b192a4f97ec6c1f477ef0ff..cb6cd899fd8af069534f45a4a9e81137
|
|||||||
|
|
||||||
+ // Leaf start - cache getBiome
|
+ // Leaf start - cache getBiome
|
||||||
+ @Override
|
+ @Override
|
||||||
+ public Holder<Biome> getBiomeCached(BlockPos pos) {
|
+ public Holder<Biome> getBiomeCached(@Nullable net.minecraft.world.level.chunk.LevelChunk chunk, BlockPos pos) {
|
||||||
+ return this.delegate.getBiomeCached(pos);
|
+ return this.delegate.getBiomeCached(chunk, pos);
|
||||||
+ }
|
+ }
|
||||||
+ // Leaf end - cache getBiome
|
+ // Leaf end - cache getBiome
|
||||||
+
|
+
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: hayanesuru <hayanesuru@outlook.jp>
|
||||||
|
Date: Thu, 26 Jun 2025 00:42:27 +0900
|
||||||
|
Subject: [PATCH] optimize despawn
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
||||||
|
index 6c298304f842612d0e063b578f274eed04b32960..dc401da92deb55ed59197c6b1a1f6996f39dc9d0 100644
|
||||||
|
--- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
||||||
|
+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java
|
||||||
|
@@ -177,7 +177,7 @@ public class WorldConfiguration extends ConfigurationPart {
|
||||||
|
@MergeMap
|
||||||
|
public Reference2IntMap<MobCategory> spawnLimits = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1)));
|
||||||
|
@MergeMap
|
||||||
|
- public Map<MobCategory, DespawnRangePair> despawnRanges = Arrays.stream(MobCategory.values()).collect(Collectors.toMap(Function.identity(), category -> DespawnRangePair.createDefault()));
|
||||||
|
+ public Map<MobCategory, DespawnRangePair> despawnRanges = new java.util.EnumMap<>(Arrays.stream(MobCategory.values()).collect(Collectors.toMap(Function.identity(), category -> DespawnRangePair.createDefault()))); // Leaf - replace EnumMap optimize despawn
|
||||||
|
public DespawnRange.Shape despawnRangeShape = DespawnRange.Shape.ELLIPSOID;
|
||||||
|
@MergeMap
|
||||||
|
public Reference2IntMap<MobCategory> ticksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1)));
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
package org.dreeam.leaf.world;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||||
|
import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class NatureSpawnChunkMap {
|
||||||
|
/// breadth-first search
|
||||||
|
///
|
||||||
|
/// 0 4 12 28 48 80 112 148 196
|
||||||
|
private static final long[][] TABLE_BFS = new long[][]{
|
||||||
|
{0L},
|
||||||
|
{0L, 4294967295L, -4294967296L, 4294967296L, 1L},
|
||||||
|
{0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, 4294967294L, -8589934592L, 8589934592L, 2L},
|
||||||
|
{0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, 4294967293L, -12884901888L, 12884901888L, 3L},
|
||||||
|
{0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 4294967292L, -17179869184L, 17179869184L, 4L},
|
||||||
|
{0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -8589934595L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869181L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -12884901885L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 12884901891L, -8589934596L, -4294967300L, -12884901891L, -12884901890L, -4L, 4294967292L, -12884901889L, -17179869184L, 8589934588L, 12884901884L, -17179869183L, -17179869182L, 17179869180L, 21474836477L, 21474836478L, 21474836479L, 17179869184L, 17179869185L, 17179869186L, -17179869181L, 17179869187L, -12884901884L, -8589934588L, -4294967292L, 4L, 4294967300L, 8589934596L, 12884901892L, 4294967291L, -21474836480L, 21474836480L, 5L},
|
||||||
|
{0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -8589934595L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869181L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -12884901885L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 12884901891L, -12884901892L, -8589934596L, -4294967300L, -12884901891L, -12884901890L, -4L, 4294967292L, -12884901889L, -17179869184L, 8589934588L, 12884901884L, -17179869183L, -17179869182L, 17179869180L, 21474836476L, 21474836477L, 21474836478L, 21474836479L, 17179869184L, 17179869185L, 17179869186L, -17179869181L, 17179869187L, -17179869180L, -12884901884L, -8589934588L, -4294967292L, 4L, 4294967300L, 8589934596L, 12884901892L, 17179869188L, -8589934597L, -17179869187L, -4294967301L, -5L, -17179869186L, -17179869185L, 4294967291L, 8589934587L, -21474836480L, -21474836479L, 12884901883L, 17179869179L, -21474836478L, -21474836477L, 25769803773L, 25769803774L, 25769803775L, 21474836480L, 21474836481L, 21474836482L, 21474836483L, -12884901883L, -8589934587L, -4294967291L, 5L, 4294967301L, 8589934597L, 12884901893L, 4294967290L, -25769803776L, 25769803776L, 6L},
|
||||||
|
{0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -8589934595L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869181L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -12884901885L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 12884901891L, -12884901892L, -8589934596L, -4294967300L, -12884901891L, -12884901890L, -4L, 4294967292L, -12884901889L, -17179869184L, 8589934588L, 12884901884L, -17179869183L, -17179869182L, 17179869180L, 21474836476L, 21474836477L, 21474836478L, 21474836479L, 17179869184L, 17179869185L, 17179869186L, -17179869181L, 17179869187L, -17179869180L, -12884901884L, -8589934588L, -4294967292L, 4L, 4294967300L, 8589934596L, 12884901892L, 17179869188L, -12884901893L, -8589934597L, -17179869188L, -17179869187L, -4294967301L, -5L, -17179869186L, -17179869185L, 4294967291L, 8589934587L, -21474836480L, -21474836479L, 12884901883L, 17179869179L, -21474836478L, -21474836477L, 21474836475L, 25769803772L, 25769803773L, 25769803774L, 25769803775L, 21474836480L, 21474836481L, 21474836482L, 21474836483L, -21474836476L, 21474836484L, -17179869179L, -12884901883L, -8589934587L, -4294967291L, 5L, 4294967301L, 8589934597L, 12884901893L, 17179869189L, -8589934598L, -4294967302L, -21474836483L, -21474836482L, -6L, 4294967290L, -21474836481L, -25769803776L, 8589934586L, 12884901882L, -25769803775L, -25769803774L, 17179869178L, -25769803773L, 30064771069L, 30064771070L, 30064771071L, 25769803776L, 25769803777L, 25769803778L, 25769803779L, -12884901882L, -8589934586L, -4294967290L, 6L, 4294967302L, 8589934598L, 12884901894L, 4294967289L, -30064771072L, 30064771072L, 7L},
|
||||||
|
{0L, -1L, 4294967295L, 8589934591L, -4294967296L, 4294967296L, -4294967295L, 1L, 4294967297L, -4294967298L, -2L, 4294967294L, -4294967297L, -8589934592L, 8589934590L, 12884901886L, 12884901887L, 8589934592L, -8589934591L, 8589934593L, -8589934590L, -4294967294L, 2L, 4294967298L, 8589934594L, -8589934595L, -4294967299L, -3L, -8589934594L, -8589934593L, 4294967293L, 8589934589L, -12884901888L, -12884901887L, 12884901885L, 17179869181L, 17179869182L, 17179869183L, 12884901888L, 12884901889L, -12884901886L, 12884901890L, -12884901885L, -8589934589L, -4294967293L, 3L, 4294967299L, 8589934595L, 12884901891L, -12884901892L, -8589934596L, -4294967300L, -12884901891L, -12884901890L, -4L, 4294967292L, -12884901889L, -17179869184L, 8589934588L, 12884901884L, -17179869183L, -17179869182L, 17179869180L, 21474836476L, 21474836477L, 21474836478L, 21474836479L, 17179869184L, 17179869185L, 17179869186L, -17179869181L, 17179869187L, -17179869180L, -12884901884L, -8589934588L, -4294967292L, 4L, 4294967300L, 8589934596L, 12884901892L, 17179869188L, -17179869189L, -12884901893L, -8589934597L, -17179869188L, -17179869187L, -4294967301L, -5L, -17179869186L, -17179869185L, 4294967291L, 8589934587L, -21474836480L, -21474836479L, 12884901883L, 17179869179L, -21474836478L, -21474836477L, 21474836475L, 25769803771L, 25769803772L, 25769803773L, 25769803774L, 25769803775L, 21474836480L, 21474836481L, 21474836482L, 21474836483L, -21474836476L, 21474836484L, -21474836475L, -17179869179L, -12884901883L, -8589934587L, -4294967291L, 5L, 4294967301L, 8589934597L, 12884901893L, 17179869189L, 21474836485L, -17179869190L, -12884901894L, -21474836485L, -21474836484L, -8589934598L, -4294967302L, -21474836483L, -21474836482L, -6L, 4294967290L, -21474836481L, -25769803776L, 8589934586L, 12884901882L, -25769803775L, -25769803774L, 17179869178L, 21474836474L, -25769803773L, -25769803772L, 25769803770L, 30064771067L, 30064771068L, 30064771069L, 30064771070L, 30064771071L, 25769803776L, 25769803777L, 25769803778L, 25769803779L, 25769803780L, -25769803771L, 25769803781L, -21474836474L, -17179869178L, -12884901882L, -8589934586L, -4294967290L, 6L, 4294967302L, 8589934598L, 12884901894L, 17179869190L, 21474836486L, -8589934599L, -25769803779L, -4294967303L, -7L, -25769803778L, -25769803777L, 4294967289L, 8589934585L, -30064771072L, -30064771071L, 12884901881L, 17179869177L, -30064771070L, -30064771069L, 34359738365L, 34359738366L, 34359738367L, 30064771072L, 30064771073L, 30064771074L, 30064771075L, -12884901881L, -8589934585L, -4294967289L, 7L, 4294967303L, 8589934599L, 12884901895L, 4294967288L, -34359738368L, 34359738368L, 8L}
|
||||||
|
};
|
||||||
|
private static final int MAX_RADIUS = 8;
|
||||||
|
private static final int SIZE_RADIUS = 9;
|
||||||
|
private static final int REGION_MASK = 15;
|
||||||
|
private static final int REGION_SHIFT = 4;
|
||||||
|
|
||||||
|
private final LongArrayList[] centersByRadius;
|
||||||
|
private final Long2LongOpenHashMap regionBitSets;
|
||||||
|
|
||||||
|
public NatureSpawnChunkMap() {
|
||||||
|
this.centersByRadius = new LongArrayList[SIZE_RADIUS];
|
||||||
|
for (int i = 0; i < SIZE_RADIUS; i++) {
|
||||||
|
this.centersByRadius[i] = new LongArrayList();
|
||||||
|
}
|
||||||
|
this.regionBitSets = new Long2LongOpenHashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
for (LongArrayList chunkPosition : this.centersByRadius) {
|
||||||
|
chunkPosition.clear();
|
||||||
|
}
|
||||||
|
this.regionBitSets.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayer(ServerPlayer player) {
|
||||||
|
if (player.isSpectator()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PlayerNaturallySpawnCreaturesEvent event = player.playerNaturallySpawnedEvent;
|
||||||
|
if (event == null || event.isCancelled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int range = event.getSpawnRadius();
|
||||||
|
if (range > MAX_RADIUS) {
|
||||||
|
range = MAX_RADIUS;
|
||||||
|
} else if (range < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.centersByRadius[range].add(player.chunkPosition().longKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void build() {
|
||||||
|
for (int index = 0; index < SIZE_RADIUS; index++) {
|
||||||
|
buildBy(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildBy(int index) {
|
||||||
|
LongArrayList list = this.centersByRadius[index];
|
||||||
|
int centersSize = deduplicate(list);
|
||||||
|
if (centersSize == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long[] centersRaw = list.elements();
|
||||||
|
long cachedKey = ChunkPos.asLong(ChunkPos.getX(centersRaw[0]) >> REGION_SHIFT, ChunkPos.getZ(centersRaw[0]) >> REGION_SHIFT);
|
||||||
|
long cachedVal = this.regionBitSets.get(cachedKey);
|
||||||
|
long[] offsets = TABLE_BFS[index];
|
||||||
|
for (int i = 0; i < centersSize; i++) {
|
||||||
|
long center = centersRaw[i];
|
||||||
|
int cx = ChunkPos.getX(center);
|
||||||
|
int cz = ChunkPos.getZ(center);
|
||||||
|
|
||||||
|
for (long packedOffset : offsets) {
|
||||||
|
int dx = ChunkPos.getX(packedOffset);
|
||||||
|
int dz = ChunkPos.getZ(packedOffset);
|
||||||
|
int chunkX = cx + dx;
|
||||||
|
int chunkZ = cz + dz;
|
||||||
|
|
||||||
|
int regionX = chunkX >> REGION_SHIFT;
|
||||||
|
int regionZ = chunkZ >> REGION_SHIFT;
|
||||||
|
long regionKey = ChunkPos.asLong(regionX, regionZ);
|
||||||
|
|
||||||
|
int localX = chunkX & REGION_MASK;
|
||||||
|
int localZ = chunkZ & REGION_MASK;
|
||||||
|
int bitIndex = (localZ << REGION_SHIFT) | localX;
|
||||||
|
long bitMask = 1L << bitIndex;
|
||||||
|
|
||||||
|
if (regionKey != cachedKey) {
|
||||||
|
this.regionBitSets.put(cachedKey, cachedVal);
|
||||||
|
cachedKey = regionKey;
|
||||||
|
cachedVal = this.regionBitSets.get(regionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedVal |= bitMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cachedVal != 0L) {
|
||||||
|
this.regionBitSets.put(cachedKey, cachedVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int deduplicate(LongArrayList list) {
|
||||||
|
int n = list.size();
|
||||||
|
if (n == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
list.unstableSort(null);
|
||||||
|
long[] centersRaw = list.elements();
|
||||||
|
int size = 0;
|
||||||
|
for (int i = 1; i < n; i++) {
|
||||||
|
long current = centersRaw[i];
|
||||||
|
long last = centersRaw[size];
|
||||||
|
if (current != last) {
|
||||||
|
size++;
|
||||||
|
centersRaw[size] = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void collectSpawningChunks(ReferenceList<LevelChunk> chunks, List<LevelChunk> out) {
|
||||||
|
LevelChunk[] raw = chunks.getRawDataUnchecked();
|
||||||
|
for (int i = 0, length = chunks.size(); i < length; i++) {
|
||||||
|
LevelChunk chunk = raw[i];
|
||||||
|
if (contains(chunk.locX, chunk.locZ)) {
|
||||||
|
out.add(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(int chunkX, int chunkZ) {
|
||||||
|
int regionX = chunkX >> REGION_SHIFT;
|
||||||
|
int regionZ = chunkZ >> REGION_SHIFT;
|
||||||
|
long bitset = this.regionBitSets.get(ChunkPos.asLong(regionX, regionZ));
|
||||||
|
return bitset != 0 && (bitset & (1L << (((chunkZ & REGION_MASK) << REGION_SHIFT) | (chunkX & REGION_MASK)))) != 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,115 +1,162 @@
|
|||||||
package org.dreeam.leaf.world;
|
package org.dreeam.leaf.world;
|
||||||
|
|
||||||
|
import ca.spottedleaf.moonrise.common.list.ReferenceList;
|
||||||
|
import ca.spottedleaf.moonrise.common.list.ShortList;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.util.RandomSource;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.GameRules;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunkSection;
|
||||||
|
import net.minecraft.world.level.levelgen.BitRandomSource;
|
||||||
import net.minecraft.world.level.material.FluidState;
|
import net.minecraft.world.level.material.FluidState;
|
||||||
|
|
||||||
import java.util.OptionalLong;
|
|
||||||
|
|
||||||
public final class RandomTickSystem {
|
public final class RandomTickSystem {
|
||||||
private static final long SCALE = 0x100000L;
|
private static final long SCALE = 0x100000L;
|
||||||
private static final long CHUNK_BLOCKS = 4096L;
|
private static final long TICK_FILTER_MASK = 0b11L;
|
||||||
|
private static final long CHUNK_BLOCKS = 4096L / 4L;
|
||||||
/// reduce unnecessary sampling and block counting
|
|
||||||
private static final long TICK_MASK = 0b11L;
|
|
||||||
private static final long TICK_MUL = 4L;
|
|
||||||
private static final int BITS_STEP = 2;
|
private static final int BITS_STEP = 2;
|
||||||
private static final int BITS_MAX = 60;
|
private static final int BITS_MAX = 60;
|
||||||
|
|
||||||
private final LongArrayList queue = new LongArrayList();
|
private final LongArrayList queue = new LongArrayList();
|
||||||
private final LongArrayList samples = new LongArrayList();
|
private final LongArrayList samples = new LongArrayList();
|
||||||
private final LongArrayList weights = new LongArrayList();
|
private final LongArrayList weights = new LongArrayList();
|
||||||
private long weightsSum = 0L;
|
|
||||||
|
|
||||||
private int bits = 60;
|
|
||||||
private long cacheRandom = 0L;
|
|
||||||
|
|
||||||
public void tick(ServerLevel world) {
|
public void tick(ServerLevel world) {
|
||||||
if (weights.isEmpty() || samples.isEmpty()) {
|
queue.clear();
|
||||||
|
samples.clear();
|
||||||
|
weights.clear();
|
||||||
|
|
||||||
|
final BitRandomSource random = world.simpleRandom;
|
||||||
|
final ReferenceList<LevelChunk> entityTickingChunks = world.moonrise$getEntityTickingChunks();
|
||||||
|
final int randomTickSpeed = world.getGameRules().getInt(GameRules.RULE_RANDOMTICKING);
|
||||||
|
final LevelChunk[] raw = entityTickingChunks.getRawDataUnchecked();
|
||||||
|
final int size = entityTickingChunks.size();
|
||||||
|
final boolean disableIceAndSnow = world.paperConfig().environment.disableIceAndSnow;
|
||||||
|
if (randomTickSpeed <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!disableIceAndSnow) {
|
||||||
final var random = world.simpleRandom;
|
iceSnow(world, size, randomTickSpeed, random, raw);
|
||||||
final long chosen;
|
|
||||||
if (((weightsSum % SCALE) >= boundedNextLong(random, SCALE))) {
|
|
||||||
chosen = weightsSum / SCALE + 1L;
|
|
||||||
} else {
|
|
||||||
chosen = weightsSum / SCALE;
|
|
||||||
}
|
}
|
||||||
|
final long weightsSum = collectTickingChunks(size, random, raw, randomTickSpeed);
|
||||||
|
if (samples.isEmpty() || weightsSum == 0L) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sampling(random, weightsSum);
|
||||||
|
|
||||||
|
final long[] q = queue.elements();
|
||||||
|
final int minY = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(world) << 4;
|
||||||
|
for (int k = 0, len = queue.size(); k < len; ++k) {
|
||||||
|
final long packed = q[k];
|
||||||
|
final LevelChunk chunk = raw[(int) (packed >>> 16)];
|
||||||
|
tickBlock(world, chunk, (int) (packed & 0xFFFF), random, minY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sampling(BitRandomSource random, long weightsSum) {
|
||||||
|
final long chosen = ((weightsSum % SCALE) >= boundedNextLong(random, SCALE))
|
||||||
|
? (weightsSum / SCALE + 1L)
|
||||||
|
: (weightsSum / SCALE);
|
||||||
if (chosen == 0L) {
|
if (chosen == 0L) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final long[] w = weights.elements();
|
||||||
|
final long[] s = samples.elements();
|
||||||
|
long accumulated = w[0];
|
||||||
final long spoke = weightsSum / chosen;
|
final long spoke = weightsSum / chosen;
|
||||||
if (spoke == 0L) {
|
if (spoke == 0L) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final long[] weightsRaw = weights.elements();
|
|
||||||
final long[] samplesRaw = samples.elements();
|
|
||||||
|
|
||||||
long accumulated = weightsRaw[0];
|
|
||||||
long current = boundedNextLong(random, spoke);
|
long current = boundedNextLong(random, spoke);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (current < weightsSum) {
|
while (current < weightsSum) {
|
||||||
while (accumulated < current) {
|
while (accumulated < current) {
|
||||||
i += 1;
|
i++;
|
||||||
accumulated += weightsRaw[i];
|
accumulated += w[i];
|
||||||
}
|
}
|
||||||
queue.add(samplesRaw[i]);
|
queue.add(s[i]);
|
||||||
current += spoke;
|
current += spoke;
|
||||||
}
|
}
|
||||||
while (queue.size() < chosen) {
|
|
||||||
queue.add(samplesRaw[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
long[] queueRaw = queue.elements();
|
|
||||||
int j = 0;
|
|
||||||
int k;
|
|
||||||
for (k = queue.size() - 3; j < k; j += 4) {
|
|
||||||
final long packed1 = queueRaw[j];
|
|
||||||
final long packed2 = queueRaw[j + 1];
|
|
||||||
final long packed3 = queueRaw[j + 2];
|
|
||||||
final long packed4 = queueRaw[j + 3];
|
|
||||||
final LevelChunk chunk1 = getChunk(world, packed1);
|
|
||||||
final LevelChunk chunk2 = packed1 != packed2 ? getChunk(world, packed2) : chunk1;
|
|
||||||
final LevelChunk chunk3 = packed2 != packed3 ? getChunk(world, packed3) : chunk2;
|
|
||||||
final LevelChunk chunk4 = packed3 != packed4 ? getChunk(world, packed4) : chunk3;
|
|
||||||
if (chunk1 != null) tickBlock(world, chunk1, random);
|
|
||||||
if (chunk2 != null) tickBlock(world, chunk2, random);
|
|
||||||
if (chunk3 != null) tickBlock(world, chunk3, random);
|
|
||||||
if (chunk4 != null) tickBlock(world, chunk4, random);
|
|
||||||
}
|
|
||||||
for (k = queue.size(); j < k; j++) {
|
|
||||||
final LevelChunk chunk = getChunk(world, queueRaw[j]);
|
|
||||||
if (chunk != null) tickBlock(world, chunk, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
weightsSum = 0L;
|
|
||||||
queue.clear();
|
|
||||||
weights.clear();
|
|
||||||
samples.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LevelChunk getChunk(ServerLevel world, long packed) {
|
private long collectTickingChunks(int size, BitRandomSource random, LevelChunk[] raw, long randomTickSpeed) {
|
||||||
return world.chunkSource.getChunkAtIfLoadedImmediately((int) packed, (int) (packed >> 32));
|
int bits = 0;
|
||||||
|
long cacheRandom = random.nextLong();
|
||||||
|
long weightsSum = 0L;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (bits != BITS_MAX) {
|
||||||
|
bits += BITS_STEP;
|
||||||
|
} else {
|
||||||
|
bits = 0;
|
||||||
|
cacheRandom = random.nextLong();
|
||||||
|
}
|
||||||
|
if ((cacheRandom & (TICK_FILTER_MASK << bits)) != 0L) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final LevelChunk chunk = raw[i];
|
||||||
|
if (chunk.leaf$tickingBlocksDirty) {
|
||||||
|
populateChunkTickingCount(chunk);
|
||||||
|
}
|
||||||
|
int[] data = chunk.leaf$tickingCount;
|
||||||
|
for (int packed : data) {
|
||||||
|
int count = packed >>> 16;
|
||||||
|
int idx = packed & 0xFFFF;
|
||||||
|
samples.add((((long) i) << 16 | idx));
|
||||||
|
long weight = (randomTickSpeed * count * SCALE) / CHUNK_BLOCKS;
|
||||||
|
weights.add(weight);
|
||||||
|
weightsSum += weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return weightsSum;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void tickBlock(ServerLevel world, LevelChunk chunk, RandomSource random) {
|
private static void populateChunkTickingCount(LevelChunk chunk) {
|
||||||
int count = chunk.leaf$tickingBlocksCount();
|
chunk.leaf$tickingBlocksDirty = false;
|
||||||
if (count == 0) {
|
int sum = 0;
|
||||||
return;
|
for (LevelChunkSection section : chunk.getSections()) {
|
||||||
|
sum += (section.moonrise$getTickingBlockList().size() == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
OptionalLong optionalPos = chunk.leaf$getTickingPos(random.nextInt(count));
|
|
||||||
if (optionalPos.isEmpty()) {
|
if (chunk.leaf$tickingCount.length != sum) {
|
||||||
return;
|
chunk.leaf$tickingCount = new int[sum];
|
||||||
}
|
}
|
||||||
BlockPos pos = BlockPos.of(optionalPos.getAsLong());
|
|
||||||
BlockState state = chunk.getBlockStateFinal(pos.getX(), pos.getY(), pos.getZ());
|
int k = 0;
|
||||||
|
LevelChunkSection[] sections = chunk.getSections();
|
||||||
|
for (int j = 0; j < sections.length; j++) {
|
||||||
|
ShortList list = sections[j].moonrise$getTickingBlockList();
|
||||||
|
int n = list.size();
|
||||||
|
if (n != 0) {
|
||||||
|
chunk.leaf$tickingCount[k++] = (n << 16) | (j & 0xFFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void iceSnow(ServerLevel world, int size, int randomTickSpeed, BitRandomSource random, LevelChunk[] raw) {
|
||||||
|
int currentIceAndSnowTick = random.nextInt(48 * 16);
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
currentIceAndSnowTick -= randomTickSpeed;
|
||||||
|
if (currentIceAndSnowTick <= 0) {
|
||||||
|
currentIceAndSnowTick = random.nextInt(48 * 16);
|
||||||
|
LevelChunk chunk = raw[i];
|
||||||
|
ChunkPos pos = chunk.getPos();
|
||||||
|
world.tickPrecipitation(world.getBlockRandomPos(pos.getMinBlockX(), 0, pos.getMinBlockZ(), 15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void tickBlock(ServerLevel world, LevelChunk chunk, int sectionIdx, BitRandomSource random, int minSection) {
|
||||||
|
LevelChunkSection section = chunk.getSection(sectionIdx);
|
||||||
|
ShortList list = section.moonrise$getTickingBlockList();
|
||||||
|
int size = list.size();
|
||||||
|
if (size == 0) return;
|
||||||
|
short location = list.getRaw(boundedNextInt(random, size));
|
||||||
|
BlockState state = section.states.get(location);
|
||||||
|
final BlockPos pos = new BlockPos((location & 15) | (chunk.locX << 4), (location >>> 8) | (minSection + (sectionIdx << 4)), ((location >>> 4) & 15) | (chunk.locZ << 4));
|
||||||
state.randomTick(world, pos, random);
|
state.randomTick(world, pos, random);
|
||||||
|
|
||||||
final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294();
|
final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294();
|
||||||
@@ -121,43 +168,13 @@ public final class RandomTickSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tickChunk(
|
private static long boundedNextLong(BitRandomSource rng, long bound) {
|
||||||
RandomSource random,
|
final long m = bound - 1L;
|
||||||
LevelChunk chunk,
|
|
||||||
long tickSpeed
|
|
||||||
) {
|
|
||||||
if (this.bits == BITS_MAX) {
|
|
||||||
this.bits = 0;
|
|
||||||
this.cacheRandom = random.nextLong();
|
|
||||||
} else {
|
|
||||||
this.bits += BITS_STEP;
|
|
||||||
}
|
|
||||||
if ((this.cacheRandom & (TICK_MASK << bits)) == 0L) {
|
|
||||||
long count = chunk.leaf$tickingBlocksCount();
|
|
||||||
if (count != 0L) {
|
|
||||||
long weight = (TICK_MUL * tickSpeed * count * SCALE) / CHUNK_BLOCKS;
|
|
||||||
samples.add(chunk.getPos().longKey);
|
|
||||||
weights.add(weight);
|
|
||||||
weightsSum += weight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param rng a random number generator to be used as a
|
|
||||||
* source of pseudorandom {@code long} values
|
|
||||||
* @param bound the upper bound (exclusive); must be greater than zero
|
|
||||||
*
|
|
||||||
* @return a pseudorandomly chosen {@code long} value
|
|
||||||
*
|
|
||||||
* @see java.util.random.RandomGenerator#nextLong(long) nextLong(bound)
|
|
||||||
*/
|
|
||||||
public static long boundedNextLong(RandomSource rng, long bound) {
|
|
||||||
final long m = bound - 1;
|
|
||||||
long r = rng.nextLong();
|
long r = rng.nextLong();
|
||||||
if ((bound & m) == 0L) {
|
if ((bound & m) == 0L) {
|
||||||
r &= m;
|
r &= m;
|
||||||
} else {
|
} else {
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
for (long u = r >>> 1;
|
for (long u = r >>> 1;
|
||||||
u + m - (r = u % bound) < 0L;
|
u + m - (r = u % bound) < 0L;
|
||||||
u = rng.nextLong() >>> 1)
|
u = rng.nextLong() >>> 1)
|
||||||
@@ -165,4 +182,19 @@ public final class RandomTickSystem {
|
|||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int boundedNextInt(BitRandomSource rng, int bound) {
|
||||||
|
final int m = bound - 1;
|
||||||
|
int r = rng.nextInt();
|
||||||
|
if ((bound & m) == 0) {
|
||||||
|
r &= m;
|
||||||
|
} else {
|
||||||
|
//noinspection StatementWithEmptyBody
|
||||||
|
for (int u = r >>> 1;
|
||||||
|
u + m - (r = u % bound) < 0;
|
||||||
|
u = rng.nextInt() >>> 1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user