9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2026-01-06 15:51:31 +00:00
Files
Leaf/leaf-server/minecraft-patches/features/0129-Use-BFS-on-getSlopeDistance-in-fluids.patch
Taiyou cd7689b16f Chunk improvements (#231)
* perf: SpatialPlayerIndex for isChunkNearPlayer

* perf: ensureCapacity with collectTickingChunks

* perf: optimize getSlopeDistance

* perf: optimize AABB Intersections

* perf: implement custom arrays for regions and caches

* perf: Improve SortedArraySet sorting (needs testing)

* rebase 1.21.4

* perf: optimize ClientBoundLightUpdatePacketData

* perf: O(1) Array Writes during Chunk Loading

* perf: Optimize LinearPalette (no not the linear format)

* perf: Rewrite ConcurrentLongHashSet

* rebase 1.21.4

* Fix Multithreaded Tracker (#236)

* duke gonna arrest me

* i hate git v2

* rebase

* dont worry ill change the name of this patch

* perf: Rewrite ConcurrentLongHashSet again

* perf: Optimize sendChunk

* [ci skip]

* cleanup

* aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

* cleanup

* remove streams on LinearPalette and SerializableChunkData

* actually commit them lmao

* actually commit them lmao 2

* fix

* rebase

* perf: clone less (could help with skyblocks)

* perf: more unload stuff

* perf: manual loop unrolling and bulk copy

* initial size for SerializeableChunkData

* perf: async chunkSend

* cleanup asyncChunkSend

* remove experimental tag

* rebase

---------

Co-authored-by: Creeam <102713261+HaHaWTH@users.noreply.github.com>
Co-authored-by: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
2025-03-05 22:45:26 +03:00

146 lines
6.8 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Taiyou06 <kaandindar21@gmail.com>
Date: Sun, 16 Feb 2025 15:15:16 +0100
Subject: [PATCH] Use BFS on getSlopeDistance in fluids
diff --git a/net/minecraft/world/level/material/FlowingFluid.java b/net/minecraft/world/level/material/FlowingFluid.java
index 4c2c2efd5380ff1fa5ad7553b51babae20f516ae..bd556e02f4e0822e2a62f53ceca7ad61bbfb736b 100644
--- a/net/minecraft/world/level/material/FlowingFluid.java
+++ b/net/minecraft/world/level/material/FlowingFluid.java
@@ -2,12 +2,16 @@ package net.minecraft.world.level.material;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.objects.Object2ByteLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.shorts.Short2BooleanMap;
import it.unimi.dsi.fastutil.shorts.Short2BooleanOpenHashMap;
import it.unimi.dsi.fastutil.shorts.Short2ObjectMap;
import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Queue;
+import java.util.Set;
+
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
@@ -342,30 +346,65 @@ 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;
+ int maxDepth = this.getSlopeFindDistance(level);
+ int initialCapacity = (int) Math.pow(4, maxDepth); // Pre-size based on max possible nodes
+ Queue<QueueEntry> queue = new java.util.ArrayDeque<>(initialCapacity);
+ it.unimi.dsi.fastutil.longs.LongSet visited = new it.unimi.dsi.fastutil.longs.LongOpenHashSet();
+
+ queue.add(new QueueEntry(pos, depth, direction));
+ visited.add(encodeVisited(pos, direction));
+
+ while (!queue.isEmpty()) {
+ QueueEntry current = queue.poll();
+ BlockPos currentPos = current.pos;
+ int currentDepth = current.depth;
+ Direction excludeDir = current.excludeDir;
+
+ for (Direction dir : Direction.Plane.HORIZONTAL) {
+ if (dir == excludeDir) continue;
+
+ BlockPos nextPos = currentPos.relative(dir);
+ Direction nextExcludeDir = dir.getOpposite();
+ long nextKey = encodeVisited(nextPos, nextExcludeDir);
+
+ if (!visited.add(nextKey)) continue;
+
+ BlockState blockState = spreadContext.getBlockStateIfLoaded(nextPos);
+ if (blockState == null) continue;
- 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;
+ if (this.canPassThrough(level, this.getFlowing(), currentPos, state, dir, nextPos, blockState, fluidState)) {
+ if (spreadContext.isHole(nextPos)) {
+ return currentDepth;
}
- if (depth < this.getSlopeFindDistance(level)) {
- int slopeDistance = this.getSlopeDistance(level, blockPos, depth + 1, direction1.getOpposite(), blockState, spreadContext);
- if (slopeDistance < i) {
- i = slopeDistance;
- }
+ if (currentDepth + 1 <= maxDepth) {
+ queue.add(new QueueEntry(nextPos, currentDepth + 1, nextExcludeDir));
}
}
}
}
- return i;
+ return 1000;
+ }
+
+ // Encode BlockPos and Direction into a long (x: 26 bits, z: 26 bits, dir: 4 bits)
+ private static long encodeVisited(BlockPos pos, Direction dir) {
+ return ((long) (pos.getX() & 0x3FFFFFF) << 38)
+ | ((long) (pos.getZ() & 0x3FFFFFF) << 12)
+ | (dir.ordinal() & 0xF);
+ }
+
+ private static class QueueEntry {
+ final BlockPos pos;
+ final int depth;
+ final Direction excludeDir;
+
+ QueueEntry(BlockPos pos, int depth, Direction excludeDir) {
+ this.pos = pos.immutable();
+ this.depth = depth;
+ this.excludeDir = excludeDir;
+ }
}
boolean isWaterHole(BlockGetter level, BlockPos pos, BlockState state, BlockPos belowPos, BlockState belowState) {
@@ -612,12 +651,30 @@ public abstract class FlowingFluid extends Fluid {
}
public boolean isHole(BlockPos pos) {
- return this.holeCache.computeIfAbsent(this.getCacheKey(pos), s -> {
- BlockState blockState = this.getBlockState(pos, s);
- BlockPos blockPos = pos.below();
- BlockState blockState1 = this.level.getBlockState(blockPos);
- return FlowingFluid.this.isWaterHole(this.level, pos, blockState, blockPos, blockState1);
- });
+ short key = this.getCacheKey(pos);
+ // Fast path - check if we already have the result
+ if (this.holeCache.containsKey(key)) {
+ return this.holeCache.get(key);
+ }
+ // Get cached block state for current position
+ BlockState blockState = this.stateCache.get(key);
+ if (blockState == null) {
+ blockState = this.level.getBlockState(pos);
+ this.stateCache.put(key, blockState);
+ }
+ // Get position below and its key
+ BlockPos belowPos = pos.below();
+ short belowKey = this.getCacheKey(belowPos);
+ // Get cached block state for position below
+ BlockState belowState = this.stateCache.get(belowKey);
+ if (belowState == null) {
+ belowState = this.level.getBlockState(belowPos);
+ this.stateCache.put(belowKey, belowState);
+ }
+ // Compute result and cache it
+ boolean result = FlowingFluid.this.isWaterHole(this.level, pos, blockState, belowPos, belowState);
+ this.holeCache.put(key, result);
+ return result;
}
private short getCacheKey(BlockPos pos) {