mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-06 00:17:58 +01:00
Improve efficiency of entity chunk update
This commit is contained in:
parent
9a5af9514e
commit
c24cc07b7c
@ -43,6 +43,7 @@ import net.minestom.server.utils.time.Cooldown;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -469,7 +470,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
|
||||
// Fix current chunk being null if the entity has been spawned before
|
||||
if (currentChunk == null) {
|
||||
currentChunk = instance.getChunkAt(position);
|
||||
refreshCurrentChunk(instance.getChunkAt(position));
|
||||
}
|
||||
|
||||
// Check if the entity chunk is loaded
|
||||
@ -836,6 +837,12 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
return currentChunk;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
protected void refreshCurrentChunk(Chunk currentChunk) {
|
||||
this.currentChunk = currentChunk;
|
||||
MinecraftServer.getUpdateManager().getThreadProvider().updateEntity(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity current instance.
|
||||
*
|
||||
@ -869,7 +876,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
|
||||
this.isActive = true;
|
||||
this.instance = instance;
|
||||
this.currentChunk = instance.getChunkAt(position.getX(), position.getZ());
|
||||
refreshCurrentChunk(instance.getChunkAt(position.getX(), position.getZ()));
|
||||
instance.UNSAFE_addEntity(this);
|
||||
spawn();
|
||||
EntitySpawnEvent entitySpawnEvent = new EntitySpawnEvent(this, instance);
|
||||
@ -1343,7 +1350,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
|
||||
player.refreshVisibleEntities(newChunk);
|
||||
}
|
||||
|
||||
this.currentChunk = newChunk;
|
||||
refreshCurrentChunk(newChunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,6 @@ import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.acquirable.AcquirableEntity;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import net.minestom.server.instance.SharedInstance;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -30,6 +28,7 @@ public abstract class ThreadProvider {
|
||||
private final Map<Chunk, ChunkEntry> chunkEntryMap = new HashMap<>();
|
||||
// Iterated over to refresh the thread used to update entities & chunks
|
||||
private final ArrayDeque<Chunk> chunks = new ArrayDeque<>();
|
||||
private final Set<Entity> updatableEntities = ConcurrentHashMap.newKeySet();
|
||||
private final Set<Entity> removedEntities = ConcurrentHashMap.newKeySet();
|
||||
|
||||
// Represents the maximum percentage of tick time
|
||||
@ -84,15 +83,6 @@ public abstract class ThreadProvider {
|
||||
return RefreshType.CONSTANT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how often entities thread should be updated.
|
||||
*
|
||||
* @return the refresh type
|
||||
*/
|
||||
public @NotNull RefreshType getEntityRefreshType() {
|
||||
return RefreshType.CONSTANT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum time used to refresh chunks & entities thread.
|
||||
*
|
||||
@ -156,12 +146,18 @@ public abstract class ThreadProvider {
|
||||
this.chunks.remove(chunk);
|
||||
}
|
||||
|
||||
protected int getThreadId(Chunk chunk) {
|
||||
/**
|
||||
* Finds the thread id associated to a {@link Chunk}.
|
||||
*
|
||||
* @param chunk the chunk to find the thread id from
|
||||
* @return the chunk thread id
|
||||
*/
|
||||
protected int getThreadId(@NotNull Chunk chunk) {
|
||||
return (int) (Math.abs(findThread(chunk)) % threads.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the update.
|
||||
* Prepares the update by creating the {@link TickThread} tasks.
|
||||
*
|
||||
* @param time the tick time in milliseconds
|
||||
*/
|
||||
@ -206,23 +202,25 @@ public abstract class ThreadProvider {
|
||||
return countDownLatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called at the end of each tick to clear removed entities,
|
||||
* refresh the chunk linked to an entity, and chunk threads based on {@link #findThread(Chunk)}.
|
||||
*
|
||||
* @param tickTime the duration of the tick in ms,
|
||||
* used to ensure that the refresh does not take more time than the tick itself
|
||||
*/
|
||||
public synchronized void refreshThreads(long tickTime) {
|
||||
// Clear removed entities
|
||||
{
|
||||
for (Entity entity : removedEntities) {
|
||||
AcquirableEntity acquirableEntity = entity.getAcquirable();
|
||||
ChunkEntry chunkEntry = acquirableEntity.getHandler().getChunkEntry();
|
||||
// Remove from list
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.remove(entity);
|
||||
}
|
||||
}
|
||||
this.removedEntities.clear();
|
||||
processRemovedEntities();
|
||||
}
|
||||
|
||||
final boolean chunkRefresh = getChunkRefreshType() != RefreshType.NEVER;
|
||||
final boolean entityRefresh = getEntityRefreshType() != RefreshType.NEVER;
|
||||
if (!chunkRefresh && !entityRefresh)
|
||||
// Update entities chunks
|
||||
{
|
||||
processUpdatedEntities();
|
||||
}
|
||||
|
||||
if (getChunkRefreshType() == RefreshType.NEVER)
|
||||
return;
|
||||
|
||||
final int timeOffset = MathUtils.clamp((int) ((double) tickTime * refreshPercentage),
|
||||
@ -238,20 +236,7 @@ public abstract class ThreadProvider {
|
||||
}
|
||||
|
||||
// Update chunk threads
|
||||
if (chunkRefresh) {
|
||||
switchChunk(chunk);
|
||||
}
|
||||
|
||||
// Update entities
|
||||
if (entityRefresh) {
|
||||
Instance instance = chunk.getInstance();
|
||||
refreshEntitiesThread(instance, chunk);
|
||||
if (instance instanceof InstanceContainer) {
|
||||
for (SharedInstance sharedInstance : ((InstanceContainer) instance).getSharedInstances()) {
|
||||
refreshEntitiesThread(sharedInstance, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
switchChunk(chunk);
|
||||
|
||||
// Add back to the deque
|
||||
chunks.addLast(chunk);
|
||||
@ -261,10 +246,13 @@ public abstract class ThreadProvider {
|
||||
|
||||
if (System.currentTimeMillis() >= endTime)
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void updateEntity(@NotNull Entity entity) {
|
||||
this.updatableEntities.add(entity);
|
||||
}
|
||||
|
||||
public void removeEntity(@NotNull Entity entity) {
|
||||
this.removedEntities.add(entity);
|
||||
}
|
||||
@ -277,34 +265,44 @@ public abstract class ThreadProvider {
|
||||
return threads;
|
||||
}
|
||||
|
||||
private void refreshEntitiesThread(Instance instance, Chunk chunk) {
|
||||
var entities = instance.getChunkEntities(chunk);
|
||||
for (Entity entity : entities) {
|
||||
private void processRemovedEntities() {
|
||||
for (Entity entity : removedEntities) {
|
||||
AcquirableEntity acquirableEntity = entity.getAcquirable();
|
||||
ChunkEntry chunkEntry = acquirableEntity.getHandler().getChunkEntry();
|
||||
// Remove from list
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.remove(entity);
|
||||
}
|
||||
}
|
||||
this.removedEntities.clear();
|
||||
}
|
||||
|
||||
private void processUpdatedEntities() {
|
||||
for (Entity entity : updatableEntities) {
|
||||
AcquirableEntity acquirableEntity = entity.getAcquirable();
|
||||
ChunkEntry handlerChunkEntry = acquirableEntity.getHandler().getChunkEntry();
|
||||
Chunk batchChunk = handlerChunkEntry != null ? handlerChunkEntry.getChunk() : null;
|
||||
|
||||
Chunk entityChunk = entity.getChunk();
|
||||
if (!Objects.equals(batchChunk, entityChunk)) {
|
||||
// Entity is possibly not in the correct thread
|
||||
|
||||
// Remove from previous list
|
||||
{
|
||||
if (handlerChunkEntry != null) {
|
||||
handlerChunkEntry.entities.remove(entity);
|
||||
}
|
||||
// Entity is possibly not in the correct thread
|
||||
|
||||
// Remove from previous list
|
||||
{
|
||||
if (handlerChunkEntry != null) {
|
||||
handlerChunkEntry.entities.remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Add to new list
|
||||
{
|
||||
ChunkEntry chunkEntry = chunkEntryMap.get(entityChunk);
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.add(entity);
|
||||
acquirableEntity.getHandler().refreshChunkEntry(chunkEntry);
|
||||
}
|
||||
// Add to new list
|
||||
{
|
||||
ChunkEntry chunkEntry = chunkEntryMap.get(entityChunk);
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.add(entity);
|
||||
acquirableEntity.getHandler().refreshChunkEntry(chunkEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.updatableEntities.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user