9
0
mirror of https://github.com/Winds-Studio/Leaf.git synced 2025-12-26 02:19:19 +00:00

Bunch of side perf improvements (#217)

* Fix TE Lag

* Sepals Rearrange the attackable conditions

* Cache ItemStack max stack size

* fix build

* extra: Skip dirty stats copy when requesting player stats

* extra: Reset dirty flag when loading maps from the disk

* extra: Supporting block cache

* extra: Avoid useless deque clear on - credit: @MachineBreaker

* experimental/draft: Optimize SortedArraySet

* experimental/draft: Simplify SortedArraySet - sometime complex stuff doesnt mean faster.

* extra: Change maps/sets in brain + remove streams from villagers

* extra: Remove 'copyOf' from Baby Villager Sensor

* experimental: Rewrite trigger in SimpleCriterionTrigger

* [ci/skip] fix comments

* Faster setter for SimpleCriterionTrigger

* extra: Cache and optimize fluidOnEyes

* Sync changes

* [ci/skip] cleanup

* extra: QuadTree implementation for isChunkNearPlayer

* [ci/skip] cleanup

* [ci/skip] cleanup

* [ci/skip] clean up

* [ci/skip] cleanup

* Only player pushable

* Store chunkPos with keys

* [ci/skip] cleanup

* [ci/skip] cleanup

* cleanup

* rebuild patches

* cache some more stuff

* extra: optimize collectTickingChunks

* remove quadTree optimization for now (will open a new PR just for that)

* temp: Lazily optimize isChunkNearPlayer

* Inline filter & merge as a single loop

* [ci/skip] Add diff on change

* extra: optimize everything but the testing itself on getEntities

* [ci/skip] cleanup

* Optimize chunkUnloadQueue

* Remove iterators from inventory

* [ci/skip] Add TODOs

* i hate programming

* remove forEach

* extra: Alternative Brain Behaviour

* remove: opt getEntities + cache fluidOnEyes

* extra: Improve checkDespawn - credits: @kidofcubes

* extra: Improve pushEntity and getEntities

* yeet this

* VERY EXPERIMENTAL: getEntities Optimization

* fix bunch of issues from getEntities patch

* extra: slightly optimize getNearestPlayer - credits: @kidofcubes

* drop a patch for now (will open a new pr)

* move these to a new branch

* fix and optimize checkDespawn patches

* Rebuild Patches

* [ci/skip] Update benchmark

* [ci/skip] cleanup

* Drop

* [ci/skip] Drop

* Rebuild

* [ci/skip]

* Add configurable brain running behavior cache update interval

* Move to new pr

* [ci/skip] Update benchmark

---------

Co-authored-by: MachineBreaker <saltspigotpp@gmail.com>
Co-authored-by: kidofcubes <kidofcubes@gmail.com>
Co-authored-by: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com>
This commit is contained in:
Kaan D.
2025-02-22 08:23:53 +01:00
committed by GitHub
parent 20aa0c932b
commit 502385840d
22 changed files with 1097 additions and 1 deletions

View File

@@ -0,0 +1,21 @@
package org.dreeam.leaf.config.modules.gameplay;
import org.dreeam.leaf.config.ConfigModules;
import org.dreeam.leaf.config.EnumConfigCategory;
public class OnlyPlayerPushable extends ConfigModules {
public String getBasePath() {
return EnumConfigCategory.GAMEPLAY.getBaseKeyName() + ".only-player-pushable";
}
public static boolean enabled = false;
@Override
public void onLoaded() {
enabled = config.getBoolean(getBasePath(), enabled, """
Enable to make only player pushable
This option override the armorstand doCollisionEntityLookups and
...write in docs""");
}
}

View File

@@ -0,0 +1,21 @@
package org.dreeam.leaf.config.modules.opt;
import org.dreeam.leaf.config.ConfigModules;
import org.dreeam.leaf.config.EnumConfigCategory;
public class BrainRunningBehaviorCacheUpdate extends ConfigModules {
public String getBasePath() {
return EnumConfigCategory.PERF.getBaseKeyName();
}
public static int interval = 5;
@Override
public void onLoaded() {
interval = config.getInt(getBasePath() + ".entity-running-behavior-cache-update-interval", interval,
config.pickStringRegionBased(
"How often entity update current brain running behavior list.",
"生物更新现有 Brain Behavior 列表缓存的间隔."));
}
}

View File

@@ -0,0 +1,103 @@
package org.dreeam.leaf.util.list;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
import java.util.Arrays;
import java.util.Collection;
/**
* A list for ServerLevel's blockEntityTickers
* <p>
* This list behaves identically to ObjectArrayList, but it has an additional method, `removeAllByIndex`, that allows a list of integers to be passed indicating what
* indexes should be deleted from the list
* <p>
* This is faster than using removeAll, since we don't need to compare the identity of each block entity, and faster than looping thru each index manually and deleting with remove,
* since we don't need to resize the array every single remove.
*/
public final class BlockEntityTickersList extends ObjectArrayList<TickingBlockEntity> {
private final IntOpenHashSet toRemove = new IntOpenHashSet();
private int startSearchFromIndex = -1;
/**
* Creates a new array list with {@link #DEFAULT_INITIAL_CAPACITY} capacity.
*/
public BlockEntityTickersList() {
super();
}
/**
* Creates a new array list and fills it with a given collection.
*
* @param c a collection that will be used to fill the array list.
*/
public BlockEntityTickersList(final Collection<? extends TickingBlockEntity> c) {
super(c);
}
/**
* Marks an entry as removed
*
* @param index the index of the item on the list to be marked as removed
*/
public void markAsRemoved(final int index) {
// The block entities list always loop starting from 0, so we only need to check if the startSearchFromIndex is -1 and that's it
if (this.startSearchFromIndex == -1)
this.startSearchFromIndex = index;
this.toRemove.add(index);
}
/**
* Removes elements that have been marked as removed.
*/
public void removeMarkedEntries() {
if (this.startSearchFromIndex == -1) // No entries in the list, skip
return;
removeAllByIndex(startSearchFromIndex, toRemove);
toRemove.clear();
this.startSearchFromIndex = -1; // Reset the start search index
}
/**
* Removes elements by their index.
*/
private void removeAllByIndex(final int startSearchFromIndex, final IntOpenHashSet c) { // can't use Set<Integer> because we want to avoid autoboxing when using contains
final int requiredMatches = c.size();
if (requiredMatches == 0)
return; // exit early, we don't need to do anything
final Object[] a = this.a;
int j = startSearchFromIndex;
int matches = 0;
for (int i = startSearchFromIndex; i < size; i++) { // If the user knows the first index to be removed, we can skip a lot of unnecessary comparsions
if (!c.contains(i)) {
// TODO: It can be possible to optimize this loop by tracking the start/finish and then using arraycopy to "skip" the elements,
// this would optimize cases where the index to be removed are far apart, HOWEVER it does have a big performance impact if you are doing
// "arraycopy" for each element
a[j++] = a[i];
} else {
matches++;
}
if (matches == requiredMatches) { // Exit the loop if we already removed everything, we don't need to check anything else
// We need to update the final size here, because we know that we already found everything!
// Because we know that the size must be currentSize - requiredMatches (because we have matched everything), let's update the value
// However, we need to copy the rest of the stuff over
if (i != (size - 1)) { // If it isn't the last index...
// i + 1 because we want to copy the *next* element over
// and the size - i - 1 is because we want to get the current size, minus the current index (which is i), and then - 1 because we want to copy -1 ahead (remember, we are adding +1 to copy the *next* element)
System.arraycopy(a, i + 1, a, j, size - i - 1);
}
j = size - requiredMatches;
break;
}
}
Arrays.fill(a, j, size, null);
size = j;
}
}