Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: mob improvements #346

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/main/java/cn/nukkit/command/defaults/StatusCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import cn.nukkit.Server;
import cn.nukkit.command.CommandSender;
import cn.nukkit.command.data.CommandParameter;
import cn.nukkit.entity.EntityThreadTicker;
import cn.nukkit.level.Level;
import cn.nukkit.math.NukkitMath;
import cn.nukkit.network.Network;
Expand Down Expand Up @@ -217,6 +218,8 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)

sender.sendMessage(TextFormat.GOLD + "Thread count: " + TextFormat.GREEN + Thread.getAllStackTraces().size());

sender.sendMessage(TextFormat.GOLD + "Entity thread: " + TextFormat.RED + EntityThreadTicker.getExecutionTime() + TextFormat.GREEN + " mspt" + ", last call count: " + TextFormat.RED + EntityThreadTicker.getLastCallCount());

Runtime runtime = Runtime.getRuntime();
double totalMB = NukkitMath.round(((double) runtime.totalMemory()) / 1024 / 1024, 2);
double usedMB = NukkitMath.round((double) (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024, 2);
Expand Down
67 changes: 67 additions & 0 deletions src/main/java/cn/nukkit/entity/EntityThreadTicker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package cn.nukkit.entity;

import cn.nukkit.utils.ThrowableRunnable;

import java.time.Instant;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class EntityThreadTicker {
private static final EntityThreadTicker INSTANCE = new EntityThreadTicker();

private static long timestamp = 0;
private static final AtomicInteger lastCallCount = new AtomicInteger(0);
private static final AtomicInteger callCount = new AtomicInteger(0);
private static final int[] executionTime = new int[20];

private Lock lock = new ReentrantLock();
private Executor executor = Executors.newSingleThreadExecutor();

public synchronized void tickEntities(ThrowableRunnable runnable) {
if (lock.tryLock()) {
lock.unlock();

executor.execute(() -> {
lock.lock();
try {
callCount.incrementAndGet();
if(timestamp != (System.currentTimeMillis() / 1000)) {
lastCallCount.set(callCount.get());
callCount.set(0);
timestamp = System.currentTimeMillis() / 1000;
}
var instant = Instant.now();
runnable.run();
int delta = (int) (Instant.now().toEpochMilli() - instant.toEpochMilli());
for (int index = executionTime.length - 2; index > 0; index--) {
executionTime[index] = executionTime[index + 1];
}
executionTime[0] = delta;
} finally {
lock.unlock();
}
});
}
}

public static int getLastCallCount() {
return lastCallCount.get();
}

public static int getExecutionTime() {
int time = 0;

for(int i : executionTime) {
time+=i;
}

return time / 20;
}

public static EntityThreadTicker getInstance() {
return INSTANCE;
}
}
30 changes: 17 additions & 13 deletions src/main/java/cn/nukkit/level/Level.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import cn.nukkit.blockentity.BlockEntity;
import cn.nukkit.entity.BaseEntity;
import cn.nukkit.entity.Entity;
import cn.nukkit.entity.EntityThreadTicker;
import cn.nukkit.entity.custom.EntityDefinition;
import cn.nukkit.entity.custom.EntityManager;
import cn.nukkit.entity.item.EntityItem;
Expand Down Expand Up @@ -1049,18 +1050,21 @@ public void doTick(int currentTick) {
}
}

if (!this.updateEntities.isEmpty()) {
for (long id : this.updateEntities.keySetLong()) {
Entity entity = this.updateEntities.get(id);
if (entity == null) {
this.updateEntities.remove(id);
continue;
}
if (entity.closed || !entity.onUpdate(currentTick)) {
this.updateEntities.remove(id);
// Tick entities in other thread for better performance
EntityThreadTicker.getInstance().tickEntities(() -> {
if (!this.updateEntities.isEmpty()) {
for (long id : this.updateEntities.keySetLong()) {
Entity entity = this.updateEntities.get(id);
if (entity == null) {
this.updateEntities.remove(id);
continue;
}
if (entity.closed || !entity.onUpdate(currentTick)) {
this.updateEntities.remove(id);
}
}
}
}
});

this.updateBlockEntities.removeIf(blockEntity -> !blockEntity.isValid() || !blockEntity.onUpdate());

Expand Down Expand Up @@ -2922,18 +2926,18 @@ public Entity[] getCollidingEntities(AxisAlignedBB bb, Entity entity) {
return nearby.toArray(new Entity[0]);
}

public Entity[] getNearbyEntities(AxisAlignedBB bb) {
public synchronized Entity[] getNearbyEntities(AxisAlignedBB bb) {
return this.getNearbyEntities(bb, null);
}

private static final Entity[] EMPTY_ENTITY_ARR = new Entity[0];
private static final Entity[] ENTITY_BUFFER = new Entity[512];

public Entity[] getNearbyEntities(AxisAlignedBB bb, Entity entity) {
public synchronized Entity[] getNearbyEntities(AxisAlignedBB bb, Entity entity) {
return getNearbyEntities(bb, entity, false);
}

public Entity[] getNearbyEntities(AxisAlignedBB bb, Entity entity, boolean loadChunks) {
public synchronized Entity[] getNearbyEntities(AxisAlignedBB bb, Entity entity, boolean loadChunks) {
int index = 0;

int minX = NukkitMath.floorDouble((bb.getMinX() - 2) * 0.0625);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
Expand Down Expand Up @@ -199,7 +200,7 @@ private static void convertChunk(BaseFullChunk oldChunk, LevelDBChunk newChunk,
newChunk.heightMap = Arrays.copyOf(oldChunk.getHeightMapArray(), oldChunk.getHeightMapArray().length);
newChunk.tiles = new Long2ObjectNonBlockingMap<>();
newChunk.tiles.putAll(oldChunk.getBlockEntities());
newChunk.entities = new Long2ObjectNonBlockingMap<>();
newChunk.entities = new ConcurrentHashMap<>();
newChunk.entities.putAll(oldChunk.getEntities());
newChunk.tileList = oldChunk.tileList;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* @author MagicDroidX
* Nukkit Project
*/
public abstract class BaseFullChunk implements FullChunk, ChunkManager {

protected Long2ObjectNonBlockingMap<Entity> entities;
protected ConcurrentHashMap<Long, Entity> entities;

protected Long2ObjectNonBlockingMap<BlockEntity> tiles;

Expand Down Expand Up @@ -414,7 +415,7 @@ public int getHighestBlockAt(int x, int z, boolean cache) {
@Override
public void addEntity(Entity entity) {
if (this.entities == null) {
this.entities = new Long2ObjectNonBlockingMap<>();
this.entities = new ConcurrentHashMap<>();
}
this.entities.put(entity.getId(), entity);
if (!(entity instanceof Player) && this.isInit) {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/cn/nukkit/utils/ThrowableRunnable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cn.nukkit.utils;

public interface ThrowableRunnable extends Runnable {

@Override
default void run() {
try {
run0();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}

void run0() throws Exception;
}
Loading