mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-25 09:59:20 +00:00
perf(core): 优化方块实体的 tick 更新机制
This commit is contained in:
@@ -18,6 +18,7 @@ public abstract class BaseParticleBlockEntity extends BlockEntity {
|
||||
}
|
||||
|
||||
public static void tick(CEWorld ceWorld, BlockPos blockPos, ImmutableBlockState state, BaseParticleBlockEntity particle) {
|
||||
if (true) return;
|
||||
particle.tickCount++;
|
||||
if (particle.tickCount % 10 != 0) return;
|
||||
particle.animateTick(state, ceWorld.world(), blockPos);
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* This implementation references the BlockEntityTickersList implementation by Winds Studio,
|
||||
* available at: https://github.com/Winds-Studio/Leaf/blob/b9ebff/leaf-server/src/main/java/org/dreeam/leaf/util/list/BlockEntityTickersList.java
|
||||
* <p>
|
||||
* This work is licensed under the GNU General Public License v3.0 (GPLv3)
|
||||
*/
|
||||
package net.momirealms.craftengine.core.util;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.momirealms.craftengine.core.block.entity.tick.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 writeIndex = startSearchFromIndex;
|
||||
int lastCopyIndex = startSearchFromIndex;
|
||||
int matches = 0;
|
||||
|
||||
for (int readIndex = startSearchFromIndex; readIndex < size; readIndex++) {
|
||||
if (c.contains(readIndex)) {
|
||||
matches++;
|
||||
final int blockLength = readIndex - lastCopyIndex;
|
||||
if (blockLength > 0) {
|
||||
System.arraycopy(a, lastCopyIndex, a, writeIndex, blockLength);
|
||||
writeIndex += blockLength;
|
||||
}
|
||||
lastCopyIndex = readIndex + 1;
|
||||
|
||||
if (matches == requiredMatches) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final int finalBlockLength = size - lastCopyIndex;
|
||||
if (finalBlockLength > 0) {
|
||||
System.arraycopy(a, lastCopyIndex, a, writeIndex, finalBlockLength);
|
||||
writeIndex += finalBlockLength;
|
||||
}
|
||||
|
||||
if (writeIndex < size) {
|
||||
Arrays.fill(a, writeIndex, size, null);
|
||||
}
|
||||
size = writeIndex;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package net.momirealms.craftengine.core.world;
|
||||
|
||||
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.entity.BlockEntity;
|
||||
import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
|
||||
import net.momirealms.craftengine.core.util.BlockEntityTickersList;
|
||||
import net.momirealms.craftengine.core.world.chunk.CEChunk;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor;
|
||||
import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage;
|
||||
@@ -25,12 +25,14 @@ public abstract class CEWorld {
|
||||
protected final WorldHeight worldHeightAccessor;
|
||||
protected List<SectionPos> pendingLightSections = new ArrayList<>();
|
||||
protected final Set<SectionPos> lightSections = ConcurrentHashMap.newKeySet(128);
|
||||
protected final List<TickingBlockEntity> tickingBlockEntities = new ArrayList<>();
|
||||
protected final BlockEntityTickersList tickingBlockEntities = new BlockEntityTickersList();
|
||||
protected final List<TickingBlockEntity> pendingTickingBlockEntities = new ArrayList<>();
|
||||
protected volatile boolean isTickingBlockEntities = false;
|
||||
protected volatile boolean isUpdatingLights = false;
|
||||
protected SchedulerTask syncTickTask;
|
||||
protected SchedulerTask asyncTickTask;
|
||||
@SuppressWarnings("FieldCanBeLocal")
|
||||
private int tileTickPosition;
|
||||
|
||||
public CEWorld(World world, StorageAdaptor adaptor) {
|
||||
this.world = world;
|
||||
@@ -205,15 +207,15 @@ public abstract class CEWorld {
|
||||
this.pendingTickingBlockEntities.clear();
|
||||
}
|
||||
if (!this.tickingBlockEntities.isEmpty()) {
|
||||
ReferenceOpenHashSet<TickingBlockEntity> toRemove = new ReferenceOpenHashSet<>();
|
||||
for (TickingBlockEntity blockEntity : this.tickingBlockEntities) {
|
||||
for (this.tileTickPosition = 0; this.tileTickPosition < this.tickingBlockEntities.size(); this.tileTickPosition++) {
|
||||
TickingBlockEntity blockEntity = this.tickingBlockEntities.get(this.tileTickPosition);
|
||||
if (blockEntity.isValid()) {
|
||||
blockEntity.tick();
|
||||
} else {
|
||||
toRemove.add(blockEntity);
|
||||
this.tickingBlockEntities.markAsRemoved(this.tileTickPosition);
|
||||
}
|
||||
}
|
||||
this.tickingBlockEntities.removeAll(toRemove);
|
||||
this.tickingBlockEntities.removeMarkedEntries();
|
||||
}
|
||||
this.isTickingBlockEntities = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user