mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-26 02:19:19 +00:00
* prevents async entity tracker update equipment * fix seenBy updated check * skip submit empty * fix invertedVisibilityEntities data race * strict thread check * set max-threads to 1 by default * use fixed thread count * increase thread priority * Revert "use fixed thread count" This reverts commit6746bc25a8. * Revert "set max-threads to 1 by default" This reverts commit5295b6d3e1. * update entity tracker * cleanup * [ci skip] fix phrasing * cleanup * cleanup * support Citizens * optimize update if chunk player no change * configurable threads * configurable no blocking * fix pos y and z * optimize no blocking * cleanup * cleanup * add handle during waitUntilNextTick * fix entity disappear * cleanup * disable nonblocking by default * [ci skip] add entity slice * impl fork-join * fix async locator diff * optimize queue * inline iterator * [ci skip] Update patch header * cleanup * improve compatibility * add license header * optimize spin wait * remove queue-size option * dynamic adjust subtasks --------- Co-authored-by: Taiyou06 <kaandindar21@gmail.com> Co-authored-by: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
174 lines
8.0 KiB
Diff
174 lines
8.0 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Taiyou06 <kaandindar21@gmail.com>
|
|
Date: Wed, 19 Mar 2025 18:41:56 +0100
|
|
Subject: [PATCH] Use BFS on getSlopeDistance
|
|
|
|
Uses Breadth First Search (BFS) to optimize getSlopeDistance
|
|
Paper: ~75ms
|
|
Leaf: ~48ms (-36%)
|
|
This should help drastically on the farms that use actively changing fluids.
|
|
|
|
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
|
index bbe2a420193d15874a4dd384ddb3e24ee2d62420..2ded2c36adc079b9bd3ab4857fb4043f92f52378 100644
|
|
--- a/net/minecraft/server/level/ServerLevel.java
|
|
+++ b/net/minecraft/server/level/ServerLevel.java
|
|
@@ -1440,6 +1440,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
|
this.emptyTime = 0;
|
|
}
|
|
|
|
+ // Leaf start - Use BFS on getSlopeDistance
|
|
+ public it.unimi.dsi.fastutil.longs.LongSet slopeDistanceCacheVisited = new it.unimi.dsi.fastutil.longs.LongOpenHashSet(512);
|
|
+ public net.minecraft.world.level.material.FlowingFluid.SlopeDistanceNodeDeque slopeDistanceCacheQueue = new net.minecraft.world.level.material.FlowingFluid.SlopeDistanceNodeDeque();
|
|
+ // Leaf end - Use BFS on getSlopeDistance
|
|
private void tickFluid(BlockPos pos, Fluid fluid) {
|
|
BlockState blockState = this.getBlockState(pos);
|
|
FluidState fluidState = blockState.getFluidState();
|
|
diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java
|
|
index 4fe1b3fc6304a2a404fd0f62f52fc792bcd5dfaf..a00c38b0d6b6d32af4ab3e926705b0f2594157dd 100644
|
|
--- a/net/minecraft/world/level/material/FlowingFluid.java
|
|
+++ b/net/minecraft/world/level/material/FlowingFluid.java
|
|
@@ -342,32 +342,124 @@ public abstract class FlowingFluid extends Fluid {
|
|
|
|
protected abstract void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state);
|
|
|
|
- protected int getSlopeDistance(LevelReader level, BlockPos pos, int depth, Direction direction, BlockState state, FlowingFluid.SpreadContext spreadContext) {
|
|
- int i = 1000;
|
|
+ // Leaf start - Use BFS on getSlopeDistance
|
|
+ protected int getSlopeDistance(LevelReader level, BlockPos startPos, int initialDepth, Direction excludedDirection, BlockState startState, FlowingFluid.SpreadContext spreadContext) {
|
|
+ it.unimi.dsi.fastutil.longs.LongSet visited = ((ServerLevel) level).slopeDistanceCacheVisited;
|
|
+ SlopeDistanceNodeDeque queue = ((ServerLevel) level).slopeDistanceCacheQueue;
|
|
+ visited.clear();
|
|
+ queue.clear();
|
|
+
|
|
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
|
|
+ if (dir == excludedDirection) continue;
|
|
+
|
|
+ BlockPos neighborPos = startPos.relative(dir); // immutable
|
|
+ BlockState neighborState = spreadContext.getBlockStateIfLoaded(neighborPos);
|
|
+ if (neighborState == null) continue;
|
|
+
|
|
+ // Check if the fluid can actually pass through to this first neighbor before adding
|
|
+ FluidState neighborFluidState = neighborState.getFluidState();
|
|
+ if (!this.canPassThrough(level, this.getFlowing(), startPos, startState, dir, neighborPos, neighborState, neighborFluidState)) {
|
|
+ continue;
|
|
+ }
|
|
+ long visitKey = encodeSlopeNode(neighborPos, dir.getOpposite());
|
|
+ if (visited.add(visitKey)) {
|
|
+ queue.add(new FlowingFluid.SlopeDistanceNode(neighborPos, initialDepth, dir.getOpposite(), neighborState));
|
|
+ }
|
|
+ }
|
|
|
|
- for (Direction direction1 : Direction.Plane.HORIZONTAL) {
|
|
- if (direction1 != direction) {
|
|
- BlockPos blockPos = pos.relative(direction1);
|
|
- BlockState blockState = spreadContext.getBlockStateIfLoaded(blockPos); // Paper - Prevent chunk loading from fluid flowing
|
|
- if (blockState == null) continue; // Paper - Prevent chunk loading from fluid flowing
|
|
- FluidState fluidState = blockState.getFluidState();
|
|
- if (this.canPassThrough(level, this.getFlowing(), pos, state, direction1, blockPos, blockState, fluidState)) {
|
|
- if (spreadContext.isHole(blockPos)) {
|
|
- return depth;
|
|
- }
|
|
+ int slopeFindDistance = this.getSlopeFindDistance(level);
|
|
+ int minDistance = 1000;
|
|
|
|
- if (depth < this.getSlopeFindDistance(level)) {
|
|
- int slopeDistance = this.getSlopeDistance(level, blockPos, depth + 1, direction1.getOpposite(), blockState, spreadContext);
|
|
- if (slopeDistance < i) {
|
|
- i = slopeDistance;
|
|
- }
|
|
- }
|
|
+ // Process the queue
|
|
+ while (!queue.isEmpty()) {
|
|
+ FlowingFluid.SlopeDistanceNode current = queue.poll();
|
|
+ if (spreadContext.isHole(current.pos)) {
|
|
+ return current.depth;
|
|
+ }
|
|
+
|
|
+ if (current.depth >= slopeFindDistance) continue;
|
|
+
|
|
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
|
|
+ if (dir == current.excludedDir) continue;
|
|
+
|
|
+ BlockPos nextPos = current.pos.relative(dir); // immutable
|
|
+ BlockState nextState = spreadContext.getBlockStateIfLoaded(nextPos);
|
|
+ if (nextState == null) continue;
|
|
+
|
|
+ FluidState nextFluidState = nextState.getFluidState();
|
|
+ if (!this.canPassThrough(level, this.getFlowing(), current.pos, current.state, dir, nextPos, nextState, nextFluidState)) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ long visitKey = encodeSlopeNode(nextPos, dir.getOpposite());
|
|
+ if (visited.add(visitKey)) {
|
|
+ queue.add(new FlowingFluid.SlopeDistanceNode(nextPos, current.depth + 1, dir.getOpposite(), nextState));
|
|
}
|
|
}
|
|
}
|
|
|
|
- return i;
|
|
+ return minDistance;
|
|
+ }
|
|
+
|
|
+ private static long encodeSlopeNode(BlockPos pos, Direction excludedDir) {
|
|
+ return ((long) pos.getX() & 0xFFFFFFFFL) << 32 | ((long) pos.getZ() & 0xFFFFFFFFL) << 4 | (excludedDir.ordinal() & 0x0F);
|
|
+ }
|
|
+
|
|
+ public static class SlopeDistanceNodeDeque {
|
|
+ private SlopeDistanceNode[] array;
|
|
+ private int length;
|
|
+ private int start;
|
|
+ private int end;
|
|
+
|
|
+ public SlopeDistanceNodeDeque() {
|
|
+ array = new SlopeDistanceNode[256];
|
|
+ length = array.length;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ private int size() {
|
|
+ int apparent = end - start;
|
|
+ return apparent >= 0 ? apparent : length + apparent;
|
|
+ }
|
|
+ */
|
|
+
|
|
+ private void clear() {
|
|
+ start = 0;
|
|
+ end = 0;
|
|
+ }
|
|
+
|
|
+ private boolean isEmpty() {
|
|
+ return end == start || (end <= start && length == start - end);
|
|
+ }
|
|
+
|
|
+ private SlopeDistanceNode poll() {
|
|
+ final SlopeDistanceNode t = array[start];
|
|
+ if (++start == length) start = 0;
|
|
+ return t;
|
|
+ }
|
|
+
|
|
+ private void add(final SlopeDistanceNode node) {
|
|
+ array[end++] = node;
|
|
+ if (end == length) end = 0;
|
|
+ if (end == start) resize(length, 2 * length);
|
|
+ }
|
|
+
|
|
+ private void resize(final int size, final int newLength) {
|
|
+ final SlopeDistanceNode[] newArray = new SlopeDistanceNode[newLength];
|
|
+ if (size != 0) {
|
|
+ System.arraycopy(array, start, newArray, 0, length - start);
|
|
+ System.arraycopy(array, 0, newArray, length - start, end);
|
|
+ }
|
|
+ start = 0;
|
|
+ end = size;
|
|
+ array = newArray;
|
|
+ length = newLength;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private record SlopeDistanceNode(BlockPos pos, int depth, Direction excludedDir, BlockState state) {
|
|
}
|
|
+ // Leaf end - Use BFS on getSlopeDistance
|
|
|
|
boolean isWaterHole(BlockGetter level, BlockPos pos, BlockState state, BlockPos belowPos, BlockState belowState) {
|
|
return canPassThroughWall(Direction.DOWN, level, pos, state, belowPos, belowState)
|