diff --git a/README.md b/README.md index 3cfa440f..9176fba9 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,7 @@ If these excellent projects hadn't appeared, Leaf wouldn't have become great. • Luminol
Nitori
Moonrise (during 1.21.1)
+ • Sakura

diff --git a/leaf-server/minecraft-patches/features/0163-Sakura-copy-EntityList-implementation-to-BasicEntity.patch b/leaf-server/minecraft-patches/features/0163-Sakura-copy-EntityList-implementation-to-BasicEntity.patch new file mode 100644 index 00000000..16a14fa4 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0163-Sakura-copy-EntityList-implementation-to-BasicEntity.patch @@ -0,0 +1,81 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Samsuik +Date: Thu, 1 May 2025 22:28:50 +0200 +Subject: [PATCH] Sakura: copy-EntityList-implementation-to-BasicEntityList + + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +index b6af8da084c83ee38bb3ecea6a98feb0c1c74d2a..4311309f14c069f929ffe86bf0a91d7df3222828 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/level/entity/ChunkEntitySlices.java +@@ -382,6 +382,13 @@ public final class ChunkEntitySlices { + + private E[] storage; + private int size; ++ // Sakura start - use methods from EntityList ++ private it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap entityToIndex = null; ++ private void setupIndexMap() { ++ this.entityToIndex = new it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap(2, 0.8f); ++ this.entityToIndex.defaultReturnValue(Integer.MIN_VALUE); ++ } ++ // Sakura end - use methods from EntityList + + public BasicEntityList() { + this(0); +@@ -402,6 +409,7 @@ public final class ChunkEntitySlices { + private void resize() { + if (this.storage == me.titaniumtown.ArrayConstants.emptyEntityArray) { // Gale - JettPack - reduce array allocations + this.storage = (E[])new Entity[DEFAULT_CAPACITY]; ++ this.setupIndexMap(); // Sakura - use methods from EntityList + } else { + this.storage = Arrays.copyOf(this.storage, this.storage.length * 2); + } +@@ -415,6 +423,7 @@ public final class ChunkEntitySlices { + } else { + this.storage[idx] = entity; + } ++ this.entityToIndex.put(entity.getId(), idx); // Sakura - use methods from EntityList + } + + public int indexOf(final E entity) { +@@ -430,24 +439,32 @@ public final class ChunkEntitySlices { + } + + public boolean remove(final E entity) { +- final int idx = this.indexOf(entity); +- if (idx == -1) { ++ // Sakura start - use methods from EntityList ++ if (this.entityToIndex == null) { + return false; + } + +- final int size = --this.size; +- final E[] storage = this.storage; +- if (idx != size) { +- System.arraycopy(storage, idx + 1, storage, idx, size - idx); ++ final int index = this.entityToIndex.remove(entity.getId()); ++ if (index == Integer.MIN_VALUE) { ++ return false; + } + +- storage[size] = null; ++ // move the entity at the end to this index ++ final int endIndex = --this.size; ++ final E end = this.storage[endIndex]; ++ if (index != endIndex) { ++ // not empty after this call ++ this.entityToIndex.put(end.getId(), index); // update index ++ } ++ this.storage[index] = end; ++ this.storage[endIndex] = null; ++ // Sakura end - use methods from EntityList + + return true; + } + + public boolean has(final E entity) { +- return this.indexOf(entity) != -1; ++ return this.entityToIndex != null && this.entityToIndex.containsKey(entity.getId()); // Sakura - use methods from EntityList + } + } + diff --git a/leaf-server/minecraft-patches/features/0164-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch b/leaf-server/minecraft-patches/features/0164-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch new file mode 100644 index 00000000..053f1b90 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0164-Sakura-Optimise-check-inside-blocks-and-traverse-blo.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Thu, 1 May 2025 22:38:48 +0200 +Subject: [PATCH] Sakura: Optimise-check-inside-blocks-and-traverse-blocks + + +diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java +index 4221e5322fa3a3ff6ab53946aa71d54144d2c4b2..4474639b0411236e208c542973084864365c544c 100644 +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -1670,6 +1670,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + private void checkInsideBlocks(List movements, Set blocksInside) { + if (this.isAffectedByBlocks()) { + LongSet set = this.visitedBlocks; ++ // Sakura start - optimise check inside blocks ++ int lastChunkX = Integer.MIN_VALUE; ++ int lastChunkZ = Integer.MIN_VALUE; ++ net.minecraft.world.level.chunk.ChunkAccess chunk = null; ++ // Sakura end - optimise check inside blocks + + for (Entity.Movement movement : movements) { + Vec3 vec3 = movement.from(); +@@ -1681,7 +1686,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + return; + } + +- BlockState blockState = this.level().getBlockState(blockPos); ++ // Sakura start - optimise check inside blocks ++ final int chunkX = blockPos.getX() >> 4; ++ final int chunkZ = blockPos.getZ() >> 4; ++ if (chunk == null || chunkX != lastChunkX || chunkZ != lastChunkZ) { ++ chunk = this.level.getChunkIfLoadedImmediately(chunkX, chunkZ); ++ if (chunk == null) { ++ continue; ++ } ++ lastChunkX = chunkX; ++ lastChunkZ = chunkZ; ++ } ++ final BlockState blockState = chunk.getBlockState(blockPos); ++ // Sakura end - optimise check inside blocks + if (!blockState.isAir() && set.add(blockPos.asLong())) { + try { + VoxelShape entityInsideCollisionShape = blockState.getEntityInsideCollisionShape(this.level(), blockPos); +diff --git a/net/minecraft/world/level/BlockGetter.java b/net/minecraft/world/level/BlockGetter.java +index 91865d7e78e15cc643a65de03045b90a52d6ec2a..03f82e60528738e89f195cfc59094f53156f5370 100644 +--- a/net/minecraft/world/level/BlockGetter.java ++++ b/net/minecraft/world/level/BlockGetter.java +@@ -214,10 +214,18 @@ public interface BlockGetter extends LevelHeightAccessor { + + static Iterable boxTraverseBlocks(Vec3 oldPosition, Vec3 position, AABB boundingBox) { + Vec3 vec3 = position.subtract(oldPosition); +- Iterable iterable = BlockPos.betweenClosed(boundingBox); ++ // Sakura start - optimise check inside blocks + if (vec3.lengthSqr() < Mth.square(0.99999F)) { +- return iterable; ++ return org.dreeam.leaf.util.map.BlockPosIterator.iterable(boundingBox); + } else { ++ final boolean xZero = vec3.x() == 0.0; ++ final boolean yZero = vec3.y() == 0.0; ++ final boolean zZero = vec3.z() == 0.0; ++ if (xZero && yZero || yZero && zZero || xZero && zZero) { ++ return org.dreeam.leaf.util.map.BlockPosIterator.traverseArea(vec3, boundingBox); ++ } ++ Iterable iterable = BlockPos.betweenClosed(boundingBox); ++ // Sakura end - optimise check inside blocks + Set set = new ObjectLinkedOpenHashSet<>(); + Vec3 minPosition = boundingBox.getMinPosition(); + Vec3 vec31 = minPosition.subtract(vec3); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/util/map/BlockPosIterator.java b/leaf-server/src/main/java/org/dreeam/leaf/util/map/BlockPosIterator.java new file mode 100644 index 00000000..bf269d54 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/util/map/BlockPosIterator.java @@ -0,0 +1,69 @@ +package org.dreeam.leaf.util.map; + +import com.google.common.collect.AbstractIterator; +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.util.Mth; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public final class BlockPosIterator extends AbstractIterator { + private final int startX; + private final int startY; + private final int startZ; + private final int endX; + private final int endY; + private final int endZ; + private @Nullable MutableBlockPos pos = null; + + public static Iterable iterable(AABB bb) { + return () -> new BlockPosIterator(bb); + } + + public static Iterable traverseArea(Vec3 vec, AABB boundingBox) { + double toTravel = Math.min(16.0 / vec.length(), 1.0); + Vec3 movement = vec.scale(toTravel); + AABB fromBB = boundingBox.move(-vec.x, -vec.y, -vec.z); + AABB searchArea = fromBB.expandTowards(movement); + return org.dreeam.leaf.util.map.BlockPosIterator.iterable(searchArea); + } + + public BlockPosIterator(AABB bb) { + this.startX = Mth.floor(bb.minX); + this.startY = Mth.floor(bb.minY); + this.startZ = Mth.floor(bb.minZ); + this.endX = Mth.floor(bb.maxX); + this.endY = Mth.floor(bb.maxY); + this.endZ = Mth.floor(bb.maxZ); + } + + @Override + protected BlockPos computeNext() { + MutableBlockPos pos = this.pos; + if (pos == null) { + return this.pos = new MutableBlockPos(this.startX, this.startY, this.startZ); + } else { + int x = pos.getX(); + int y = pos.getY(); + int z = pos.getZ(); + + if (y < this.endY) { + y += 1; + } else if (x < this.endX) { + x += 1; + y = this.startY; + } else if (z < this.endZ) { + z += 1; + x = this.startX; + } else { + return this.endOfData(); + } + + pos.set(x, y, z); + return pos; + } + } +}